The operator's decision of 2026-11-02 01:05: issue requires overlay_key, the machine's tunnel public key; the vault's enrolment-token tunnel declares all five fields a token needs (hub_key, hub_endpoint, address, key, range). An add-node is proposed at the yes with the tunnel key alone; the identity keys come whole at redeem or not at all. A new remove-node, mesh-enrolment's alone on the same warrant and the proposal's one change, takes a joining node running nothing off placement when its enrolment ends unconfirmed (RemovedNodes names the key the holder deletes; LayChanges lays nothing for it and refuses a node that runs modules or holds seats). NodeLogin gains Acts, the hand acts that warranted its rotations. Tested as rows of TestPlacement, TestNodeRecord and the two seat tests; no new test function (1438 unit tests). (issue 774, review round 1b) Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
89 lines
3.7 KiB
Go
89 lines
3.7 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// A served verb is written as a bare name or in full, and both read into one type (novox/hq ADR 0132).
|
|
func TestAServedVerbIsANameOrADefinition(t *testing.T) {
|
|
m, err := decodeForTest([]byte(`{"module":"till","version":"1","tools":["price","refund"],` +
|
|
`"seats":[{"name":"shop-till","serves":["price",{"name":"refund","description":"give it back",` +
|
|
`"input":{"type":"object","properties":{"order":{"type":"string"}}}}]}],` +
|
|
`"claims":[{"name":"shop-till","scope":"mesh"}]}`))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := m.DefinesSeats[0].Serves
|
|
if len(got) != 2 || got[0].Name != "price" || got[1].Name != "refund" || got[1].Description != "give it back" {
|
|
t.Fatalf("verbs not read: %+v", got)
|
|
}
|
|
if got[1].Input["type"] != "object" {
|
|
t.Fatalf("the schema did not travel with the verb: %+v", got[1].Input)
|
|
}
|
|
}
|
|
|
|
// Holding a mesh seat that serves verbs requires serving them, and the refusal names the verbs.
|
|
func TestHoldingAMeshSeatRequiresServingItsVerbs(t *testing.T) {
|
|
// 774/S34/AC1: the seat mesh-enrolment is held by a module serving its six verbs, and a seventh is refused.
|
|
enrolment, ok := SeatNamed(EnrolmentSeat)
|
|
if !ok || enrolment.Scope != ScopeMesh || len(enrolment.Emits) != 5 {
|
|
t.Fatalf("mesh-enrolment: in the set %v, %+v", ok, enrolment)
|
|
}
|
|
six := []string{"issue", "redeem", "confirm", "revoke", "tokens", "again"}
|
|
// The tunnel key is given at issue (the operator, 2026-11-02 01:05).
|
|
if issue := enrolment.Serves[0]; issue.Name != "issue" || !slices.Contains(issue.Input["required"].([]string), "overlay_key") {
|
|
t.Errorf("issue requires %v", issue.Input["required"])
|
|
}
|
|
holder := func(serves ...string) Manifest {
|
|
return Manifest{Module: "mesh-enrolment", Tools: serves,
|
|
Claims: []Claim{{Name: EnrolmentSeat, Scope: ScopeMesh, Serves: serves}}}
|
|
}
|
|
if err := CanHold(holder(six...), enrolment); err != nil {
|
|
t.Errorf("the holder serving the six verbs was refused: %v", err)
|
|
}
|
|
if err := CanHold(holder(six[:5]...), enrolment); err == nil || !strings.Contains(err.Error(), "does not serve again") {
|
|
t.Errorf("a holder missing again: %v", err)
|
|
}
|
|
if err := CanHold(holder(append(six, "mint")...), enrolment); err == nil || !strings.Contains(err.Error(), "mint") {
|
|
t.Errorf("a holder saying it serves mint: %v", err)
|
|
}
|
|
|
|
was := Seats()
|
|
t.Cleanup(func() { UseSeats(was) })
|
|
UseSeats([]Seat{{Name: "mesh-controller", Scope: ScopeMesh, Decision: "test",
|
|
Serves: []Verb{{Name: "status"}, {Name: "push"}}}})
|
|
seat, _ := SeatNamed("mesh-controller")
|
|
|
|
partial := Manifest{Module: "a-controller", Tools: []string{"status"},
|
|
Claims: []Claim{{Name: "mesh-controller", Scope: ScopeMesh}}}
|
|
err := CanHold(partial, seat)
|
|
if err == nil || !strings.Contains(err.Error(), "does not serve push") {
|
|
t.Fatalf("a holder missing a verb was not refused by name: %v", err)
|
|
}
|
|
whole := Manifest{Module: "a-controller", Tools: []string{"status", "push"},
|
|
Claims: []Claim{{Name: "mesh-controller", Scope: ScopeMesh}}}
|
|
if err := CanHold(whole, seat); err != nil {
|
|
t.Fatalf("a holder serving every verb was refused: %v", err)
|
|
}
|
|
}
|
|
|
|
// The mesh's own verbs are declared in full: an agent cannot call a name without a schema.
|
|
func TestEveryControllerVerbIsDescribedWithASchema(t *testing.T) {
|
|
seen := map[string]bool{}
|
|
for _, v := range ControllerVerbs {
|
|
if v.Description == "" || v.Input == nil || v.Input["type"] != "object" {
|
|
t.Errorf("%s: no description or no object schema", v.Name)
|
|
}
|
|
if seen[v.Name] {
|
|
t.Errorf("%s declared twice", v.Name)
|
|
}
|
|
seen[v.Name] = true
|
|
}
|
|
seat, _ := SeatNamed(ControllerSeatName)
|
|
if len(seat.Serves) != len(ControllerVerbs) {
|
|
t.Fatalf("the compiled mesh-controller seat serves %d verbs, the table has %d", len(seat.Serves), len(ControllerVerbs))
|
|
}
|
|
}
|