package packageseats import ( "crypto/sha256" "encoding/hex" "errors" "strings" "testing" "time" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/handacts" "git.novox.be/novox/mesh-sdk/go/parts" ) type memObjects map[string][]byte func (m memObjects) Put(n string, d []byte) error { m[n] = append([]byte(nil), d...); return nil } func (m memObjects) Get(n string) ([]byte, error) { if d, ok := m[n]; ok { return d, nil } return nil, errors.New("no object " + n) } func (m memObjects) Delete(n string) error { delete(m, n); return nil } func (m memObjects) Used() (uint64, error) { var u uint64 for _, d := range m { u += uint64(len(d)) } return u, nil } type memTransfers map[string]Transfer func (m memTransfers) Get(id string) (Transfer, bool, error) { t, ok := m[id]; return t, ok, nil } func (m memTransfers) Put(t Transfer) error { m[t.ID] = t; return nil } func (m memTransfers) Delete(id string) error { delete(m, id); return nil } func (m memTransfers) List() ([]Transfer, error) { var out []Transfer for _, t := range m { out = append(out, t) } return out, nil } type builder struct{ ok bool } func (b builder) Recorded(_, _, _ string) (bool, string, error) { return b.ok, "not numbered", nil } // forge is the forge's package registry: seat -> name@version -> file. type forge struct { files map[string]map[string][]byte removed []string } func (f *forge) Held(seat, name, version string) (string, bool, error) { b, ok := f.files[seat][name+"@"+version] if !ok { return "", false, nil } return digest(b), true, nil } func (f *forge) Put(seat string, p Publish, file []byte) (string, error) { if f.files[seat] == nil { f.files[seat] = map[string][]byte{} } f.files[seat][p.Name+"@"+p.Version] = file if seat == GoSeat { return "git.example/" + p.Name, nil } return "", nil } func (f *forge) Versions(seat, name string) ([]Version, error) { var out []Version for k, b := range f.files[seat] { if n, v, _ := strings.Cut(k, "@"); n == name { out = append(out, Version{Version: v, Digest: digest(b)}) } } return out, nil } func (f *forge) Remove(seat, name, version string) error { delete(f.files[seat], name+"@"+version) f.removed = append(f.removed, seat+" "+name+"@"+version) return nil } func digest(b []byte) string { s := sha256.Sum256(b); return "sha256:" + hex.EncodeToString(s[:]) } type said struct { seat, event string o Outcome } type bed struct { h *Holder forge *forge said []said findings map[string]bool } func newBed(numbered bool) *bed { b := &bed{forge: &forge{files: map[string]map[string][]byte{}}, findings: map[string]bool{}} b.h = &Holder{Callers: DefaultCallers(), Objects: memObjects{}, Transfers: memTransfers{}, Builder: builder{ok: numbered}, Store: b.forge, Now: func() time.Time { return time.Unix(1e9, 0) }, Say: func(seat, event string, o Outcome) error { b.said = append(b.said, said{seat, event, o}); return nil }, Finding: func(kind, subject, _ string, raised bool) { b.findings[kind+" "+subject] = raised }} return b } func pub(name, version string, file []byte) Publish { return Publish{Transfer: "t-" + name, Name: name, Version: version, Kind: KindPackage, Digest: digest(file), Parts: []parts.Want{{Digest: digest(file), Size: int64(len(file))}}} } const goBuild, nodeBuild = "ace.mesh-build-go", "ace.mesh-build-node" // **The forge module holds both package seats** (design 51 *Publishing*, *Testing* for the holder): a package's one // file comes in parts, is checked against the builder's numbering and its digest, and is uploaded with the forge's own // write side; the outcome is said on the seat's events. func TestTheForgeHoldsBothPackageSeats(t *testing.T) { file := []byte("a package's tarball") t.Run("a publish in parts is uploaded and said published", func(t *testing.T) { for _, seat := range []string{NpmSeat, GoSeat} { b := newBed(true) a, err := b.h.Publish(seat, goBuild, pub("mesh-sdk", "0.1.15", file)) if err != nil || len(a.Lacks) != 1 || a.Transfer != "t-mesh-sdk" { t.Fatalf("%s: publish answered %+v, %v", seat, a, err) } a, err = b.h.PublishPart(seat, goBuild, "t-mesh-sdk", digest(file), 0, true, file) if err != nil || !a.Ended { t.Fatalf("%s: the last chunk answered %+v, %v", seat, a, err) } if len(b.said) != 1 || b.said[0].event != EventPublished || b.said[0].o.Reference != "mesh-sdk@0.1.15" { t.Fatalf("%s: said %+v", seat, b.said) } if (seat == GoSeat) != (b.said[0].o.Module != "") { t.Errorf("%s: the Go module path said is %q", seat, b.said[0].o.Module) } if len(b.h.Objects.(memObjects)) != 0 || len(b.h.Transfers.(memTransfers)) != 0 { t.Errorf("%s: the transfer's chunks or key stayed after its end", seat) } if v, _ := b.h.Versions(seat, "g14.anyone", "mesh-sdk"); len(v) != 1 || v[0].Digest != digest(file) { t.Errorf("%s: versions answered %+v", seat, v) } // A rebuild of the published version is held: nothing uploaded, published said again. if a, err := b.h.Publish(seat, goBuild, pub("mesh-sdk", "0.1.15", file)); err != nil || !a.Ended || len(a.Lacks) != 0 { t.Errorf("%s: a held version answered %+v, %v", seat, a, err) } } }) // Tools review F4 and running review F2's kind: a seat's incoming is read per seat with the cap its binding // carries; an open transfer is armed with its idle key, re-armed for the rest when it took a chunk since, and // dropped with its chunks when it had none for 24 hours; a cap from the binding refuses a transfer past it. t.Run("incoming: read per seat, the binding's cap, an idle transfer dropped when its key ends", func(t *testing.T) { b := newBed(true) now := time.Unix(1e9, 0) b.h.Now = func() time.Time { return now } armed := map[string]time.Duration{} b.h.Idle = func(k string, ttl time.Duration) error { armed[k] = ttl; return nil } b.h.Cap = func() (uint64, error) { return 1000, nil } big := []byte(strings.Repeat("x", 600)) if _, err := b.h.Publish(NpmSeat, nodeBuild, pub("a", "0.1.0", big)); err != nil { t.Fatal(err) } if _, err := b.h.Publish(GoSeat, goBuild, pub("b", "0.1.0", big)); err != nil || armed["go-package-registry_t-b"] != TransferIdle { t.Fatalf("publish = %v, armed %v; want the Go transfer's idle key for %s", err, armed, TransferIdle) } _, _ = b.h.PublishPart(NpmSeat, nodeBuild, "t-a", digest(big), 0, false, big[:500]) in, err := b.h.IncomingOf(NpmSeat) if err != nil || len(in.Transfers) != 1 || in.Transfers[0].Shape.Name != "a" || in.Cap != 1000 || in.Used != 500 { t.Fatalf("IncomingOf(npm) = %+v, %v; want a's transfer alone, 500 used of the binding's 1000", in, err) } if _, err := b.h.Publish(NpmSeat, nodeBuild, pub("c", "0.1.0", big)); err == nil || !strings.Contains(err.Error(), "cap") { t.Errorf("a transfer past the binding's cap = %v; want refused at the cap", err) } now = now.Add(time.Hour) _, _ = b.h.PublishPart(GoSeat, goBuild, "t-b", digest(big), 0, false, big[:10]) now = now.Add(TransferIdle - time.Hour) if dropped, err := b.h.Expired("go-package-registry_t-b"); err != nil || dropped || armed["go-package-registry_t-b"] != time.Hour { t.Errorf("b's key ends an hour after a chunk: %v, %v, armed %v; want kept, armed 1h", dropped, err, armed) } if dropped, err := b.h.Expired("npm-package-registry_t-a"); err != nil || !dropped { t.Errorf("a's key ends 24 hours after its last chunk: %v, %v; want dropped", dropped, err) } if in, _ := b.h.IncomingOf(NpmSeat); len(in.Transfers) != 0 || in.Used != 10 { t.Errorf("after a's drop: %+v; want no npm transfer and only b's 10 bytes used", in) } b.h.Idle = func(string, time.Duration) error { return errors.New("bus down") } if _, err := b.h.Publish(NpmSeat, nodeBuild, pub("d", "0.1.0", file)); err == nil || !strings.Contains(err.Error(), "idle bound") { t.Errorf("a publish whose idle key is not written = %v; want it failed loudly", err) } // Seams note 2: go_incoming's drop is a hand act recorded through mesh-hand-acts before anything goes; an act // not recorded leaves the transfer open. var acts []handacts.Request b.h.Record = func(handacts.Request) error { return errors.New("mesh-hand-acts has no holder") } if _, err := b.h.DropByHand(GoSeat, "t-b", "person.jochen", "stuck"); err == nil || !strings.Contains(err.Error(), "stays open") { t.Errorf("a drop whose act is not recorded = %v; want refused, t-b open", err) } b.h.Record = func(q handacts.Request) error { acts = append(acts, q); return nil } if got, err := b.h.DropByHand(GoSeat, "t-b", "person.jochen", "stuck"); err != nil || got.Shape.Name != "b" || len(acts) != 1 || acts[0].Cause != "go_incoming" || !strings.Contains(acts[0].What, "t-b") || !strings.Contains(acts[0].What, "person.jochen") { t.Errorf("a drop by hand = %+v, %v, acts %+v; want t-b dropped and its act recorded", got, err, acts) } if in, _ := b.h.IncomingOf(GoSeat); len(in.Transfers) != 0 { t.Errorf("after the drop by hand: %+v; want no Go transfer", in) } }) t.Run("refusals", func(t *testing.T) { for _, c := range []struct { name string do func(b *bed) error want string }{ {"a caller not on publish's list", func(b *bed) error { _, err := b.h.Publish(NpmSeat, "ace.claude-code", pub("x", "0.1.0", file)) return err }, "not on the holder's caller list"}, {"an unproven caller", func(b *bed) error { _, err := b.h.Publish(NpmSeat, stdio.Unproven, pub("x", "0.1.0", file)) return err }, "no caller the tool runner proved"}, {"a seat the forge does not hold", func(b *bed) error { _, err := b.h.Publish("cargo-package-registry", goBuild, pub("x", "0.1.0", file)) return err }, "holds no seat"}, {"a package in two files", func(b *bed) error { p := pub("x", "0.1.0", file) p.Parts = append(p.Parts, parts.Want{Digest: digest([]byte("b")), Size: 1}) _, err := b.h.Publish(NpmSeat, goBuild, p) return err }, "one file"}, {"a part from another module than its opener", func(b *bed) error { if _, err := b.h.Publish(NpmSeat, nodeBuild, pub("x", "0.1.0", file)); err != nil { return err } _, err := b.h.PublishPart(NpmSeat, goBuild, "t-x", digest(file), 0, true, file) return err }, "opened"}, {"a transfer of the other seat", func(b *bed) error { if _, err := b.h.Publish(NpmSeat, nodeBuild, pub("x", "0.1.0", file)); err != nil { return err } _, err := b.h.PublishPart(GoSeat, nodeBuild, "t-x", digest(file), 0, true, file) return err }, "not open"}, {"remove by a build module", func(b *bed) error { _, err := b.h.Remove(GoSeat, goBuild, "x", "0.1.1-feat.1", "tag-gone"); return err }, "not on the holder's caller list"}, {"remove of a version with no tag", func(b *bed) error { _, err := b.h.Remove(GoSeat, "novox.mesh-delivery-builder", "x", "0.1.1", "tag-gone") return err }, "has no tag"}, } { b := newBed(true) if err := c.do(b); err == nil || !strings.Contains(err.Error(), c.want) { t.Errorf("%s: %v, want %q", c.name, err, c.want) } } }) t.Run("a refused publish is said refused and raises the finding; nothing is uploaded", func(t *testing.T) { for _, c := range []struct { name string numbered bool data []byte want string }{{"the builder did not number it", false, file, "did not number"}} { b := newBed(c.numbered) if _, err := b.h.Publish(NpmSeat, nodeBuild, pub("x", "0.1.0", file)); err != nil { t.Fatal(err) } _, _ = b.h.PublishPart(NpmSeat, nodeBuild, "t-x", digest(file), 0, true, c.data) if len(b.said) != 1 || b.said[0].event != EventRefused || !strings.Contains(b.said[0].o.Reason, c.want) { t.Errorf("%s: said %+v", c.name, b.said) } if !b.findings["publish-refused x@0.1.0"] || len(b.forge.files[NpmSeat]) != 0 { t.Errorf("%s: finding %v, uploaded %v", c.name, b.findings, b.forge.files) } } // A part whose bytes do not match its digest is refused to its sender, to send again; nothing is said yet. b := newBed(true) if _, err := b.h.Publish(NpmSeat, nodeBuild, pub("x", "0.1.0", file)); err != nil { t.Fatal(err) } if _, err := b.h.PublishPart(NpmSeat, nodeBuild, "t-x", digest(file), 0, true, []byte("other bytes")); err == nil || !strings.Contains(err.Error(), "send the part again") || len(b.said) != 0 || len(b.forge.files[NpmSeat]) != 0 { t.Errorf("a part with other bytes: %v, said %+v", err, b.said) } // The same version published with other content is refused, never uploaded over. b = newBed(true) b.forge.files[NpmSeat] = map[string][]byte{"x@0.1.0": []byte("first")} if _, err := b.h.Publish(NpmSeat, nodeBuild, pub("x", "0.1.0", file)); err != nil || len(b.said) != 1 || b.said[0].event != EventRefused { t.Errorf("a published version with other content: %v, said %+v", err, b.said) } }) t.Run("the builder removes a tagged version; one already gone is said removed all the same", func(t *testing.T) { b := newBed(true) b.forge.files[GoSeat] = map[string][]byte{"x@0.1.1-feat.1": file} for range 2 { if _, err := b.h.Remove(GoSeat, "novox.mesh-delivery-builder", "x", "0.1.1-feat.1", "tag-gone"); err != nil { t.Fatal(err) } } if len(b.forge.removed) != 1 || len(b.said) != 2 || b.said[0].event != EventRemoved || b.said[0].o.Digest != digest(file) { t.Errorf("removed %v, said %+v", b.forge.removed, b.said) } }) }