catalogue's compiled set is read from git.novox.be/novox/mesh-contracts/go/seats; the hand-written seat literal and its
verb builders go. The bar's block keeps its checks as code beside the data. A case of the seat set's test holds the
generated set equal, entry for entry, to the list compiled at 5a38a12 (testdata), but for the whole seats' additions
and the vault's two verbs; the three new registry seats are the only new entries. A holder of mesh-broker now serves its
eight verbs.
SDK-12: the vault's seat no longer promises accept and move-asks, which no holder served and nothing called; retire is
optional (design 32). The intake and move rows join one table; the move's own test goes with the two verbs.
Requires mesh-contracts (no version on the forge yet: a go.work, uncommitted, uses the sibling worktree until the
group pins it).
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
122 lines
4.2 KiB
Go
122 lines
4.2 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"}
|
|
dropped := map[string]bool{"mesh-vault.accept": true, "mesh-vault.move-asks": 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)
|
|
}
|
|
}
|
|
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
|
|
g.Optional = slices.DeleteFunc(g.Optional, func(n string) bool { return n == "renames" || n == "retire" })
|
|
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)
|
|
}
|
|
}
|