Skip to content
quarry Artifact [91949e4113]
Login

Artifact [91949e4113]

Artifact 91949e4113939fa0a1a25d9039cd46eb5e8015a7eba18bb006eed6476c83b195:


package api

import (
	"context"
	"encoding/json"
	"slices"
	"testing"

	"craftdesign.group/quarry/internal/artifact"
	"craftdesign.group/quarry/internal/index"
	"craftdesign.group/quarry/internal/ops"
)

// AN INHERITED VERDICT IS THE CHECK-IN'S OWN (RFC 0009 §inheritance).
//
// checkin.get lists it among the check-in's checks with its state and
// the pair run it came from, and the required-checks rule a deploy
// gates on reads it as it reads a run: an inherited pass is met, an
// inherited fail is unmet.
func TestAnInheritedVerdictIsTheCheckinsOwn(t *testing.T) {
	ix := redTrunkIndex(t)
	runs := runsStub{runs: []CIRun{
		{RunID: "p-quarry", InheritedFrom: "p-quarry", Checkin: redTrunkTip, Workflow: "quarry.quarry.1", Pipeline: "ci/quarry.json", Status: "completed"},
		{RunID: "p-race", InheritedFrom: "p-race", Checkin: redTrunkTip, Workflow: "quarry.race.1", Pipeline: "ci/race.json", Status: "failed"},
	}}
	r := ops.NewRegistry()
	d := Deps{Version: "v", Repos: NewRepos(map[string]*Repo{"test": {Index: ix}}), CI: runs, Checks: checksStub{required: []string{"quarry", "race"}}}
	if err := RegisterAll(r, d); err != nil {
		t.Fatal(err)
	}
	got, err := r.Invoke(context.Background(), "checkin.get", checkinGetIn{Repo: "test", Hash: redTrunkTip})
	if err != nil {
		t.Fatal(err)
	}
	b, _ := json.Marshal(got)
	var m struct {
		Checks []map[string]any `json:"checks"`
		Unmet  []string         `json:"unmet"`
	}
	if err := json.Unmarshal(b, &m); err != nil {
		t.Fatal(err)
	}
	from := map[string]any{}
	for _, c := range m.Checks {
		from[c["pipeline"].(string)] = c["inheritedFrom"]
	}
	if from["ci/quarry.json"] != "p-quarry" || from["ci/race.json"] != "p-race" {
		t.Fatalf("FAIL: checks name their sources %v; want p-quarry and p-race", from)
	}
	if len(m.Unmet) != 1 || m.Unmet[0] != "race" {
		t.Fatalf("FAIL: unmet = %v; want the inherited fail, race, and only it", m.Unmet)
	}
	t.Log("ok: inherited verdicts are listed with their source and judged as the check-in's own")
}

// pushedStub files runs under a check-in or under a pair, apart, and
// names the newest pair tested for a tip.
type pushedStub struct {
	runsStub
	tip, pair  map[string][]CIRun
	latestPair map[string]string
}

func (p pushedStub) CIRunsFor(_, c string) ([]CIRun, error)      { return p.tip[c], nil }
func (p pushedStub) CIRunsForPair(_, s string) ([]CIRun, error)  { return p.pair[s], nil }
func (p pushedStub) LatestPairFor(_, tip string) (string, error) { return p.latestPair[tip], nil }

// A PROPOSED BRANCH CHECK-IN'S CHECKS ARE ITS MERGE'S (RFC 0009 §once).
//
// A push to a branch with an open landing is tested as its merge with
// the landing's target, so the check-in's own runs are only what stayed
// on its tip; checkin.get lists that merge's runs beside them, and
// judges the branch's required checks on either. A merge that is not
// its landing's (the branch was never proposed, or it merged into
// another branch) is not the check-in's: its tip must answer for itself.
func TestAProposedCheckinShowsItsMergesChecks(t *testing.T) {
	const tip = "f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1"
	const trunk = "t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0"
	get := func(ix *index.Index, hash, pair string) checkinGetOut {
		t.Helper()
		stub := pushedStub{
			tip: map[string][]CIRun{hash: {{RunID: "d", Checkin: hash, Workflow: "w.deploy", Pipeline: "ci/deploy.yml", Status: "completed"}}},
			pair: map[string][]CIRun{pair: {
				{RunID: "q", Checkin: pair, Pair: pair, Workflow: "w.quarry", Pipeline: "ci/quarry.json", Status: "completed"},
				{RunID: "r", Checkin: pair, Pair: pair, Workflow: "w.race", Pipeline: "ci/race.json", Status: "failed"},
			}},
			latestPair: map[string]string{hash: pair},
		}
		r := ops.NewRegistry()
		d := Deps{Version: "v", Repos: NewRepos(map[string]*Repo{"test": {Index: ix}}), CI: stub,
			Checks: checksStub{required: []string{"deploy", "quarry", "race"}}}
		if err := RegisterAll(r, d); err != nil {
			t.Fatal(err)
		}
		got, err := r.Invoke(context.Background(), "checkin.get", checkinGetIn{Repo: "test", Hash: hash})
		if err != nil {
			t.Fatal(err)
		}
		return got.(checkinGetOut)
	}
	out := get(openLandingIndex(t, "feat", tip), tip, "pair:"+tip+":"+trunk)
	if len(out.Checks) != 3 {
		t.Fatalf("FAIL: checks %+v; want the tip's deploy and the merge's two runs", out.Checks)
	}
	if len(out.Unmet) != 1 || out.Unmet[0] != "race" {
		t.Fatalf("FAIL: unmet = %v; want race, failed on the merge", out.Unmet)
	}
	t.Log("ok: the open landing's merge runs are the branch check-in's checks")

	out = get(openLandingIndex(t, "feat", tip), tip, "pair:"+tip+":"+redTrunkTip)
	if len(out.Checks) != 1 || len(out.Unmet) != 2 {
		t.Fatalf("FAIL: a merge into another tip read as checks %+v, unmet %v; want the tip's own only", out.Checks, out.Unmet)
	}
	out = get(redTrunkIndex(t), redFeatureTip, "pair:"+redFeatureTip+":"+redTrunkTip)
	if len(out.Checks) != 1 || len(out.Unmet) != 2 {
		t.Fatalf("FAIL: an unproposed tip read a merge's runs: checks %+v, unmet %v; want its own only", out.Checks, out.Unmet)
	}
	t.Log("ok: a merge that is not the open landing's is not the check-in's")
}

// A CLOSED LANDING'S MERGE IS STILL ITS TIP'S CHECKS.
//
// A push made while the landing was open was tested as its merge with
// the landing's target, and that verdict is the tip's (its tag, and the
// reason a closing landing does not test the tip again). Rejected, the
// landing no longer holds, but the merge was still the one the branch
// was proposed into: checkin.get lists its runs and judges the required
// checks on them, so checks, Required and the badge agree.
func TestAClosedLandingsMergeIsStillItsTipsChecks(t *testing.T) {
	const tip = "f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1"
	const trunk = "t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0"
	pair := "pair:" + tip + ":" + trunk
	ix := openLandingIndex(t, "feat", tip)
	if err := ix.FoldNow([]index.Parsed{{Hash: "bbbb2222bbbb2222bbbb2222bbbb2222",
		Art: &artifact.Artifact{Type: artifact.Forum, ThreadRoot: landingHash, Date: "2026-01-01T11:00:00", User: "carol",
			Body: []byte("zit-landing-state: rejected\n\nnot now\n")}}}); err != nil {
		t.Fatal(err)
	}
	if _, open, err := ix.OpenLandingOn("feat"); err != nil || open {
		t.Fatalf("FAIL: the landing is still open (%v)", err)
	}
	stub := pushedStub{
		pair: map[string][]CIRun{pair: {
			{RunID: "q", Checkin: pair, Pair: pair, Workflow: "w.quarry", Pipeline: "ci/quarry.json", Status: "completed"},
			{RunID: "r", Checkin: pair, Pair: pair, Workflow: "w.race", Pipeline: "ci/race.json", Status: "failed"},
		}},
		latestPair: map[string]string{tip: pair},
	}
	r := ops.NewRegistry()
	if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(map[string]*Repo{"test": {Index: ix}}), CI: stub,
		Checks: checksStub{required: []string{"quarry", "race"}}}); err != nil {
		t.Fatal(err)
	}
	got, err := r.Invoke(context.Background(), "checkin.get", checkinGetIn{Repo: "test", Hash: tip})
	if err != nil {
		t.Fatal(err)
	}
	out := got.(checkinGetOut)
	if len(out.Checks) != 2 || out.CIRun != "q" {
		t.Fatalf("FAIL: checks %+v, ciRun %q; want the merge's two runs, q behind the badge", out.Checks, out.CIRun)
	}
	if !slices.Equal(out.Unmet, []string{"race"}) {
		t.Fatalf("FAIL: unmet = %v; want race, failed on the merge, and quarry met by it", out.Unmet)
	}
	t.Log("ok: a closed landing's merge stays its tip's checks")
}

// A PROPOSED CHECK-IN'S RUN IS ITS MERGE'S when nothing ran on the tip
// itself: ciRun links the badge to the run behind it, as before.
func TestAProposedCheckinsRunIsItsMerges(t *testing.T) {
	const tip = "f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1"
	pair := "pair:" + tip + ":t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0"
	stub := pushedStub{
		pair:       map[string][]CIRun{pair: {{RunID: "q", Checkin: pair, Pair: pair, Workflow: "w.quarry", Pipeline: "ci/quarry.json", Status: "completed"}}},
		latestPair: map[string]string{tip: pair},
	}
	r := ops.NewRegistry()
	if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(map[string]*Repo{"test": {Index: openLandingIndex(t, "feat", tip)}}), CI: stub}); err != nil {
		t.Fatal(err)
	}
	got, err := r.Invoke(context.Background(), "checkin.get", checkinGetIn{Repo: "test", Hash: tip})
	if err != nil {
		t.Fatal(err)
	}
	if run := got.(checkinGetOut).CIRun; run != "q" {
		t.Fatalf("FAIL: ciRun = %q; want the merge's run q", run)
	}
	t.Log("ok: the merge's run is the check-in's")
}