mesh-contracts now defines the node-engine seat whole (ADR 0613 §3): its verbs carry answers and it says manifest-reads-changed, so the set compares it as it compares every whole seat. Nothing else here is redundant: the seat's hand-written list went in SDK-8, and the compose-candidate layouts keep their checks, which a generated package does not carry, until the seat's client package exists (ADR 0625 §2, proposed). Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
133 lines
4.9 KiB
Go
133 lines
4.9 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"reflect"
|
|
"slices"
|
|
"testing"
|
|
)
|
|
|
|
// Defends novox/hq ADR 0110: a seat is a module assignment from a closed set.
|
|
|
|
func claimed(claims string) []byte {
|
|
return []byte(`{"module":"thing","version":"1","provides":[{"name":"npm-package-registry","scope":"mesh","credential":{"form":"none"}}],"claims":` + claims + `}`)
|
|
}
|
|
|
|
// The holder of a seat answers among several providers.
|
|
|
|
// The working set is loaded from the store, and an empty load never erases it (novox/hq ADR 0122).
|
|
func TestUseSeatsReplacesTheSetButNeverEmptiesIt(t *testing.T) {
|
|
before := Seats()
|
|
defer UseSeats(DefaultSeats()) // restore for other tests, whatever this leaves it as
|
|
|
|
// An empty load (store not seeded, or unreadable) leaves the compiled defaults in force.
|
|
UseSeats(nil)
|
|
if len(Seats()) != len(before) {
|
|
t.Fatalf("an empty load changed the set from %d to %d seats", len(before), len(Seats()))
|
|
}
|
|
// A non-empty load replaces it — this is how a rename in the store reaches the lookups.
|
|
UseSeats([]Seat{{Name: "node-firewall", Scope: ScopeNode, Decision: "novox/hq ADR 0122"}})
|
|
if _, known := SeatNamed("node-firewall"); !known {
|
|
t.Fatal("the loaded set did not replace the working set")
|
|
}
|
|
if len(Seats()) != 1 {
|
|
t.Fatalf("the working set is %d seats, not the one that was loaded", len(Seats()))
|
|
}
|
|
|
|
// **The compiled set is the generated one, and the move changed nothing a reader sees** (design 26, SD-M3, SDK-8):
|
|
// every seat the list mesh-sdk compiled before the move held (testdata, written from 5a38a12) is in the set
|
|
// generated from mesh-contracts' definitions, entry for entry, its verbs' optional marks and a kind's code
|
|
// included. A seat defined whole may add verbs, events and answers; the vault drops accept and move-asks (ADR 0620
|
|
// §3); three registry seats are new.
|
|
UseSeats(DefaultSeats())
|
|
type golden struct {
|
|
Seat
|
|
Optional []string
|
|
ShapeCode []string
|
|
}
|
|
raw, err := os.ReadFile("testdata/seats-before-move.json")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var compiled []golden
|
|
if err := json.Unmarshal(raw, &compiled); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
whole := []string{"npm-package-registry", "mesh-broker", "mesh-config", "mesh-vault", "mesh-delivery-checker", "node-engine"}
|
|
dropped := map[string]bool{"mesh-vault.accept": true, "mesh-vault.move-asks": true}
|
|
// Verbs a seat gained in the milestone, each optional until its holders serve it: node-engine's manifest-reads (ADR
|
|
// 0618 §3) came with its move whole.
|
|
addedVerbs := map[string]bool{"node-engine.pull-waits": true, "node-engine.manifest-reads": true}
|
|
norm := func(v any) any {
|
|
b, _ := json.Marshal(v)
|
|
var out any
|
|
_ = json.Unmarshal(b, &out)
|
|
return out
|
|
}
|
|
var names []string
|
|
for _, want := range compiled {
|
|
names = append(names, want.Name)
|
|
got, ok := SeatNamed(want.Name)
|
|
if !ok {
|
|
t.Errorf("%s left the generated set", want.Name)
|
|
continue
|
|
}
|
|
g := golden{Seat: got}
|
|
for _, v := range got.Serves {
|
|
if v.Optional {
|
|
g.Optional = append(g.Optional, v.Name)
|
|
}
|
|
}
|
|
for _, r := range got.Receives {
|
|
if r.Shape != nil && (r.Shape.Check != nil || r.Shape.Reads != nil) {
|
|
g.ShapeCode = append(g.ShapeCode, r.Kind)
|
|
}
|
|
}
|
|
g.Serves = slices.DeleteFunc(slices.Clone(g.Serves), func(v Verb) bool { return addedVerbs[want.Name+"."+v.Name] })
|
|
g.Optional = slices.DeleteFunc(g.Optional, func(n string) bool { return addedVerbs[want.Name+"."+n] })
|
|
if slices.Contains(whole, want.Name) {
|
|
want := &want
|
|
byName := map[string]Verb{}
|
|
for _, v := range got.Serves {
|
|
v.Output = nil
|
|
byName[v.Name] = v
|
|
}
|
|
var kept []Verb
|
|
for _, v := range want.Serves {
|
|
if dropped[want.Name+"."+v.Name] {
|
|
if _, still := byName[v.Name]; still {
|
|
t.Errorf("%s still serves %s", want.Name, v.Name)
|
|
}
|
|
continue
|
|
}
|
|
kept = append(kept, byName[v.Name])
|
|
}
|
|
g.Serves, g.Emits, g.Accepts = kept, want.Emits, want.Accepts
|
|
// A verb the whole seat added (new, optional while its holders catch up) is no verb of the compiled list.
|
|
g.Optional = slices.DeleteFunc(g.Optional, func(n string) bool {
|
|
return !slices.ContainsFunc(want.Serves, func(v Verb) bool { return v.Name == n })
|
|
})
|
|
if len(g.Optional) == 0 {
|
|
g.Optional = nil
|
|
}
|
|
want.Optional = slices.DeleteFunc(want.Optional, func(n string) bool { return dropped[want.Name+"."+n] })
|
|
want.Serves = slices.DeleteFunc(slices.Clone(want.Serves), func(v Verb) bool { return dropped[want.Name+"."+v.Name] })
|
|
}
|
|
if !reflect.DeepEqual(norm(g), norm(want)) {
|
|
gb, _ := json.Marshal(g)
|
|
wb, _ := json.Marshal(want)
|
|
t.Errorf("%s is not as the compiled list held it:\n got %s\nwant %s", want.Name, gb, wb)
|
|
}
|
|
}
|
|
var added []string
|
|
for _, s := range DefaultSeats() {
|
|
if !slices.Contains(names, s.Name) {
|
|
added = append(added, s.Name)
|
|
}
|
|
}
|
|
if want := []string{"go-package-registry", "container-image-registry", "oci-artifact-registry"}; !slices.Equal(added, want) {
|
|
t.Errorf("the generated set adds %v, want %v", added, want)
|
|
}
|
|
}
|