package index
import "testing"
// defaultIs checks the answer after the fixture's direct writes, which
// bypass the fold and so drop the memo themselves, as a fold does.
func defaultIs(t *testing.T, ix *Index, want string) {
t.Helper()
ix.invalidateCounts()
if got := ix.DefaultBranch(); got != want {
t.Fatalf("FAIL: DefaultBranch = %q, want %q", got, want)
}
}
func TestDefaultBranchOfAnEmptyRepositoryIsTrunk(t *testing.T) {
defaultIs(t, openIndex(t), "trunk")
}
// The case the old rule got wrong: a separate main branch with newer
// work is not the default while trunk exists.
func TestDefaultBranchKeepsTrunkWhenMainIsNewer(t *testing.T) {
ix := openIndex(t)
ci(t, ix, "a1", "trunk", "2026-01-01T00:00:00", "root")
ci(t, ix, "b1", "main", "2026-08-01T00:00:00", "side line", "a1")
foldSymbols(t, ix)
defaultIs(t, ix, "trunk")
ci(t, ix, "b2", "main", "2026-08-02T00:00:00", "more", "b1")
foldSymbols(t, ix)
defaultIs(t, ix, "trunk")
}
// tcl, tk and tcllib renamed trunk to main by adding a propagating
// sym-main without cancelling sym-trunk: trunk is another name for main
// there, and main is what the repository calls its line.
func TestDefaultBranchFollowsARenamedTrunk(t *testing.T) {
ix := openIndex(t)
ci(t, ix, "a1", "trunk", "2011-03-05T00:00:00", "before the rename")
ci(t, ix, "b1", "main", "2026-08-01T00:00:00", "renamed", "a1")
tag(t, ix, "b1", "sym-trunk", "*", "2026-08-01T00:00:00")
foldSymbols(t, ix)
defaultIs(t, ix, "main")
}
// A feature branch that forgot to cancel sym-trunk makes trunk resolve
// into it; that never makes the feature branch the default.
func TestDefaultBranchIgnoresATrunkAliasOntoAFeatureBranch(t *testing.T) {
ix := openIndex(t)
ci(t, ix, "a1", "trunk", "2026-01-01T00:00:00", "root")
ci(t, ix, "f1", "wild-idea", "2026-08-01T00:00:00", "fork", "a1")
tag(t, ix, "f1", "sym-trunk", "*", "2026-08-01T00:00:00")
foldSymbols(t, ix)
defaultIs(t, ix, "trunk")
}
func TestDefaultBranchWithoutTrunkPrefersMainThenMaster(t *testing.T) {
ix := openIndex(t)
ci(t, ix, "m1", "master", "2026-09-01T00:00:00", "git import")
foldSymbols(t, ix)
defaultIs(t, ix, "master")
ci(t, ix, "n1", "main", "2020-01-01T00:00:00", "older, still preferred")
foldSymbols(t, ix)
defaultIs(t, ix, "main")
}
// No conventional name at all: the branch the history starts on, not
// whichever branch was pushed to last.
func TestDefaultBranchFallsBackToTheOldestRootBranch(t *testing.T) {
ix := openIndex(t)
ci(t, ix, "r1", "develop", "2020-01-01T00:00:00", "first")
ci(t, ix, "f1", "feature", "2026-08-01T00:00:00", "newer", "r1")
ci(t, ix, "r2", "orphan", "2024-01-01T00:00:00", "a later root")
foldSymbols(t, ix)
defaultIs(t, ix, "develop")
}
// The answer is memoised until the next fold: it sits on the CI path of
// every new check-in.
func TestDefaultBranchIsMemoisedUntilAFold(t *testing.T) {
ix := openIndex(t)
ci(t, ix, "m1", "main", "2026-01-01T00:00:00", "root")
foldSymbols(t, ix)
defaultIs(t, ix, "main")
ci(t, ix, "t1", "trunk", "2026-01-02T00:00:00", "unfolded")
if got := ix.DefaultBranch(); got != "main" {
t.Fatalf("FAIL: answer moved without a fold: %q", got)
}
defaultIs(t, ix, "trunk")
}