Artifact 6179566c73312bfd360e28f19fc5658b033270c9b3e101639f5cafebdb0ab838:
- File
conformance/horizon_structural_zit_test.go
— part of check-in
[fe7d47778e]
at
2026-10-23 01:40:32
on branch feat/horizon-cones
— fix(sync): label a seed's U card with the minting implementation
The re-pinned draft (5.2.2) makes a seed's U card the origin's own label for the minting implementation, as zit writes zit-serve, never a user's login: a seed is a tag application no user made. The origin wrote the role name "origin"; it now writes "go-libfossil".
The seed-date case is restated to the final text: D is the latest summarized tag event rounded up to a whole second and written with no fraction, checked with an event a quarter second past the second, and the dropped clause (D strictly later than the seed replaced) is no longer asserted. The structural fallback case moves to the new 6.3 row for a store already at the structural horizon. (user: [email protected], size: 9940) [annotate] [blame] [check-ins using] [more...]
package conformance import ( "context" "fmt" "maps" "net/http/httptest" "os" "path/filepath" "slices" "strings" "testing" "github.com/danmestas/go-libfossil/internal/cone" libfossil "github.com/danmestas/go-libfossil/internal/fsltype" "github.com/danmestas/go-libfossil/internal/horizon/seed" "github.com/danmestas/go-libfossil/internal/sync" "github.com/danmestas/go-libfossil/internal/tag" "github.com/danmestas/go-libfossil/internal/verify" "github.com/danmestas/go-libfossil/internal/xfer" ) // TestZitHorizonStructuralInterop runs the structural horizon between // go-libfossil and zit both ways (draft-fossil-horizon-00 §5, §6.2.2, // §6.2.3, §6.2.5) over hzSeed: zit clones and pulls at C's structural horizon // from go-libfossil's handler, and go-libfossil clones and pulls at it from // `zit serve`. Each origin sends no manifest below the horizon, labels the // boundary and sends the seed; each client stays structural and derives the // branch, leaf and tag state of K exactly as a dense clone of the same origin // does. func TestZitHorizonStructuralInterop(t *testing.T) { bin := zitHorizonBinary(t) x := newHZStruct(t) dir := t.TempDir() home := filepath.Join(dir, "home") if err := os.Mkdir(home, 0o755); err != nil { t.Fatal(err) } origin := &hzRecorder{r: x.s.f.r} server := httptest.NewServer(origin) defer server.Close() k := []string{x.c3, x.c4, x.feat2} // zit clones at the structural horizon from go-libfossil. zs := filepath.Join(dir, "zstructural") if out, err := zitCommand(bin, home, dir, "clone", server.URL, zs, "--no-open", "--once", "--horizon="+x.c3, "--structural"); err != nil || strings.Contains(out, "content horizon instead") { t.Fatalf("zit's structural clone from go-libfossil: %v\n%s", err, out) } if err := hzStructZitServed(origin.take(), x); err != nil { t.Fatalf("zit's structural clone from go-libfossil: %v", err) } _, want, err := x.s.mint(seedLoginGo) if err != nil { t.Fatal(err) } if got, err := os.ReadFile(filepath.Join(zs, ".zit-cache", "horizon-seed")); err != nil || string(got) != string(want) { t.Fatalf("zit holds seed %q (%v), want go-libfossil's:\n%s", got, err, want) } zd := filepath.Join(dir, "zdense") zitRun(t, bin, home, dir, "clone", server.URL, zd, "--no-open", "--once") origin.take() if err := hzZitSameState(t, bin, home, zs, zd, k); err != nil { t.Fatalf("zit's structural clone of go-libfossil: %v", err) } // A check-in above the horizon and a tag below it that moves the state // in force at C: zit's pull brings the check-in and the re-minted seed. c5rid := hzCommit(t, x.s.f, "c5", []libfossil.FslID{x.s.c4}, nil) c5 := hzUUID(t, x.s.f.r, c5rid) x.s.tag(t, x.s.c1, "hue", tag.TagPropagating, "blue") k = append(k, c5) zitRun(t, bin, home, zs, "pull", server.URL) if err := hzStructZitServed(origin.take(), x); err != nil { t.Fatalf("zit's structural pull from go-libfossil: %v", err) } zitRun(t, bin, home, zd, "pull", server.URL) origin.take() if err := hzZitSameState(t, bin, home, zs, zd, k); err != nil { t.Fatalf("zit's structural pull from go-libfossil: %v", err) } // go-libfossil clones and pulls at the structural horizon from zit. zitURL := zitServe(t, bin, home, zd) peer := &hzRecordingTransport{inner: &sync.HTTPTransport{URL: zitURL}} r, cloned, err := sync.Clone(context.Background(), filepath.Join(dir, "structural.fossil"), peer, sync.CloneOpts{StoreCone: x.cone(t), PullPhantoms: true}) if err != nil { t.Fatalf("go-libfossil's structural clone from zit: %v", err) } defer r.Close() if cloned.HorizonFallback || cloned.StructuralServed == 0 { t.Fatalf("clone from zit fell back %v, structural in %d of %d rounds", cloned.HorizonFallback, cloned.StructuralServed, cloned.Rounds) } if err := hzCrossed(peer.replies, x.below()); err != nil { t.Fatalf("zit's structural clone reply: %v", err) } if held, _ := seed.Held(r.DB()); held == nil { t.Fatal("go-libfossil holds no seed from zit") } if err := x.s.derivesDense(t, r); err != nil { t.Fatalf("go-libfossil's structural clone of zit: %v", err) } if err := hzElided(r, x.below()); err != nil { t.Fatalf("go-libfossil's structural clone of zit: %v", err) } c6 := hzCommit(t, x.s.f, "c6", []libfossil.FslID{x.s.c3}, hzBranch("side")) x.s.tag(t, x.s.feat1, "closed", tag.TagCancel, "") zitRun(t, bin, home, zd, "pull", server.URL) peer.replies = nil pulled, err := sync.Sync(context.Background(), r, peer, sync.SyncOpts{Pull: true}) if err != nil { t.Fatalf("go-libfossil's structural pull from zit: %v", err) } if pulled.HorizonFallback || pulled.StructuralServed == 0 || len(pulled.Errors) != 0 { t.Fatalf("pull from zit: fell back %v, structural %d of %d rounds, errors %q", pulled.HorizonFallback, pulled.StructuralServed, pulled.Rounds, pulled.Errors) } if err := hzCrossed(peer.replies, x.below()); err != nil { t.Fatalf("zit's structural pull reply: %v", err) } x.s.k = append(x.s.k, c5rid, c6) if err := x.s.derivesDense(t, r); err != nil { t.Fatalf("go-libfossil's structural pull from zit: %v", err) } } // seedLoginGo is the U card go-libfossil's origin mints seeds with: the // origin's label for the minting implementation (§5.2.2), as zit's is // "zit-serve". const seedLoginGo = "go-libfossil" // hzStructZitServed checks zit's structural requests and go-libfossil's // replies: every horizon request carries horizon-structural and a seed offer, // every reply to one says horizon-served structural, and nothing below the // horizon is sent or announced. func hzStructZitServed(exchanges []hzExchange, x *hzStruct) error { scoped := 0 var replies []*xfer.Message for i, e := range exchanges { replies = append(replies, e.reply) if len(hzPragmas(e.request, "horizon")) == 0 { continue } scoped++ if len(hzPragmas(e.request, "horizon-structural")) != 1 || len(hzPragmas(e.request, "horizon-seed")) != 1 { return expectf("request %d: horizon-structural %q, horizon-seed %q", i, hzPragmas(e.request, "horizon-structural"), hzPragmas(e.request, "horizon-seed")) } if got := hzPragmas(e.reply, "horizon-served"); len(got) != 1 || strings.Join(got[0], " ") != "structural" { return expectf("reply %d advertises %q", i, got) } } if scoped == 0 { return expectf("no request of %d carried the horizon pragma", len(exchanges)) } return hzCrossed(replies, x.below()) } // hzZitSameState compares what two zit repositories derive: each check-in // of k described, the branches and the leaves. func hzZitSameState(t *testing.T, bin, home, a, b string, k []string) error { t.Helper() state := func(dir string) string { var out strings.Builder for _, u := range k { out.WriteString(zitRun(t, bin, home, dir, "info", u)) } out.WriteString(zitRun(t, bin, home, dir, "branch")) out.WriteString(zitRun(t, bin, home, dir, "leaves")) return out.String() } if got, want := state(a), state(b); got != want { return expectf("derived state differs from dense:\nstructural:\n%s\ndense:\n%s", got, want) } return nil } // TestZitHorizonStructuralPaths composes the structural horizon with a path // cone over sub-manifest trees, both ways: an origin keeps K's trees but may // withhold the nodes outside the cone (zit since 2b635ea5ce does), so the // client must take those as elided rather than wait on them. go-libfossil // clones from zit serve, and zit from go-libfossil, at C = c3 under cone // src, and each derives K's tag state as the dense origin does. func TestZitHorizonStructuralPaths(t *testing.T) { bin := zitHorizonBinary(t) f := newSCRepo(t, true) if _, err := f.r.DB().Exec("UPDATE user SET cap='gio' WHERE login='nobody'"); err != nil { t.Fatal(err) } files := maps.Clone(scFiles) checkins := []libfossil.FslID{f.c1} for i := 2; i <= 4; i++ { files["src/a.c"] = fmt.Sprintf("a %d\n", i) files["tools/t.c"] = fmt.Sprintf("tools %d\n", i) checkins = append(checkins, f.commit(t, files, checkins[len(checkins)-1])) } c3 := hzUUID(t, f.r, checkins[2]) k := []libfossil.FslID{checkins[2], checkins[3]} dir := t.TempDir() home := filepath.Join(dir, "home") if err := os.Mkdir(home, 0o755); err != nil { t.Fatal(err) } server := httptest.NewServer(xferHandler(t, f.r, func(*xfer.Message) {})) defer server.Close() zs := filepath.Join(dir, "zstructural") if out, err := zitCommand(bin, home, dir, "clone", server.URL, zs, "--no-open", "--once", "--cone=src", "--horizon="+c3, "--structural"); err != nil || strings.Contains(out, "content horizon instead") { t.Fatalf("zit's structural clone under cone src from go-libfossil: %v\n%s", err, out) } zitRun(t, bin, home, zs, "verify") zd := filepath.Join(dir, "zdense") zitRun(t, bin, home, dir, "clone", server.URL, zd, "--no-open", "--once") h, err := cone.NewHorizon(c3, cone.RuleDescendants, cone.DepthStructural) if err != nil { t.Fatal(err) } sc, err := cone.NewWithHorizon([]string{"src"}, nil, h) if err != nil { t.Fatal(err) } r, cloned, err := sync.Clone(context.Background(), filepath.Join(dir, "structural.fossil"), &sync.HTTPTransport{URL: zitServe(t, bin, home, zd)}, sync.CloneOpts{StoreCone: sc, PullPhantoms: true}) if err != nil { t.Fatalf("go-libfossil's structural clone under cone src from zit: %v", err) } defer r.Close() if cloned.HorizonFallback || cloned.StructuralServed == 0 { t.Fatalf("clone fell back %v, structural in %d of %d rounds", cloned.HorizonFallback, cloned.StructuralServed, cloned.Rounds) } report, err := verify.Verify(r) if err != nil || !report.OK() || report.Phantoms != 0 { t.Fatalf("verify: %v ok=%v transit=%d issues=%v", err, report.OK(), report.Phantoms, report.Issues) } for _, rid := range k { u := hzUUID(t, f.r, rid) want, _ := hzTagState(f.r.DB(), u) got, err := hzTagState(r.DB(), u) if err != nil || !slices.Equal(got, want) { t.Fatalf("check-in %.10s carries %v, dense %v (%v)", u, got, want, err) } } }