Skip to content
quarry Artifact [d672f492a4]
Login

Artifact [d672f492a4]

Artifact d672f492a49136cbbae195fb47d7b7920da42324708ef1bb1e3c1a1d36aa5369:


package index

import "testing"

// tag records a tag on a check-in, the way an artifact fold would.
func tag(t *testing.T, ix *Index, checkin, name, sign, date string) {
	t.Helper()
	// src is the artifact carrying the T card; one per (checkin, name)
	// here, since these fixtures never re-tag the same pair.
	if _, err := ix.db.Exec(`INSERT OR REPLACE INTO checkin_tag (src, date, checkin, name, sign, value)
		VALUES (?, ?, ?, ?, ?, '')`, "t-"+checkin+"-"+name+"-"+sign, date, checkin, name, sign); err != nil {
		t.Fatal(err)
	}
}

// foldBranches runs the derivation the ingest fold runs at the end of
// each batch. These fixtures write checkin rows directly, so they have
// to trigger it themselves — production does it once per ingest.
func foldBranches(t *testing.T, ix *Index) {
	t.Helper()
	tx, err := ix.db.Begin()
	if err != nil {
		t.Fatal(err)
	}
	if err := refreshBranches(tx); err != nil {
		t.Fatal(err)
	}
	if err := tx.Commit(); err != nil {
		t.Fatal(err)
	}
}

func closedSet(t *testing.T, ix *Index) map[string]bool {
	t.Helper()
	foldBranches(t, ix)
	bs, err := ix.Branches()
	if err != nil {
		t.Fatal(err)
	}
	out := map[string]bool{}
	for _, b := range bs {
		out[b.Name] = b.Closed
	}
	return out
}

// Closing a branch is a question about its TIP, and fossil writes the
// answer three different ways. Reading only a `+` on the tip reported
// five of this repository's own closed branches as open, which put them
// all back in the timeline's branch selector.
func TestClosedBranchesCoverEveryTagShape(t *testing.T) {
	ix := openIndex(t)
	// A one-check-in close: the tag sits on the tip itself.
	ci(t, ix, "a1", "plain-plus", "2026-01-01T10:00:00", "work")
	tag(t, ix, "a1", "closed", "+", "2026-01-01T10:00:00")

	// A PROPAGATING close on the tip. This is the shape the old query
	// rejected outright, because it tested the sign against '+' alone.
	ci(t, ix, "b1", "star-on-tip", "2026-01-02T10:00:00", "work")
	tag(t, ix, "b1", "closed", "*", "2026-01-02T10:00:00")

	// A propagating close INHERITED: the tag is on the parent, and the
	// tip that follows carries no tag of its own.
	ci(t, ix, "c1", "star-inherited", "2026-01-03T10:00:00", "work")
	tag(t, ix, "c1", "closed", "*", "2026-01-03T10:00:00")
	ci(t, ix, "c2", "star-inherited", "2026-01-03T10:00:01", "after the close", "c1")

	// Re-landed: closed with a NON-propagating tag, then committed to
	// again. The newer tip is not closed, so neither is the branch —
	// this is the case a branch-wide "latest closed tag wins" rule gets
	// wrong, and it is why the tip's own tag is read separately.
	ci(t, ix, "d1", "relanded", "2026-01-04T10:00:00", "work")
	tag(t, ix, "d1", "closed", "+", "2026-01-04T10:00:00")
	ci(t, ix, "d2", "relanded", "2026-01-04T11:00:00", "picked back up", "d1")

	// Cancelled: a propagating close undone by a later '-'.
	ci(t, ix, "e1", "reopened", "2026-01-05T10:00:00", "work")
	tag(t, ix, "e1", "closed", "*", "2026-01-05T10:00:00")
	ci(t, ix, "e2", "reopened", "2026-01-05T11:00:00", "reopen", "e1")
	tag(t, ix, "e2", "closed", "-", "2026-01-05T11:00:00")

	// Never closed at all.
	ci(t, ix, "f1", "live", "2026-01-06T10:00:00", "work")

	got := closedSet(t, ix)
	for branch, want := range map[string]bool{
		"plain-plus":     true,
		"star-on-tip":    true,
		"star-inherited": true,
		"relanded":       false,
		"reopened":       false,
		"live":           false,
	} {
		if got[branch] != want {
			t.Errorf("%s: closed = %v, want %v", branch, got[branch], want)
		}
	}
}