Skip to content
go-libfossil Artifact [d60512a3e7]
Login

Artifact [d60512a3e7]

Artifact d60512a3e7a4b7542ac95dd08f119f52d50570ee6c0aec4f30e8fe46afecfc5e:


package manifest

import (
	"testing"
	"time"

	"github.com/danmestas/go-libfossil/internal/blob"
	libfossil "github.com/danmestas/go-libfossil/internal/fsltype"
	"github.com/danmestas/go-libfossil/internal/leaf"
)

// TestLeafRebuildOnlyHoldsCheckins pins Section 7.1: a leaf is a check-in.
// fossil writes plink rows for forum posts and wiki edits that carry a P-card,
// so a candidate set drawn from plink children alone puts them in the table.
func TestLeafRebuildOnlyHoldsCheckins(t *testing.T) {
	r := setupTestRepo(t)
	base := time.Date(2026, 7, 27, 12, 0, 0, 0, time.UTC)
	c1 := storeCheckin(t, r, "c1", "trunk", base)
	storeCheckin(t, r, "c2", "", base.Add(time.Minute), c1)
	if _, err := Crosslink(r); err != nil {
		t.Fatalf("Crosslink: %v", err)
	}

	var parent int64
	if err := r.DB().QueryRow("SELECT rid FROM blob WHERE uuid=?", c1).Scan(&parent); err != nil {
		t.Fatal(err)
	}
	for i, typ := range []string{"f", "w"} {
		rid, _, err := blob.Store(r.DB(), []byte{byte('a' + i)})
		if err != nil {
			t.Fatal(err)
		}
		if _, err := r.DB().Exec("INSERT INTO event(type,mtime,objid,user,comment) "+
			"VALUES(?,2460000.5,?,'u','x')", typ, int64(rid)); err != nil {
			t.Fatal(err)
		}
		if _, err := r.DB().Exec("INSERT INTO plink(pid,cid,isprim,mtime) "+
			"VALUES(?,?,1,2460000.5)", parent, int64(rid)); err != nil {
			t.Fatal(err)
		}
	}
	if err := leaf.Rebuild(r.DB()); err != nil {
		t.Fatalf("leaf.Rebuild: %v", err)
	}
	var n int
	if err := r.DB().QueryRow("SELECT count(*) FROM leaf JOIN event ON objid=rid " +
		"WHERE type<>'ci'").Scan(&n); err != nil {
		t.Fatal(err)
	}
	if n != 0 {
		t.Fatalf("%d non-check-in rows in leaf", n)
	}
	assertLeaves(t, r, "after repair", []string{"c2"})
}

// TestLeafUntaggedChildUsesMainBranch pins Section 6.1: with main-branch set,
// a check-in with no branch tag is on that branch, so it joins its parent
// there and the parent is no leaf, by leaf.Rebuild and by leaf.Check.
func TestLeafUntaggedChildUsesMainBranch(t *testing.T) {
	r := setupTestRepo(t)
	if err := r.SetConfig("main-branch", "main"); err != nil {
		t.Fatal(err)
	}
	base := time.Date(2026, 7, 27, 12, 0, 0, 0, time.UTC)
	c1 := storeCheckin(t, r, "c1", "main", base)
	storeCheckin(t, r, "c2", "", base.Add(time.Minute), c1)
	if _, err := Crosslink(r); err != nil {
		t.Fatalf("Crosslink: %v", err)
	}
	// Propagation gave c2 the branch; drop it so c2 carries no active branch
	// row, as a check-in with a non-propagating parent tag does.
	if _, err := r.DB().Exec("DELETE FROM tagxref WHERE tagid=? AND rid=("+
		"SELECT objid FROM event WHERE comment='c2')", leaf.BranchTagID); err != nil {
		t.Fatal(err)
	}
	if err := leaf.Rebuild(r.DB()); err != nil {
		t.Fatal(err)
	}
	// c2 is never rechecked (it has no tag, as in fossil), so only c1's
	// verdict is at stake: it has a same-branch child and is no leaf.
	assertLeaves(t, r, "after repair", nil)

	var rid int64
	if err := r.DB().QueryRow("SELECT rid FROM blob WHERE uuid=?", c1).Scan(&rid); err != nil {
		t.Fatal(err)
	}
	if _, err := r.DB().Exec("INSERT OR IGNORE INTO leaf(rid) VALUES(?)", rid); err != nil {
		t.Fatal(err)
	}
	if err := leaf.Check(r.DB(), libfossil.FslID(rid)); err != nil {
		t.Fatal(err)
	}
	assertLeaves(t, r, "after leaf.Check", nil)
}

// TestInitialCheckinStartsMainBranch pins Section 6.1: the initial check-in
// starts the branch named by the main-branch setting, not the literal trunk.
func TestInitialCheckinStartsMainBranch(t *testing.T) {
	r := setupTestRepo(t)
	if err := r.SetConfig("main-branch", "main"); err != nil {
		t.Fatal(err)
	}
	rid, _, err := Checkin(r, CheckinOpts{
		Files: []File{{Name: "a.txt", Content: []byte("a")}}, Comment: "root",
		User: "test", Time: time.Date(2026, 7, 27, 12, 0, 0, 0, time.UTC),
	})
	if err != nil {
		t.Fatal(err)
	}
	var n int
	if err := r.DB().QueryRow(`SELECT count(*) FROM tagxref x JOIN tag t USING(tagid)
		WHERE x.rid=? AND x.tagtype=2 AND ((t.tagname='branch' AND x.value='main')
		OR t.tagname='sym-main')`, int64(rid)).Scan(&n); err != nil {
		t.Fatal(err)
	}
	if n != 2 {
		t.Fatalf("%d of branch=main, sym-main tags on the root check-in, want 2", n)
	}
}