Artifact 32b9ec25cd8e218ed8f49ef476fafe99cb7efa6e1cbfc6f548402d9751f4497c:
- File
internal/index/tag_test.go
— part of check-in
[ff7f5d3d69]
at
2026-08-23 22:03:24
on branch trunk
— check-in page: tag history, branch diff, manifest view
Fossil lets an author correct a check-in comment after the fact; quarry showed the superseded C card as if it were current, on 697 fossil check-ins. The correction is now applied at read, batched in AttachCI, with the original kept beside it — the stored card is an artifact fact and stays untouched. hidden and closed surface too.
Branch diff is the branch against its fork point, which is not the landing page's base (the target's current tip). Offered only where a fork point exists.
The manifest view renders the eight cards that are not the file list and counts the other 980, linking the tree.
Also: unified/side-by-side now swaps in place via datastar, keeping the window and correcting the URL; the wiki note panel lets blocks use the full width while prose keeps its measure. (user: dmestas, size: 6925) [annotate] [blame] [check-ins using]
package index import ( "strings" "testing" "craftdesign.group/quarry/internal/artifact" ) // Fossil tags are a LOG, not a set: a later artifact can cancel an // earlier one. TagsOf and the timeline's tag filter must agree on which // tags are standing, or a chip on the check-in page links to a timeline // that disagrees with the chip. func TestTagsOfAndTimelineFilterAgreeOnStandingTags(t *testing.T) { ix := openIndex(t) a, b := strings.Repeat("a1", 32), strings.Repeat("b2", 32) ci := func(hash, date string, tags ...artifact.Tag) Parsed { return Parsed{Hash: hash, Art: &artifact.Artifact{ Type: artifact.Manifest, Date: date, User: "dan", Comment: "c", Tags: tags}} } if err := ix.FoldNow([]Parsed{ ci(a, "2026-08-18T10:00:00", artifact.Tag{Name: "sym-release", Target: "*"}, artifact.Tag{Name: "sym-version-1.0", Target: "*"}), ci(b, "2026-08-19T10:00:00", artifact.Tag{Name: "sym-doomed", Target: "*"}), }); err != nil { t.Fatal(err) } got, err := ix.TagsOf(a) if err != nil { t.Fatal(err) } if len(got) != 2 || got[0] != "release" || got[1] != "version-1.0" { t.Fatalf("TagsOf = %v, want [release version-1.0] sorted", got) } // The filter finds exactly the check-in the chip sits on. tl, _, err := ix.Timeline(TimelineFilter{Limit: 10, Tag: "release"}) if err != nil { t.Fatal(err) } if len(tl) != 1 || tl[0].Hash != a { t.Fatalf("tag filter returned %d rows, want just %.4s", len(tl), a) } // A tag nobody carries is an empty timeline, not an error and not // the whole timeline — the failure mode that would matter most. if tl, _, err := ix.Timeline(TimelineFilter{Limit: 10, Tag: "nonesuch"}); err != nil || len(tl) != 0 { t.Fatalf("unknown tag returned %d rows (err %v)", len(tl), err) } // Cancel one. A later artifact carrying the minus sign is fossil's // way of retracting a tag, and both readers must stop reporting it. if err := ix.FoldNow([]Parsed{ {Hash: strings.Repeat("c3", 32), Art: &artifact.Artifact{ Type: artifact.Manifest, Date: "2026-08-20T10:00:00", User: "dan", Comment: "untag", Tags: []artifact.Tag{{Name: "-sym-doomed", Target: b}}}}, }); err != nil { t.Fatal(err) } got, err = ix.TagsOf(b) if err != nil { t.Fatal(err) } if len(got) != 0 { t.Errorf("TagsOf still reports a cancelled tag: %v", got) } if tl, _, err := ix.Timeline(TimelineFilter{Limit: 10, Tag: "doomed"}); err != nil || len(tl) != 0 { t.Errorf("the filter still finds a cancelled tag: %d rows (err %v)", len(tl), err) } } // Fossil lets an author correct a check-in comment after the fact with // a `comment` tag, and the correction is what fossil displays. quarry // showed the superseded C card instead — not an omission but a wrong // answer, and 697 fossil check-ins are in that state. func TestAttachCIAppliesCommentCorrectionsAndFlags(t *testing.T) { ix := openIndex(t) a := strings.Repeat("a1", 32) if err := ix.FoldNow([]Parsed{ {Hash: a, Art: &artifact.Artifact{ Type: artifact.Manifest, Date: "2026-08-18T10:00:00", User: "dan", Comment: "fixs the thing"}}, {Hash: strings.Repeat("b2", 32), Art: &artifact.Artifact{ Type: artifact.Manifest, Date: "2026-08-19T10:00:00", User: "dan", Comment: "amend", Tags: []artifact.Tag{ {Name: "+comment", Target: a, Value: "fixes the thing"}, {Name: "*hidden", Target: a}, }}}, }); err != nil { t.Fatal(err) } e, _, ok, err := ix.CheckinByPrefix(a) if err != nil || !ok { t.Fatalf("checkin not found (err %v)", err) } // Undecorated, the stored C card is untouched: it is an artifact // fact and the index must not rewrite it. if e.Comment != "fixs the thing" { t.Errorf("stored comment was rewritten to %q", e.Comment) } entries := []TimelineEntry{e} if err := ix.AttachCI(entries); err != nil { t.Fatal(err) } if entries[0].Comment != "fixes the thing" { t.Errorf("shown comment = %q, want the correction", entries[0].Comment) } if entries[0].CommentOrig != "fixs the thing" { t.Errorf("superseded comment = %q, want it kept", entries[0].CommentOrig) } if !entries[0].Hidden { t.Error("a standing hidden tag was not reported") } if entries[0].Closed { t.Error("closed was reported without a closed tag") } // A check-in nobody corrected must not claim it was corrected. plain := []TimelineEntry{{Hash: strings.Repeat("b2", 32), Comment: "amend"}} if err := ix.AttachCI(plain); err != nil { t.Fatal(err) } if plain[0].CommentOrig != "" { t.Errorf("uncorrected check-in reports an original of %q", plain[0].CommentOrig) } // The history is the whole log, with the superseded row marked. hist, err := ix.TagHistory(a) if err != nil { t.Fatal(err) } if len(hist) != 2 { t.Fatalf("history has %d rows, want 2: %+v", len(hist), hist) } for _, h := range hist { if !h.Standing { t.Errorf("row %+v should stand — nothing overruled it", h) } } } // A branch diff is the branch against its FORK POINT, which is not the // same base a landing uses (the target's current tip). Getting this // wrong shows the right file count against the wrong baseline. func TestForkPointIsWhereTheBranchLeftItsParent(t *testing.T) { ix := openIndex(t) root, mid, br1, br2 := strings.Repeat("a1", 32), strings.Repeat("b2", 32), strings.Repeat("c3", 32), strings.Repeat("d4", 32) ci := func(hash, date string, tags []artifact.Tag, parents ...string) Parsed { return Parsed{Hash: hash, Art: &artifact.Artifact{ Type: artifact.Manifest, Date: date, User: "dan", Comment: "c", Parents: parents, Tags: tags}} } onto := func(name string) []artifact.Tag { return []artifact.Tag{{Name: "*branch", Target: "*", Value: name}, {Name: "*sym-" + name, Target: "*"}} } if err := ix.FoldNow([]Parsed{ ci(root, "2026-08-01T10:00:00", nil), ci(mid, "2026-08-02T10:00:00", nil, root), ci(br1, "2026-08-03T10:00:00", onto("feature"), mid), ci(br2, "2026-08-04T10:00:00", nil, br1), }); err != nil { t.Fatal(err) } // The fork is where the branch left trunk — mid, not the trunk tip // and not the branch's own first check-in. got, err := ix.ForkPoint("feature") if err != nil { t.Fatal(err) } if got != mid { t.Errorf("fork point = %.4s, want %.4s", got, mid) } // The branch's own tip is its newest member, not the repository's. tip, ok, err := ix.TipOf("feature") if err != nil || !ok { t.Fatalf("no tip for the branch (err %v)", err) } if tip.Tip != br2 { t.Errorf("tip = %.4s, want %.4s", tip.Tip, br2) } // A line that forked from nothing has no fork point, and must say // so rather than inventing one — the page uses this to decide // whether to offer the link at all. if got, err := ix.ForkPoint("trunk"); err != nil || got != "" { t.Errorf("trunk fork point = %q (err %v), want empty", got, err) } if got, err := ix.ForkPoint("no-such-branch"); err != nil || got != "" { t.Errorf("unknown branch = %q (err %v), want empty", got, err) } }