Skip to content
quarry Artifact [15a5ebc754]
Login

Artifact [15a5ebc754]

Artifact 15a5ebc754e99abd74f55a47c19446d2fc7bc282ef86c578103f93e88ddbb20f:


package index

import (
	"strings"
	"testing"

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

// A TAG ADDED TO A CHECK-IN IS ONE ROW, ON THE CHECK-IN THAT CARRIES IT
// (RFC 0020 §releases, the `tag` event).
//
// What counts is what a person means by tagging: `fossil tag add NAME
// CHECKIN` writes `+sym-NAME`, `fossil commit --tag NAME` writes the
// same card inline, and `*sym-NAME` without a branch is a propagating
// tag on its carrier only. A branch's own `sym-` names, raw tags
// (closed, hidden, comment, ci-pass), cancellations, a tag since
// cancelled, and a tag on something that is not a check-in do not.
func TestTagsAfterListsTagsAddedToCheckins(t *testing.T) {
	ix := openIndex(t)
	a, b, c := strings.Repeat("a1", 32), strings.Repeat("b2", 32), strings.Repeat("c3", 32)
	x, y, z := strings.Repeat("d4", 32), strings.Repeat("e5", 32), strings.Repeat("f6", 32)
	manifest := func(hash, date string, parents []string, tags ...artifact.Tag) Parsed {
		return Parsed{Hash: hash, Art: &artifact.Artifact{
			Type: artifact.Manifest, Date: date, User: "dan",
			Comment: "c", Parents: parents, Tags: tags}}
	}
	control := func(hash, date string, tags ...artifact.Tag) Parsed {
		return Parsed{Hash: hash, Art: &artifact.Artifact{
			Type: artifact.Control, Date: date, User: "dan", Tags: tags}}
	}
	if err := ix.FoldNow([]Parsed{
		manifest(a, "2026-09-01T10:00:00", nil,
			artifact.Tag{Name: "*branch", Target: "*", Value: "trunk"},
			artifact.Tag{Name: "*sym-trunk", Target: "*"}),
		manifest(b, "2026-09-02T10:00:00", []string{a},
			artifact.Tag{Name: "*branch", Target: "*", Value: "feature"},
			artifact.Tag{Name: "*sym-feature", Target: "*"},
			artifact.Tag{Name: "-sym-trunk", Target: "*"}),
		manifest(c, "2026-09-03T10:00:00", []string{a},
			artifact.Tag{Name: "+sym-version-0.1.0", Target: "*"}),
		control(x, "2026-09-04T10:00:00",
			artifact.Tag{Name: "+sym-version-0.2.0", Target: a},
			artifact.Tag{Name: "*sym-rel", Target: a},
			artifact.Tag{Name: "+sym-gone", Target: a},
			artifact.Tag{Name: "+closed", Target: b},
			artifact.Tag{Name: "+sym-elsewhere", Target: strings.Repeat("99", 32)}),
		control(y, "2026-09-05T10:00:00",
			artifact.Tag{Name: "-sym-gone", Target: a},
			artifact.Tag{Name: "+ci-pass", Target: c, Value: "ci/test.yml"}),
		control(z, "2026-09-06T10:00:00",
			artifact.Tag{Name: "+sym-feature", Target: c}),
	}); err != nil {
		t.Fatal(err)
	}

	got, err := ix.TagsAfter(TagKey{}, 100)
	if err != nil {
		t.Fatal(err)
	}
	type row struct{ checkin, name, branch, src string }
	want := []row{
		{c, "version-0.1.0", "trunk", c},
		{a, "rel", "trunk", x},
		{a, "version-0.2.0", "trunk", x},
	}
	if len(got) != len(want) {
		t.Fatalf("FAIL: TagsAfter = %+v; want %d rows", got, len(want))
	}
	for i, w := range want {
		g := got[i]
		if g.Checkin != w.checkin || g.Name != w.name || g.Branch != w.branch || g.Src != w.src {
			t.Errorf("FAIL: row %d = %.4s %s on %s from %.4s; want %.4s %s on %s from %.4s",
				i, g.Checkin, g.Name, g.Branch, g.Src, w.checkin, w.name, w.branch, w.src)
		}
	}

	// The key pages: after the first row comes the second, even when
	// two rows share one artifact.
	next, err := ix.TagsAfter(got[0].TagKey, 1)
	if err != nil || len(next) != 1 || next[0].Name != "rel" {
		t.Errorf("FAIL: the page after %s = %+v, %v; want rel", got[0].Name, next, err)
	}
	next, err = ix.TagsAfter(got[1].TagKey, 10)
	if err != nil || len(next) != 1 || next[0].Name != "version-0.2.0" {
		t.Errorf("FAIL: the page after rel = %+v, %v; want version-0.2.0 alone", next, err)
	}

	newest, err := ix.NewestTag()
	if err != nil || newest != got[2].TagKey {
		t.Errorf("FAIL: NewestTag = %+v, %v; want %+v", newest, err, got[2].TagKey)
	}
	empty := openIndex(t)
	if k, err := empty.NewestTag(); err != nil || k != (TagKey{}) {
		t.Errorf("FAIL: NewestTag on an empty index = %+v, %v; want the zero key", k, err)
	}
	t.Log("ok: added sym tags on check-ins only, once on the carrier, branch names and raw tags excluded")
}