Artifact f1a8cd42b47cac517491b1aa3eb6a7f17bd33439799cf0a9b61336c8f1c0054b:
- File
apitest/horizon_origin_test.go
— part of check-in
[6956a92661]
at
2026-10-26 14:00:50
on branch main
— test: move black-box root tests to apitest/
Every package libfossil_test file in the root used only exported identifiers and shared no helpers with the white-box tests, so all 21 move to a test-only apitest package. Docs that name their location follow. (user: [email protected], size: 3809) [annotate] [blame] [check-ins using] [more...]
package apitest_test import ( "context" "path/filepath" "testing" "github.com/danmestas/go-libfossil" ) // embedderTransport is a Transport defined outside the package, as an // embedder's NATS or in-process transport is: it reaches the origin directly // and names it by a stable string. type embedderTransport struct { origin *libfossil.Repo name string } func (t embedderTransport) RoundTrip(ctx context.Context, payload []byte) ([]byte, error) { return t.origin.HandleSync(ctx, payload) } func (t embedderTransport) SyncOrigin() string { return t.name } var _ libfossil.OriginNamer = embedderTransport{} // igotCounter counts the igot cards every request of a sync announces. type igotCounter struct { libfossil.SyncObserver igots int } func (c *igotCounter) RoundCompleted(_ int, stats libfossil.RoundStats) { c.igots += stats.IGots } // newOriginFixture is an origin of three check-ins and a store cone at the // horizon of the second. func newOriginFixture(t *testing.T) (*libfossil.Repo, *libfossil.Cone) { t.Helper() origin, err := libfossil.Create(filepath.Join(t.TempDir(), "origin.fossil"), libfossil.CreateOpts{User: "test"}) if err != nil { t.Fatal(err) } t.Cleanup(func() { origin.Close() }) var parent int64 var horizon string for i, body := range []string{"one\n", "two\n", "three\n"} { rid, uuid, err := origin.Commit(libfossil.CommitOpts{User: "test", ParentID: parent, Files: []libfossil.FileToCommit{{Name: "a", Content: []byte(body)}}}) if err != nil { t.Fatal(err) } if i == 1 { horizon = uuid } parent = rid } cone, err := libfossil.NewHorizonCone(nil, nil, libfossil.Horizon{Checkin: horizon}) if err != nil { t.Fatal(err) } return origin, cone } // cloneThenPull clones a horizon store over t and pulls once more, returning // the clone and the igot cards the pull announced. func cloneThenPull(t *testing.T, origin *libfossil.Repo, cone *libfossil.Cone, tr libfossil.Transport) (*libfossil.Repo, int) { t.Helper() clone, _, err := libfossil.Clone(context.Background(), filepath.Join(t.TempDir(), "clone.fossil"), tr, libfossil.CloneOpts{StoreCone: cone}) if err != nil { t.Fatal(err) } t.Cleanup(func() { clone.Close() }) counter := &igotCounter{SyncObserver: libfossil.NopSyncObserver()} if _, err := clone.Sync(context.Background(), tr, libfossil.SyncOpts{Pull: true, Observer: counter}); err != nil { t.Fatal(err) } return clone, counter.igots } // A Transport an embedder defines keeps the horizon cursor of // draft-fossil-horizon-00 §6.2.6 when it names its origin: the clone records // the cursor under that name, and the pull that follows a converged exchange // announces nothing with igot. func TestEmbedderTransportKeepsHorizonCursor(t *testing.T) { origin, cone := newOriginFixture(t) const name = "nats://origin.example/project" clone, igots := cloneThenPull(t, origin, cone, embedderTransport{origin: origin, name: name}) cursor, err := clone.Config("horizon-cursor:" + name) if err != nil { t.Fatal(err) } if cursor == "" { t.Fatalf("no horizon cursor recorded for %s", name) } if igots != 0 { t.Fatalf("pull under a cursor announced %d igot cards, want 0", igots) } } // A Transport that names no origin keeps no cursor, and its pull announces // the client's whole holding, which §6.2.6 allows. func TestUnnamedTransportKeepsNoHorizonCursor(t *testing.T) { origin, cone := newOriginFixture(t) tr := libfossil.TransportFunc(func(ctx context.Context, payload []byte) ([]byte, error) { return origin.HandleSync(ctx, payload) }) if _, ok := libfossil.Transport(tr).(libfossil.OriginNamer); ok { t.Fatal("TransportFunc names an origin") } _, igots := cloneThenPull(t, origin, cone, tr) if igots == 0 { t.Fatal("pull without a cursor announced nothing") } }