Artifact [23ac2d9df8]

Not logged in

Artifact 23ac2d9df81e2ac3732929315d96a441be8e6096f7bc6e6ca72113d73e660a37:


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. The ask is for the missing runs only: a tip
// whose quarry run passed, on a target that also requires race, asks
// once, and the engine side starts just the race pipeline.
func TestOpeningALandingAsksForTheRunItsTipLacks(t *testing.T) {
	const tip = "c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1"
	for _, c := range []struct {
		name     string
		required []string
		runs     []CIRun
		premerge bool
		want     string // the check-in asked for; "" for no ask
	}{
		{"no run at all", []string{"quarry"}, nil, false, tip},
		{"a cancelled run is not a verdict", []string{"quarry"}, []CIRun{{Workflow: "quarry", Pipeline: "ci/quarry.json", Status: "cancelled"}}, false, tip},
		{"a pass is left alone", []string{"quarry"}, []CIRun{{Workflow: "quarry", Pipeline: "ci/quarry.json", Status: "completed"}}, false, ""},
		{"a run in flight is left alone", []string{"quarry"}, []CIRun{{Workflow: "quarry", Pipeline: "ci/quarry.json", Status: "running"}}, false, ""},
		{"a premerge subject is somebody else's to start", []string{"quarry"}, nil, true, ""},
		{"a pass with race still owed asks", []string{"quarry", "race"}, []CIRun{{Workflow: "quarry", Pipeline: "ci/quarry.json", Status: "completed"}}, false, tip},
		{"a race in flight is left alone", []string{"quarry", "race"}, []CIRun{{Workflow: "quarry", Pipeline: "ci/quarry.json", Status: "completed"}, {Workflow: "race", Pipeline: "ci/race.json", Status: "running"}}, false, ""},
	} {
		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: c.required, premerge: c.premerge},
				CI:     runsStub{runs: c.runs},
				CIRunMissing: func(_, branch, checkin string) error {
					asked = append(asked, branch+" "+checkin)
					return nil
				},
				CITrigger: func(_, _ string) error { t.Error("FAIL: the open re-ran every pipeline"); 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] != "feat/x "+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")
}