A seat's protocol lives in the store (migration 0047; seeded additively), a served verb carries its description and schema, holding a mesh seat requires serving its verbs, a node-scoped seat's tool carries the node, and the control plane serves status, nodes, node, modules, seats, builds, plan, assign, unassign, push, build and tools on its seat by running the same commands (novox/hq ADR 0132, ADR 0154, design 33). A grant of * reaches a role's tools; seat:<seat>.<verb> grants one.
73 lines
2.9 KiB
Go
73 lines
2.9 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"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 := ParseManifest([]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)
|
|
}
|
|
}
|
|
|
|
// A misspelt key inside a verb's definition is refused, like one anywhere else in the manifest.
|
|
func TestAVerbWithAnUnknownKeyIsRefused(t *testing.T) {
|
|
_, err := ParseManifest([]byte(`{"module":"till","version":"1",` +
|
|
`"seats":[{"name":"shop-till","serves":[{"name":"price","descripton":"typo"}]}]}`))
|
|
if err == nil || !strings.Contains(err.Error(), "descripton") {
|
|
t.Fatalf("a verb with a misspelt key was accepted: %v", err)
|
|
}
|
|
}
|
|
|
|
// Holding a mesh seat that serves verbs requires serving them, and the refusal names the verbs.
|
|
func TestHoldingAMeshSeatRequiresServingItsVerbs(t *testing.T) {
|
|
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))
|
|
}
|
|
}
|