Skip to content
quarry Artifact [e673adda03]
Login

Artifact [e673adda03]

Artifact e673adda03d51f16b99ac2095ff6e8fe2b38b82ebae495dd99fbcaac48e6f90e:


package api

import (
	"path/filepath"
	"testing"

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

// checksByTarget answers required checks per target branch, as the real
// rules do: trunk's rule names checks, a feature branch has none.
type checksByTarget map[string][]string

func (c checksByTarget) RequiredChecks(_, target string) []string { return c[target] }

// An open landing's badge is its required checks' on the merge, not its
// branch's own run: green only when every one has passed, red when one
// failed, waiting while one has not answered.
func TestALandingIsGreenOnlyWhenItsRequiredChecksAre(t *testing.T) {
	const tip = "f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1"
	const trunk = "t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0"
	pair := "pair:" + tip + ":" + trunk
	run := func(id, pipeline, status string) CIRun {
		return CIRun{RunID: id, Checkin: pair, Pair: pair, Workflow: "w." + id, Pipeline: pipeline, Status: status}
	}
	for _, c := range []struct {
		name     string
		runs     []CIRun
		required []string
		tip      string
		want     string
	}{
		{"both pass", []CIRun{run("q", "ci/quarry.json", "completed"), run("r", "ci/race.json", "completed")}, []string{"quarry", "race"}, "ci-pass", "ci-pass"},
		{"one running", []CIRun{run("q", "ci/quarry.json", "running"), run("r", "ci/race.json", "completed")}, []string{"quarry", "race"}, "ci-pass", "ci-waiting"},
		{"none yet", nil, []string{"quarry", "race"}, "ci-pass", "ci-waiting"},
		{"one failed", []CIRun{run("q", "ci/quarry.json", "failed"), run("r", "ci/race.json", "running")}, []string{"quarry", "race"}, "ci-pass", "ci-fail"},
		{"the branch failed", []CIRun{run("q", "ci/quarry.json", "completed")}, []string{"quarry"}, "ci-fail", "ci-fail"},
		{"nothing required", nil, nil, "ci-pass", "ci-pass"},
	} {
		ix := openLandingIndex(t, "feat", tip)
		l, open, err := ix.OpenLandingOn("feat")
		if err != nil || !open {
			t.Fatalf("no open landing: %v", err)
		}
		d := Deps{Repos: NewRepos(map[string]*Repo{"test": {Index: ix}}),
			CI:     pushedStub{pair: map[string][]CIRun{pair: c.runs}, latestPair: map[string]string{tip: pair}},
			Checks: checksStub{required: c.required}}
		if got := d.gatedVerdict("test", l, c.tip); got != c.want {
			t.Errorf("FAIL: %s: the badge is %q, want %q", c.name, got, c.want)
		}
	}
}

// A RUNG OF A STACK IS JUDGED ON THE CHECKS OF THE BRANCH THE STACK
// LANDS ON, NOT ON ITS OWN TARGET'S.
//
// !1293 targeted !1292's branch, which no rule names, so its badge was
// its branch's own run: "pass", in the list and on the page, while the
// stack it was armed with waited on its race check.
func TestARungOfAStackWaitsOnTheBaseBranchsChecks(t *testing.T) {
	const (
		trunk   = "t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0"
		baseTip = "b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1"
		topTip  = "c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2"
		landA   = "a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"
		landB   = "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1"
	)
	ix, err := index.Open(filepath.Join(t.TempDir(), "index.db"))
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { ix.Close() })
	manifest := func(hash, date, branch string) index.Parsed {
		return index.Parsed{Hash: hash, Art: &artifact.Artifact{Type: artifact.Manifest, Date: date, User: "ada", Comment: "work",
			Tags: []artifact.Tag{{Name: "*branch", Target: "*", Value: branch}}}}
	}
	landing := func(hash, date, branch, target string) index.Parsed {
		return index.Parsed{Hash: hash, Art: &artifact.Artifact{Type: artifact.Forum, ThreadTitle: "land " + branch, Date: date, User: "ada",
			Body: []byte("zit-landing: 1\nbranch: " + branch + "\ntarget: " + target + "\n\nready\n")}}
	}
	if err := ix.FoldNow([]index.Parsed{
		manifest(trunk, "2026-01-01T08:00:00", "trunk"),
		manifest(baseTip, "2026-01-01T09:00:00", "base"),
		manifest(topTip, "2026-01-01T09:30:00", "top"),
		landing(landA, "2026-01-01T10:00:00", "base", "trunk"),
		landing(landB, "2026-01-01T10:30:00", "top", "base"),
	}); err != nil {
		t.Fatal(err)
	}
	top, open, err := ix.OpenLandingOn("top")
	if err != nil || !open {
		t.Fatalf("no open landing on top: %v", err)
	}
	pair := "pair:" + topTip + ":" + baseTip
	run := func(id, pipeline, status string) CIRun {
		return CIRun{RunID: id, Checkin: pair, Pair: pair, Workflow: "w." + id, Pipeline: pipeline, Status: status}
	}
	for _, c := range []struct {
		name string
		runs []CIRun
		want string
	}{
		{"race still running", []CIRun{run("q", "ci/quarry.json", "completed"), run("r", "ci/race.json", "running")}, "ci-waiting"},
		{"both pass", []CIRun{run("q", "ci/quarry.json", "completed"), run("r", "ci/race.json", "completed")}, "ci-pass"},
		{"race failed", []CIRun{run("q", "ci/quarry.json", "completed"), run("r", "ci/race.json", "failed")}, "ci-fail"},
	} {
		d := Deps{Repos: NewRepos(map[string]*Repo{"test": {Index: ix}}),
			CI:     pushedStub{pair: map[string][]CIRun{pair: c.runs}, latestPair: map[string]string{topTip: pair}},
			Checks: checksByTarget{"trunk": {"quarry", "race"}}}
		if got := d.gatedVerdict("test", top, "ci-pass"); got != c.want {
			t.Errorf("FAIL: %s: the rung's badge is %q, want %q", c.name, got, c.want)
		}
	}
	// A landing on a branch nothing proposes, with no rule, keeps its tip's verdict.
	d := Deps{Repos: NewRepos(map[string]*Repo{"test": {Index: ix}}), Checks: checksByTarget{}}
	if got := d.gatedVerdict("test", top, "ci-pass"); got != "ci-pass" {
		t.Errorf("FAIL: with no rule anywhere the badge is %q, want the tip's", got)
	}
	t.Log("ok")
}