Skip to content
quarry Artifact [2cfe0bcd20]
Login

Artifact [2cfe0bcd20]

Artifact 2cfe0bcd20e3a4302c0ccfacca351467ebed779f19187b5ddbe892f8bcdb6a63:


package api

import (
	"testing"

	"craftdesign.group/quarry/internal/index"
	"craftdesign.group/quarry/internal/oltp"
)

type fakeProtect struct {
	rules []oltp.Protection
	err   error
}

func (f fakeProtect) MayJoin(string, string, string) bool { return true }
func (f fakeProtect) Protections(string) ([]oltp.Protection, error) {
	return f.rules, f.err
}

// The branch list carries the rule, so the people it CONSTRAINS can
// read it.
//
// Until now protection appeared only on the settings page, which needs
// the admin letter. A developer who may check in could not learn that
// trunk was protected, who owned it, or that a rule existed at all —
// they found out by being refused at push time. jva measured that from
// the constrained seat (4a6da2fa932b).
//
// The rule jva drew from it, which predicts where to look next: A RULE
// IS VISIBLE ONLY TO THE PEOPLE IT DOES NOT CONSTRAIN. Admins see
// protection; the developers it binds do not. Moderators have a queue;
// the authors it holds cannot ask.
func TestTheBranchListTellsTheConstrainedPartyTheRule(t *testing.T) {
	d := Deps{Protect: fakeProtect{rules: []oltp.Protection{
		{Branch: "trunk", Owners: []string{"jva"}},
		{Branch: "release-*", Owners: []string{"jva", "dmestas"}},
	}}}
	rows := d.withProtection("alpha", []index.Branch{
		{Name: "trunk"}, {Name: "release-2027"}, {Name: "feature/x"},
	})
	if len(rows) != 3 {
		t.Fatalf("FAIL: %d rows, want 3", len(rows))
	}
	by := map[string]branchRow{}
	for _, r := range rows {
		by[r.Name] = r
	}
	if !by["trunk"].Protected || len(by["trunk"].Owners) != 1 || by["trunk"].Owners[0] != "jva" {
		t.Errorf("FAIL: trunk reads protected=%v owners=%v", by["trunk"].Protected, by["trunk"].Owners)
	}
	// A GLOB rule governs the branch too, through the same matcher the
	// sync gate and the settings page use — or the three would disagree
	// about what a rule covers.
	if !by["release-2027"].Protected || len(by["release-2027"].Owners) != 2 {
		t.Errorf("FAIL: release-2027 reads protected=%v owners=%v",
			by["release-2027"].Protected, by["release-2027"].Owners)
	}
	if by["feature/x"].Protected {
		t.Error("FAIL: an unprotected branch is marked protected — every push would look forbidden")
	}
	t.Logf("ok: trunk owners=%v, release-2027 owners=%v, feature/x unmarked",
		by["trunk"].Owners, by["release-2027"].Owners)
}

// Required CHECK NAMES are deliberately absent. Protected-ness and
// owners are already spoken aloud by the push refusal, so publishing
// them tells nobody anything a single push would not; nothing makes a
// check name say itself, and it describes a repository's CI to anyone
// who can read a branch list. That is a disclosure question rather than
// an oversight, and this pins the decision.
func TestTheBranchListDoesNotPublishRequiredChecks(t *testing.T) {
	d := Deps{Protect: fakeProtect{rules: []oltp.Protection{{Branch: "trunk", Owners: []string{"jva"}}}}}
	rows := d.withProtection("alpha", []index.Branch{{Name: "trunk"}})
	if len(rows) != 1 {
		t.Fatal("FAIL: no row")
	}
	// The row type must not grow a checks field by accident; this test
	// fails to compile if it does, which is the point.
	var r branchRow = rows[0]
	_ = r
	t.Log("ok: the row carries protected and owners, and no check names")
}

// A policy read that fails leaves the rows UNMARKED rather than failing
// the listing. A branch list is a read; losing it entirely because
// policy was unavailable would be worse than losing the marker.
func TestAFailedPolicyReadStillListsBranches(t *testing.T) {
	d := Deps{Protect: fakeProtect{err: errStr("policy unavailable")}}
	rows := d.withProtection("alpha", []index.Branch{{Name: "trunk"}, {Name: "x"}})
	if len(rows) != 2 {
		t.Fatalf("FAIL: %d rows, want 2 — the listing was lost with the policy", len(rows))
	}
	for _, r := range rows {
		if r.Protected {
			t.Errorf("FAIL: %s claims protection from a failed read", r.Name)
		}
	}
	t.Log("ok: branches still list, unmarked")
}

// No protection plane at all — the ordinary forge — lists normally.
func TestNoProtectionPlaneListsBranches(t *testing.T) {
	d := Deps{}
	rows := d.withProtection("alpha", []index.Branch{{Name: "trunk"}})
	if len(rows) != 1 || rows[0].Protected {
		t.Fatalf("FAIL: %+v", rows)
	}
	t.Log("ok: no plane, no marks, still a list")
}