Skip to content
quarry Artifact [ff03489d19]
Login

Artifact [ff03489d19]

Artifact ff03489d199f2909a66f605ed9b7ec6f8f668f62af9afbdb8d732408fe8321e0:


package index

import (
	"fmt"
	"path/filepath"
	"sort"
	"strings"
	"testing"

	"craftdesign.group/quarry/internal/artifact"
)

func edgeHash(n int) string { return fmt.Sprintf("%040x", n) }

func edgeCi(n, parent int, date string, tags ...artifact.Tag) Parsed {
	a := &artifact.Artifact{Type: artifact.Manifest, Date: date, User: "u", Comment: fmt.Sprint(n), Tags: tags}
	if parent > 0 {
		a.Parents = []string{edgeHash(parent)}
	}
	return Parsed{Hash: edgeHash(n), Art: a}
}

func edgeState(t *testing.T, ix *Index) string {
	t.Helper()
	var out []string
	rows, err := ix.db.Query(`SELECT hash, branch, COALESCE((SELECT group_concat(name) FROM (
		SELECT name FROM checkin_sym s WHERE s.checkin = c.hash ORDER BY name)), '') FROM checkin c`)
	if err != nil {
		t.Fatal(err)
	}
	defer rows.Close()
	for rows.Next() {
		var h, b, s string
		rows.Scan(&h, &b, &s)
		out = append(out, h[36:]+"="+b+"/"+s)
	}
	sort.Strings(out)
	return strings.Join(out, " ")
}

// #263: a round that only grows the graph at its edge computes the new
// check-ins from their parents, and that answer is the wholesale walk's;
// a round that cannot (a parent missing, a tag re-aiming an old
// check-in) walks the whole graph.
func TestBranchesGrownAtTheEdgeMatchTheWholeWalk(t *testing.T) {
	open := func() *Index {
		ix, err := Open(filepath.Join(t.TempDir(), "index.db"))
		if err != nil {
			t.Fatal(err)
		}
		t.Cleanup(func() { ix.Close() })
		return ix
	}
	tag := func(name, value string) artifact.Tag { return artifact.Tag{Name: name, Target: "*", Value: value} }
	base := []Parsed{
		edgeCi(1, 0, "2026-01-01T00:00:00", tag("*branch", "trunk"), tag("*sym-trunk", "")),
		edgeCi(2, 1, "2026-01-02T00:00:00"),
		edgeCi(3, 2, "2026-01-03T00:00:00", tag("*branch", "feature"), tag("*sym-feature", ""), tag("-sym-trunk", "")),
	}
	rounds := [][]Parsed{
		{edgeCi(4, 3, "2026-01-04T00:00:00")},                                      // inherits feature
		{edgeCi(5, 2, "2026-01-05T00:00:00"), edgeCi(6, 5, "2026-01-06T00:00:00")}, // a chain on trunk in one batch
		{edgeCi(7, 4, "2026-01-07T00:00:00", tag("*sym-release", ""))},             // a new name on the branch
		{edgeCi(9, 8, "2026-01-09T00:00:00")},                                      // its parent has not arrived: whole walk
		{edgeCi(8, 6, "2026-01-08T00:00:00")},                                      // the parent arrives under its child: whole walk
		{{Hash: edgeHash(10), Art: &artifact.Artifact{Type: artifact.Control, Date: "2026-01-10T00:00:00", User: "u",
			Tags: []artifact.Tag{{Name: "*branch", Target: edgeHash(6), Value: "moved"}}}}}, // re-aims an old check-in: whole walk
		{{Hash: edgeHash(11), Art: &artifact.Artifact{Type: artifact.Control, Date: "2026-01-11T00:00:00", User: "u",
			Tags: []artifact.Tag{{Name: "+closed", Target: edgeHash(4)}, {Name: "+ci-pass", Target: edgeHash(7)}}}}}, // tags that move no branch: the edge path
		{{Hash: edgeHash(12), Art: &artifact.Artifact{Type: artifact.Control, Date: "2026-01-12T00:00:00", User: "u",
			Tags: []artifact.Tag{{Name: "*sym-v1", Target: edgeHash(5), Value: ""}}}}}, // a name on an old check-in: whole walk
	}
	edge := open()
	if err := edge.FoldNow(base); err != nil {
		t.Fatal(err)
	}
	whole := open()
	all := append([]Parsed{}, base...)
	for i, r := range rounds {
		if err := edge.FoldNow(r); err != nil {
			t.Fatal(err)
		}
		all = append(all, r...)
		if _, err := whole.Fold(all); err != nil {
			t.Fatal(err)
		}
		if err := whole.RecomputeBranches(); err != nil {
			t.Fatal(err)
		}
		if got, want := edgeState(t, edge), edgeState(t, whole); got != want {
			t.Fatalf("FAIL: after round %d the edge path reads\n  %s\nthe whole walk\n  %s", i+1, got, want)
		}
	}
	if _, ok := edgeOnly(rounds[6]); !ok {
		t.Error("FAIL: a control artifact carrying only closed and CI tags walked the whole graph")
	}
	if _, ok := edgeOnly(rounds[7]); ok {
		t.Error("FAIL: a sym tag on an old check-in took the edge path")
	}
	if s := edgeState(t, edge); !strings.Contains(s, edgeHash(5)[36:]+"=trunk/trunk,v1") {
		t.Errorf("FAIL: the propagated name did not reach its check-in: %s", s)
	}
	if s := edgeState(t, edge); !strings.Contains(s, edgeHash(7)[36:]+"=feature/feature,release") {
		t.Errorf("FAIL: the new name did not join the inherited one: %s", s)
	}
}