Artifact 56f110b70f6f495a6d6169bd9d2d6178a4853b5c2e9100122694a5ddef34ff23:
- File
horizon_history_test.go
— part of check-in
[ee3ccc7c67]
at
2026-10-22 17:21:12
on branch feat/horizon-cones
— feat(history): render a horizon boundary as the horizon
A parent the origin labelled below a structural horizon is the horizon itself (draft-fossil-horizon-00 5.4), but the history views showed it as a bare parent hash, and the ancestry walk tried to read an event for it.
LogEntry and TimelineEntry gain HorizonParents, the subset of Parents that carries a boundary label; Ancestry ends at such a parent without an error; repo timeline prints it as "parent: <hash> (horizon)". An absent parent that is not labelled is transit and renders as before. (user: [email protected], size: 2660) [annotate] [blame] [check-ins using] [more...]
package libfossil import ( "path/filepath" "testing" "github.com/danmestas/go-libfossil/internal/content" ) // horizonHistory is a three-check-in repository cut the way a structural // clone at c2 cuts it: c1, c2's parent, is a phantom with no event. label // says whether the origin labelled it below the horizon. func horizonHistory(t *testing.T, label bool) (r *Repo, c1, c2, c3 string, rid3 int64) { t.Helper() r, err := Create(filepath.Join(t.TempDir(), "h.fossil"), CreateOpts{User: "test"}) if err != nil { t.Fatal(err) } t.Cleanup(func() { r.Close() }) var rids [3]int64 var uuids [3]string parent := int64(0) for i := range rids { rids[i], uuids[i], err = r.Commit(CommitOpts{User: "test", ParentID: parent, Files: []FileToCommit{ {Name: "a", Content: []byte{byte('a' + i)}}, }}) if err != nil { t.Fatal(err) } parent = rids[i] } db := r.Inner().DB() if _, err := db.Exec("UPDATE blob SET size=-1, content=NULL WHERE rid=?", rids[0]); err != nil { t.Fatal(err) } if _, err := db.Exec("DELETE FROM event WHERE objid=?", rids[0]); err != nil { t.Fatal(err) } if label { if err := content.LabelBelow(db, []string{uuids[0]}); err != nil { t.Fatal(err) } } return r, uuids[0], uuids[1], uuids[2], rids[2] } func TestHistoryRendersBoundaryAsHorizon(t *testing.T) { r, c1, c2, c3, rid3 := horizonHistory(t, true) anc, err := r.Ancestry(LogOpts{Start: rid3}) if err != nil { t.Fatalf("ancestry ended at the horizon with an error: %v", err) } if len(anc) != 2 || anc[0].UUID != c3 || anc[1].UUID != c2 { t.Fatalf("ancestry = %+v, want c3, c2 and no more", anc) } if len(anc[0].HorizonParents) != 0 { t.Errorf("c3 horizon parents = %v, want none", anc[0].HorizonParents) } if got := anc[1].HorizonParents; len(got) != 1 || got[0] != c1 { t.Errorf("c2 horizon parents = %v, want [%s]", got, c1) } if got := anc[1].Parents; len(got) != 1 || got[0] != c1 { t.Errorf("c2 parents = %v, want [%s]", got, c1) } tl, err := r.Timeline(TimelineOpts{}) if err != nil { t.Fatal(err) } if len(tl) != 2 || len(tl[1].HorizonParents) != 1 || tl[1].HorizonParents[0] != c1 { t.Fatalf("timeline = %+v, want c2 to name %s as the horizon", tl, c1) } } func TestHistoryUnlabelledAbsentParentIsNotHorizon(t *testing.T) { r, c1, c2, _, _ := horizonHistory(t, false) tl, err := r.Timeline(TimelineOpts{}) if err != nil { t.Fatal(err) } if len(tl) != 2 || tl[1].UUID != c2 || len(tl[1].Parents) != 1 || tl[1].Parents[0] != c1 { t.Fatalf("timeline = %+v", tl) } if len(tl[1].HorizonParents) != 0 { t.Errorf("unlabelled absent parent rendered as horizon: %v", tl[1].HorizonParents) } }