package api
import (
"path/filepath"
"testing"
"craftdesign.group/quarry/internal/artifact"
"craftdesign.group/quarry/internal/index"
)
type checksStub struct {
required []string
premerge bool
}
func (c checksStub) RequiredChecks(string, string) []string { return c.required }
func (c checksStub) PremergeMode(string, string) bool { return c.premerge }
type runsStub struct{ runs []CIRun }
func (r runsStub) CIRuns(string, int) ([]CIRun, error) { return r.runs, nil }
func (r runsStub) CIRunsPage(string, int, int) ([]CIRun, error) { return r.runs, nil }
func (r runsStub) CIRunsFor(string, string) ([]CIRun, error) { return r.runs, nil }
func (r runsStub) CIRunsForPair(string, string) ([]CIRun, error) { return r.runs, nil }
// openLandingIndex holds one check-in on branch, and one open landing
// from it, so the branch has a tip a landing can be judged at.
func openLandingIndex(t *testing.T, branch, tip string) *index.Index {
t.Helper()
ix, err := index.Open(filepath.Join(t.TempDir(), "index.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { ix.Close() })
if err := ix.FoldNow([]index.Parsed{
{Hash: "t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0t0", Art: &artifact.Artifact{Type: artifact.Manifest, Date: "2026-01-01T08:00:00", User: "ada", Comment: "trunk",
Tags: []artifact.Tag{{Name: "*branch", Target: "*", Value: "trunk"}}}},
{Hash: tip, Art: &artifact.Artifact{Type: artifact.Manifest, Date: "2026-01-01T09:00:00", User: "ada", Comment: "work",
Tags: []artifact.Tag{{Name: "*branch", Target: "*", Value: branch}}}},
{Hash: landingHash, Art: &artifact.Artifact{Type: artifact.Forum, ThreadTitle: "land it", Date: "2026-01-01T10:00:00", User: "ada",
Body: []byte("zit-landing: 1\nbranch: " + branch + "\ntarget: trunk\n\nready\n")}},
}); err != nil {
t.Fatal(err)
}
return ix
}
// OPENING A LANDING ASKS FOR THE RUN ITS TIP LACKS, AND ONLY THAT.
//
// A required check with no verdict at the branch tip has no way forward
// on its own: the reader sees "Waiting for 1 required check" and has to
// know to press Re-run (49323c33). RunForOpenedLanding is the one
// producer of the ask, shared by landing.open and by the fold loop for
// landings that arrive by push. It asks for the BRANCH tip, named; it
// asks nothing when a run already answered or is on its way, nothing
// for a premerge subject (a pair, quarry-premerge's job), and nothing
// when no engine is wired.
func TestOpeningALandingAsksForTheRunItsTipLacks(t *testing.T) {
const tip = "c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1"
for _, c := range []struct {
name string
runs []CIRun
premerge bool
want string // the check-in asked for; "" for no ask
}{
{"no run at all", nil, false, tip},
{"a cancelled run is not a verdict", []CIRun{{Workflow: "quarry", Pipeline: "ci/quarry.json", Status: "cancelled"}}, false, tip},
{"a pass is left alone", []CIRun{{Workflow: "quarry", Pipeline: "ci/quarry.json", Status: "completed"}}, false, ""},
{"a run in flight is left alone", []CIRun{{Workflow: "quarry", Pipeline: "ci/quarry.json", Status: "running"}}, false, ""},
{"a premerge subject is somebody else's to start", nil, true, ""},
} {
t.Run(c.name, func(t *testing.T) {
ix := openLandingIndex(t, "feat/x", tip)
var asked []string
d := Deps{Repos: NewRepos(map[string]*Repo{"test": {Index: ix}}),
Checks: checksStub{required: []string{"quarry"}, premerge: c.premerge},
CI: runsStub{runs: c.runs},
CITrigger: func(_, checkin string) error { asked = append(asked, checkin); return nil }}
l, ok, err := ix.LandingByPrefix(landingHash[:12])
if err != nil || !ok {
t.Fatalf("landing not folded: %v %v", ok, err)
}
got := d.RunForOpenedLanding("test", l)
if c.want == "" && (got || len(asked) != 0) {
t.Errorf("FAIL: asked for a run (%v) when none was owed", asked)
}
if c.want != "" && (!got || len(asked) != 1 || asked[0] != c.want) {
t.Errorf("FAIL: asked %v, want one run at the branch tip %.12s", asked, c.want)
}
})
}
// No engine wired: nothing to ask, nothing to panic.
ix := openLandingIndex(t, "feat/x", tip)
l, _, _ := ix.LandingByPrefix(landingHash[:12])
if (Deps{Repos: NewRepos(map[string]*Repo{"test": {Index: ix}})}).RunForOpenedLanding("test", l) {
t.Error("FAIL: a forge with no CI engine claimed to ask for a run")
}
t.Log("ok: the open asks for exactly the run the tip lacks")
}