Artifact fc5f77b90043ac7e0cff19b5977d76dae2b4c36fe10d4a4af911c6d6887ef53f:
- File
conformance/horizon_seedrefuse_test.go
— part of check-in
[7a375b6d73]
at
2026-10-26 01:18:55
on branch main
— test(conformance): close four partial horizon rows
HZ-5.2-2: the seed label appears on no artifact an origin or its structural clone stores, across re-seeding and writes on both sides; the minted floor survives a restart of the origin. HZ-5.2-9: a seed's missing base filled by a pull, a local check-in and a local tag leaves the seed refused, unlinked and unserved, and the transfer that filled it linked. HZ-6.1-2: path-cone and cursor pragmas beside horizon-name are answered as beside pragma horizon, and act; a named clone with path members sends them and records both. HZ-6.2-17: an event-mtime tie resolves to the lowest full hash for tip and a branch; a full-hash-shaped name no check-in has resolves as a symbolic name, and one a check-in has names that check-in first. (user: [email protected], size: 10640) [annotate] [blame] [check-ins using] [more...]
package conformance import ( "context" "strings" "testing" "time" "github.com/danmestas/go-libfossil/internal/blob" "github.com/danmestas/go-libfossil/internal/content" "github.com/danmestas/go-libfossil/internal/deck" "github.com/danmestas/go-libfossil/internal/delta" "github.com/danmestas/go-libfossil/internal/hash" "github.com/danmestas/go-libfossil/internal/sync" "github.com/danmestas/go-libfossil/internal/tag" "github.com/danmestas/go-libfossil/internal/xfer" ) // An origin identifies a pushed seed by its own seed label, or by a // horizon-seed T card under any label, on expanded content, and refuses the // whole request with one error card (horizon §5.2.3). Each case pushes a seed, with no // horizon-seed pragma naming it, beside an ordinary artifact, and checks // that the reply is the refusal alone and that neither was stored. func horizonSeedRefuseCases() []Case { return []Case{horizonSeedRefuseFull, horizonSeedRefuseDelta, horizonSeedRefuseLate, horizonPushCheckLate, horizonSeedShapeFull, horizonSeedShapeLate, horizonForeignOrdinary, horizonSeedLaterRequest} } var ( horizonSeedRefuseFull = Case{ID: "horizon.5.2.3.seed-label-full-text", Req: "HZ-5.2-4"} horizonSeedRefuseDelta = Case{ID: "horizon.5.2.3.seed-label-delta", Req: "HZ-5.2-4"} horizonSeedRefuseLate = Case{ID: "horizon.5.2.3.seed-label-late-delta", Req: "HZ-5.2-4"} horizonSeedShapeFull = Case{ID: "horizon.5.2.3.seed-shape-foreign-label", Req: "HZ-5.2-10"} horizonSeedShapeLate = Case{ID: "horizon.5.2.3.seed-shape-late-delta", Req: "HZ-5.2-10"} horizonForeignOrdinary = Case{ID: "horizon.5.2.3.foreign-label-ordinary", Req: "HZ-5.2-10"} horizonSeedLaterRequest = Case{ID: "horizon.5.2.3.seed-base-later-request", Req: "HZ-5.2-9"} ) const hzSeedRefused = "seed refused: a seed is private to the clone that holds it; it is never pushed" func TestHorizonSeedRefuse(t *testing.T) { check(t, horizonSeedRefuseFull, func(t *testing.T) error { return hzSeedRefuse(t, hzOwnSeed, hzFullText) }) check(t, horizonSeedRefuseDelta, func(t *testing.T) error { return hzSeedRefuse(t, hzOwnSeed, func(x *hzStruct, name string, data []byte) ([]xfer.Card, []string, error) { rid, ok := blob.Exists(x.s.f.r.DB(), x.c3) if !ok { return nil, nil, expectf("the origin does not hold c3") } base, err := content.Expand(x.s.f.r.DB(), rid) if err != nil { return nil, nil, err } d, err := delta.Create(base, data) if err != nil { return nil, nil, err } return []xfer.Card{&xfer.FileCard{UUID: name, DeltaSrc: x.c3, Content: d}}, nil, nil }) }) check(t, horizonPushCheckLate, func(t *testing.T) error { return hzPushCheckLate(t) }) check(t, horizonSeedRefuseLate, func(t *testing.T) error { return hzSeedRefuse(t, hzOwnSeed, hzLateDelta) }) check(t, horizonSeedShapeFull, func(t *testing.T) error { return hzSeedRefuse(t, hzForeignSeed(true), hzFullText) }) check(t, horizonSeedShapeLate, func(t *testing.T) error { return hzSeedRefuse(t, hzForeignSeed(true), hzLateDelta) }) check(t, horizonForeignOrdinary, func(t *testing.T) error { return hzForeignStored(t) }) check(t, horizonSeedLaterRequest, func(t *testing.T) error { return hzSeedLaterRequest(t) }) } // hzOwnSeed is the seed the origin mints, under its own label. func hzOwnSeed(x *hzStruct) (string, []byte, error) { return x.s.mint(seedLoginGo) } // hzForeignSeed is a control artifact on c3 under zit's seed label, carrying // a horizon-seed T card when bound. func hzForeignSeed(bound bool) func(x *hzStruct) (string, []byte, error) { return func(x *hzStruct) (string, []byte, error) { name := "hue" if bound { name = "horizon-seed" } d := &deck.Deck{Type: deck.Control, D: time.Date(2026, 10, 3, 0, 0, 0, 0, time.UTC), U: deck.User("zit-seed"), T: []deck.TagCard{{Type: deck.TagSingleton, Name: name, UUID: x.c3, Value: x.c3}}} data, err := d.Marshal() if err != nil { return "", nil, err } return hash.SHA1(data), data, nil } } // hzFullText sends the seed as full text. func hzFullText(x *hzStruct, name string, data []byte) ([]xfer.Card, []string, error) { return []xfer.Card{&xfer.FileCard{UUID: name, Content: data}}, nil, nil } // hzLateDelta sends the seed as a delta whose base, an ordinary control // artifact, follows it in the same request. func hzLateDelta(x *hzStruct, name string, data []byte) ([]xfer.Card, []string, error) { b := &deck.Deck{Type: deck.Control, D: time.Date(2026, 10, 2, 0, 0, 0, 0, time.UTC), U: deck.User("u"), T: []deck.TagCard{{Type: deck.TagSingleton, Name: "late", UUID: x.c3}}} base, err := b.Marshal() if err != nil { return nil, nil, err } baseName := hash.SHA1(base) d, err := delta.Create(base, data) if err != nil { return nil, nil, err } return []xfer.Card{&xfer.FileCard{UUID: name, DeltaSrc: baseName, Content: d}, &xfer.FileCard{UUID: baseName, Content: base}}, []string{baseName}, nil } // hzForeignStored: a control artifact under another implementation's seed // label with no horizon-seed T card is ordinary, and is stored. func hzForeignStored(t *testing.T) error { x := newHZStruct(t) name, data, err := hzForeignSeed(false)(x) if err != nil { return err } var pc string if err := x.s.f.r.DB().QueryRow("SELECT value FROM config WHERE name='project-code'").Scan(&pc); err != nil { return err } req := &xfer.Message{Cards: []xfer.Card{&xfer.PushCard{ServerCode: "x", ProjectCode: pc}, &xfer.FileCard{UUID: name, Content: data}}} resp, err := sync.HandleSync(context.Background(), x.s.f.r, req) if err != nil { return err } for _, c := range resp.Cards { if e, ok := c.(*xfer.ErrorCard); ok { return expectf("pushing a U zit-seed artifact: error %q", e.Message) } } if _, ok := blob.Exists(x.s.f.r.DB(), name); !ok { return expectf("the origin did not store a U zit-seed artifact with no horizon-seed card") } return nil } // hzSeedRefuse pushes the seed seedOf makes as cards builds it, after an // ordinary artifact, and checks the refusal and that nothing was stored: // not the seed, the ordinary artifact, nor the extra names. func hzSeedRefuse(t *testing.T, seedOf func(x *hzStruct) (string, []byte, error), cards func(x *hzStruct, name string, data []byte) ([]xfer.Card, []string, error)) error { x := newHZStruct(t) name, data, err := seedOf(x) if err != nil { return err } seedCards, extra, err := cards(x, name, data) if err != nil { return err } var pc string if err := x.s.f.r.DB().QueryRow("SELECT value FROM config WHERE name='project-code'").Scan(&pc); err != nil { return err } ordinary := []byte("an ordinary artifact pushed beside the seed\n") req := &xfer.Message{Cards: append([]xfer.Card{&xfer.PushCard{ServerCode: "x", ProjectCode: pc}, &xfer.FileCard{UUID: hash.SHA1(ordinary), Content: ordinary}}, seedCards...)} resp, err := sync.HandleSync(context.Background(), x.s.f.r, req) if err != nil { return err } if len(resp.Cards) != 1 { return expectf("the reply has %d cards, want the refusal alone", len(resp.Cards)) } if e, ok := resp.Cards[0].(*xfer.ErrorCard); !ok || e.Message != hzSeedRefused { return expectf("the reply is %#v, want error %q", resp.Cards[0], hzSeedRefused) } for _, u := range append([]string{name, hash.SHA1(ordinary)}, extra...) { if _, ok := blob.Exists(x.s.f.r.DB(), u); ok { return expectf("the refused request stored %.10s", u) } } return nil } var horizonPushCheckLate = Case{ID: "horizon.5.7.push-check-late-delta", Req: "HZ-5.7-2"} // hzPushCheckLate: the §5.7 check is judged on expanded content as the seed // check is. A stale cancellation sent as a delta whose base arrives later in // the same push is refused whole, the base not stored either. func hzPushCheckLate(t *testing.T) error { x := newHZStruct(t) x.s.tag(t, x.s.c4, "branch", tag.TagPropagating, "moved") x.s.tag(t, x.s.c4, "sym-moved", tag.TagPropagating, "") x.s.tag(t, x.s.c4, "sym-trunk", tag.TagCancel, "") nb := &deck.Deck{Type: deck.Checkin, C: "nb", D: time.Date(2026, 10, 9, 0, 0, 0, 0, time.UTC), U: deck.User("u"), P: []string{x.c4}, T: []deck.TagCard{ {Type: deck.TagPropagating, Name: "branch", UUID: "*", Value: "nb"}, {Type: deck.TagPropagating, Name: "sym-nb", UUID: "*"}, {Type: deck.TagCancel, Name: "sym-trunk", UUID: "*"}, }} data, err := nb.Marshal() if err != nil { return err } base := []byte("the base of a late delta\n") d, err := delta.Create(base, data) if err != nil { return err } var pc string if err := x.s.f.r.DB().QueryRow("SELECT value FROM config WHERE name='project-code'").Scan(&pc); err != nil { return err } name, baseName := hash.SHA1(data), hash.SHA1(base) req := &xfer.Message{Cards: []xfer.Card{&xfer.PushCard{ServerCode: "x", ProjectCode: pc}, &xfer.FileCard{UUID: name, DeltaSrc: baseName, Content: d}, &xfer.FileCard{UUID: baseName, Content: base}}} resp, err := sync.HandleSync(context.Background(), x.s.f.r, req) if err != nil { return err } if len(resp.Cards) != 1 { return expectf("the reply has %d cards, want the refusal alone", len(resp.Cards)) } if e, ok := resp.Cards[0].(*xfer.ErrorCard); !ok || !strings.HasPrefix(e.Message, "stale seed: ") || !strings.Contains(e.Message, "'moved'") { return expectf("the reply is %#v, want the stale-seed refusal naming moved", resp.Cards[0]) } for _, u := range []string{name, baseName} { if _, ok := blob.Exists(x.s.f.r.DB(), u); ok { return expectf("the refused push stored %.10s", u) } } return nil } // hzSeedLaterRequest (B.4.4): a seed pushed as a delta whose base the // origin lacks is stored dangling, since it cannot be judged. A later push // of the base alone succeeds and stores the base; the seed, judged when it // first becomes readable, is neither crosslinked nor served, and its tags // never reach derived state, a rebuild's included. It is refused by its // hash (F.1), kept stored but never linked or served. The refusal is // store-local: a shun entry would reach every peer that pulls the shun // group, and a horizon clone holding this, its current seed, would then // drop it. func hzSeedLaterRequest(t *testing.T) error { x := newHZStruct(t) r := x.s.f.r name, data, err := hzOwnSeed(x) if err != nil { return err } seedCards, extra, err := hzLateDelta(x, name, data) if err != nil { return err } if err := hzPushed(r, "the dangling delta", seedCards[0]); err != nil { return err } if err := hzPushed(r, "the base's push", seedCards[1]); err != nil { return err } for _, u := range extra { if !hzHeldReal(r, u) { return expectf("the base %.10s was not stored", u) } } return hzSeedInert(r, name) }