Skip to content
quarry Artifact [e648dbe67f]
Login

Artifact [e648dbe67f]

Artifact e648dbe67f01c6838299a275f4d581b43129f1095b3c4e161a425789dced0841:


package index

import (
	"testing"
)

// foldMerge records a check-in whose second parent is a branch tip —
// the shape zit writes when it lands a branch.
func foldMerge(t *testing.T, ix *Index, hash, branch, date, comment string, parents ...string) {
	t.Helper()
	if _, err := ix.db.Exec(`INSERT OR REPLACE INTO checkin (hash, date, user, comment, branch)
		VALUES (?, ?, 'dmestas', ?, ?)`, hash, date, comment, branch); err != nil {
		t.Fatal(err)
	}
	for i, p := range parents {
		if _, err := ix.db.Exec(`INSERT OR REPLACE INTO checkin_parent (child, parent, ord)
			VALUES (?, ?, ?)`, hash, p, i); err != nil {
			t.Fatal(err)
		}
	}
}

// landed builds an accepted landing with a branch tip and its merge.
func landed(t *testing.T, ix *Index, root, branch, title string) {
	t.Helper()
	foldForumBody(t, ix, root, "", title, "2026-01-01T10:00:00",
		landingBody(branch, "trunk", "please"))
	foldForumBody(t, ix, root+"ff", root, "", "2026-01-03T10:00:00",
		landingState("accepted", "landed"))
	foldMerge(t, ix, "tip"+branch, branch, "2026-01-02T10:00:00", "work on "+branch)
	foldMerge(t, ix, "mrg"+branch, "trunk", "2026-01-02T12:00:00",
		"land "+branch, "prevtrunk", "tip"+branch)
}

// landing.settled_by holds the reply that DECIDED the state — a forum
// post hash, not a commit — which was read as meaning nothing connects
// a landing to the check-in it produced. The answer was already in the
// DAG: a merge check-in lists its primary parent first and the branches
// it merged after, so ord > 0 IS the tip that landed.
func TestLandingResolvesToItsMergeCheckin(t *testing.T) {
	ix := openIndex(t)
	landed(t, ix, "aaaa1111", "feat-x", "Ship it")

	got, err := ix.MergesForLandings([]string{"aaaa1111"})
	if err != nil {
		t.Fatal(err)
	}
	m, ok := got["aaaa1111"]
	if !ok {
		t.Fatal("an accepted landing resolved to no check-in")
	}
	if m.Checkin != "mrgfeat-x" {
		t.Errorf("resolved to %q, want the merge on trunk", m.Checkin)
	}
	if m.Comment != "land feat-x" {
		t.Errorf("comment %q", m.Comment)
	}
}

// The merge lands BEFORE the artifact recording the verdict is written
// — one landing in this forge merged at 12:46:55 and was recorded at
// 12:49:51 — so bounding the search on the verdict silently loses
// exactly the check-ins this exists to find. A proposal cannot be
// landed before it is made; that is the bound that holds.
func TestMergeIsFoundEvenWhenItPrecedesTheVerdict(t *testing.T) {
	ix := openIndex(t)
	foldForumBody(t, ix, "aaaa1111", "", "Ship it", "2026-01-01T10:00:00",
		landingBody("feat-x", "trunk", "please"))
	foldMerge(t, ix, "tipfeat-x", "feat-x", "2026-01-02T12:46:55", "work")
	foldMerge(t, ix, "mrgfeat-x", "trunk", "2026-01-02T12:46:55", "land it",
		"prevtrunk", "tipfeat-x")
	// The verdict is filed three minutes AFTER the merge.
	foldForumBody(t, ix, "cccc3333", "aaaa1111", "", "2026-01-02T12:49:51",
		landingState("accepted", "landed"))

	got, err := ix.MergesForLandings([]string{"aaaa1111"})
	if err != nil {
		t.Fatal(err)
	}
	if _, ok := got["aaaa1111"]; !ok {
		t.Fatal("the merge was lost because it preceded its own verdict record")
	}
}

// Open and rejected landings produced nothing, and must not borrow a
// merge that happens to touch the same branch.
func TestUnsettledLandingsResolveToNothing(t *testing.T) {
	ix := openIndex(t)
	foldForumBody(t, ix, "aaaa1111", "", "Still open", "2026-01-01T10:00:00",
		landingBody("feat-x", "trunk", "please"))
	foldMerge(t, ix, "tipfeat-x", "feat-x", "2026-01-02T10:00:00", "work")
	foldMerge(t, ix, "mrgfeat-x", "trunk", "2026-01-02T12:00:00", "merged by hand",
		"prevtrunk", "tipfeat-x")

	got, err := ix.MergesForLandings([]string{"aaaa1111"})
	if err != nil {
		t.Fatal(err)
	}
	if m, ok := got["aaaa1111"]; ok {
		t.Errorf("an OPEN landing claimed check-in %q", m.Checkin)
	}

	foldForumBody(t, ix, "bbbb2222", "aaaa1111", "", "2026-01-03T10:00:00",
		landingState("rejected", "no"))
	if got, _ := ix.MergesForLandings([]string{"aaaa1111"}); len(got) != 0 {
		t.Error("a REJECTED landing claimed a check-in")
	}
}

// A merge somebody made by hand carries no landing, and the timeline
// decoration's absence is what distinguishes the two.
func TestTimelineDecoratesOnlyLandingMerges(t *testing.T) {
	ix := openIndex(t)
	landed(t, ix, "aaaa1111", "feat-x", "Ship it")
	// An unrelated merge of a branch no landing proposed.
	foldMerge(t, ix, "tiphotfix", "hotfix", "2026-01-04T10:00:00", "urgent")
	foldMerge(t, ix, "mrghotfix", "trunk", "2026-01-04T12:00:00", "merge hotfix",
		"prevtrunk", "tiphotfix")

	rows := []TimelineEntry{
		{Kind: "ci", Hash: "mrgfeat-x", IsMerge: true},
		{Kind: "ci", Hash: "mrghotfix", IsMerge: true},
		{Kind: "ci", Hash: "tipfeat-x"},
	}
	if err := ix.attachLandings(rows); err != nil {
		t.Fatal(err)
	}
	if rows[0].Landing != "aaaa1111" || rows[0].LandingTitle != "Ship it" {
		t.Errorf("landing merge not decorated: %+v", rows[0])
	}
	if rows[0].LandingApprover != "dan" {
		t.Errorf("approver %q, want the author of the verdict", rows[0].LandingApprover)
	}
	if rows[1].Landing != "" {
		t.Errorf("a hand-made merge was decorated with %q", rows[1].Landing)
	}
	if rows[2].Landing != "" {
		t.Error("a non-merge check-in was decorated")
	}
}