The review of 2026-10-09: the router read the absence of an agent-root condition as a pass, and its minute hung on a break-glass file, taking the work it already held. An approval now rests on the controller's verb root-free, asked live for the router's machine and the channel's, failing closed on every other answer, and a stale word on the kinds promises no verified sender. Break-glass runs in the minute, says on every channel what root did, and ends the router's own open questions. An ask offering an approval is answered only where it could be approved; a message says who asks, what each answer does and what expiry does. The router's own asks bind the account they are about; a link is checked against one per kind as it is made; an unlinked account does not come back by a code within a day; a fresh link is unlinked from the desk; no link begins while the desk cannot show its code, and a code is marked secret on every path. Links are bounded per account in the router's state; a warrant's message id cannot be foreseen. Moves to mesh-sdk 76902998 (canonical digests).
142 lines
4.8 KiB
Go
142 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/asks"
|
|
)
|
|
|
|
// The manifest says what the code does (novox/hq ADR 0259): the seats it declares — the operator's, and
|
|
// the two kinded benches its channels hold — the one it holds and the verbs it serves, the bench it sends
|
|
// on, the events it consumes, the one it emits, the tool it calls, its state, and its own tools. It holds
|
|
// no channel's secret, and names nothing of one installation.
|
|
|
|
type manifest struct {
|
|
Module string `json:"module"`
|
|
Seats []seat `json:"seats"`
|
|
Claims []seat `json:"claims"`
|
|
Uses []string
|
|
Consumes []string
|
|
Emits []string
|
|
Invokes []string
|
|
State []json.RawMessage
|
|
Own map[string]string `json:"own-secrets"`
|
|
RunsAs string `json:"runs-as"`
|
|
SecretsOwner string `json:"secrets-owner"`
|
|
Tools []string
|
|
Build struct {
|
|
Artifacts []map[string]any `json:"artifacts"`
|
|
} `json:"build"`
|
|
}
|
|
|
|
type seat struct {
|
|
Name string `json:"name"`
|
|
Scope string `json:"scope"`
|
|
Serves []string `json:"serves"`
|
|
Accepts []string `json:"accepts"`
|
|
Emits []string `json:"emits"`
|
|
Kinded bool `json:"kinded"`
|
|
ByCaller []string `json:"by-caller"`
|
|
Proofs []string `json:"proofs"`
|
|
Records []string `json:"records"`
|
|
}
|
|
|
|
func readManifest(t *testing.T) (manifest, string) {
|
|
t.Helper()
|
|
raw, err := os.ReadFile(filepath.Join("..", "..", "module.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var m manifest
|
|
if err := json.Unmarshal(raw, &m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return m, string(raw)
|
|
}
|
|
|
|
func TestItDeclaresTheSeatsOfAnAskAndHoldsTheOperatorChannel(t *testing.T) {
|
|
m, _ := readManifest(t)
|
|
verbs := []string{"open", "history", "notify", "asks"}
|
|
want := []seat{
|
|
{Name: asks.Seat, Scope: "mesh", Serves: verbs, Accepts: []string{"ask", "cancel"}, Emits: []string{"decided"},
|
|
ByCaller: []string{"ask", "cancel", "decided"}, Records: []string{"asks"}},
|
|
{Name: asks.ChannelSeat, Scope: "mesh", Kinded: true, Accepts: []string{asks.Show, asks.Edit, asks.Send}},
|
|
{Name: asks.IntakeSeat, Scope: "mesh", Kinded: true,
|
|
Emits: []string{asks.Chosen, asks.Link, asks.FailedWhat, asks.StandingWhat}, Proofs: []string{asks.CodeProof}},
|
|
}
|
|
if !reflect.DeepEqual(m.Seats, want) {
|
|
t.Fatalf("seats %+v", m.Seats)
|
|
}
|
|
if len(m.Claims) != 1 || m.Claims[0].Name != asks.Seat || !reflect.DeepEqual(m.Claims[0].Serves, verbs) {
|
|
t.Fatalf("claims %+v", m.Claims)
|
|
}
|
|
if !reflect.DeepEqual(m.Uses, []string{asks.ChannelSeat}) {
|
|
t.Fatalf("uses %v", m.Uses)
|
|
}
|
|
if !reflect.DeepEqual(m.Consumes, []string{
|
|
ControllerSeat + "." + EventRaised, ControllerSeat + "." + EventChanged, ControllerSeat + "." + EventCleared,
|
|
"intake.choice", "intake.link", "intake.failed", "intake.standing"}) {
|
|
t.Fatalf("consumes %v", m.Consumes)
|
|
}
|
|
// It performs nothing: the verbs it calls read the open conditions and whether root is free.
|
|
if !reflect.DeepEqual(m.Emits, []string{"refused"}) || !reflect.DeepEqual(m.Invokes, []string{"seat:" + ControllerSeat + ".conditions",
|
|
"seat:" + ControllerSeat + ".root-free"}) {
|
|
t.Fatalf("emits %v invokes %v", m.Emits, m.Invokes)
|
|
}
|
|
var state []string
|
|
for _, s := range m.State {
|
|
var name string
|
|
if json.Unmarshal(s, &name) != nil {
|
|
var full struct{ Name string }
|
|
_ = json.Unmarshal(s, &full)
|
|
name = full.Name
|
|
}
|
|
state = append(state, name)
|
|
}
|
|
if !reflect.DeepEqual(state, []string{"open", "sent", "asks", "identities"}) {
|
|
t.Fatalf("state %v", state)
|
|
}
|
|
// It holds no channel's secret: only its own bus account, read by its own account alone (ADR 0259 §8).
|
|
if len(m.Own) != 1 || m.Own["broker"] == "" || m.RunsAs != "messenger" || m.SecretsOwner != "messenger" {
|
|
t.Fatalf("own secrets %v, runs as %q, secrets owned by %q", m.Own, m.RunsAs, m.SecretsOwner)
|
|
}
|
|
}
|
|
|
|
func TestTheToolsAgreeWithTheManifest(t *testing.T) {
|
|
m, raw := readManifest(t)
|
|
var own, verbs []string
|
|
for _, tool := range tools(&Holder{}, &listening{}) {
|
|
if strings.TrimSpace(tool.Description) == "" {
|
|
t.Errorf("%s has no description", tool.Name)
|
|
}
|
|
if seatName, verb, ok := strings.Cut(tool.Name, "."); ok {
|
|
if seatName != "operator-channel" {
|
|
t.Errorf("%s is a verb of a seat this does not hold", tool.Name)
|
|
}
|
|
verbs = append(verbs, verb)
|
|
continue
|
|
}
|
|
own = append(own, tool.Name)
|
|
}
|
|
listed := append([]string(nil), m.Tools...)
|
|
sort.Strings(own)
|
|
sort.Strings(listed)
|
|
if !reflect.DeepEqual(own, listed) {
|
|
t.Fatalf("serves %v, lists %v", own, listed)
|
|
}
|
|
if !reflect.DeepEqual(verbs, m.Claims[0].Serves) {
|
|
t.Fatalf("verbs %v, claim %v", verbs, m.Claims[0].Serves)
|
|
}
|
|
for _, never := range []string{"/home/", "jochen", "g14", "shanks", "novox", "zurag", "telegram"} {
|
|
if strings.Contains(strings.ToLower(raw), never) {
|
|
t.Errorf("module.json names %q", never)
|
|
}
|
|
}
|
|
}
|