mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery-group group feat/asks-answered-on-any-channel rejected: a member's own check failed
mesh/delivery superseded: a newer head of the same pull request
162 lines
7.6 KiB
Go
162 lines
7.6 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// The router of novox/hq ADR 0259: it declares the operator's seat and the two kinded benches.
|
|
func router() Manifest {
|
|
return Manifest{Module: "messenger", Tools: []string{"open", "history", "notify"},
|
|
State: []StateDeclaration{{Name: "asks"}}, RunsAs: "messenger", SecretsOwner: "messenger",
|
|
DefinesSeats: []SeatDeclaration{
|
|
{Name: "operator-channel", Scope: ScopeMesh, Accepts: []string{"ask", "cancel"}, Emits: []string{"decided"},
|
|
ByCaller: []string{"ask", "cancel", "decided"}, Records: []string{"asks"},
|
|
Serves: []Verb{{Name: "open"}, {Name: "history"}, {Name: "notify"}}},
|
|
{Name: "channel", Scope: ScopeMesh, Kinded: true, Accepts: []string{"show", "edit", "send"}},
|
|
{Name: "intake", Scope: ScopeMesh, Kinded: true, Emits: []string{"choice", "link"}, Proofs: []string{"code"}},
|
|
},
|
|
Claims: []Claim{{Name: "operator-channel", Scope: ScopeMesh}},
|
|
Uses: []string{"channel"}}
|
|
}
|
|
|
|
func aChannel(module, kind string) Manifest {
|
|
return Manifest{Module: module, Claims: []Claim{
|
|
{Name: "channel", Scope: ScopeMesh, Kind: kind}, {Name: "intake", Scope: ScopeMesh, Kind: kind}}}
|
|
}
|
|
|
|
func TestTwoChannelsOfDifferentKindsHoldTheBenches(t *testing.T) {
|
|
shelf := Shelf{"messenger": router(), "telegram": aChannel("telegram", "telegram"),
|
|
"desk-channel": aChannel("desk-channel", "desktop")}
|
|
if got := problemsFor(t, shelf); got != "" {
|
|
t.Fatalf("two kinds were refused: %s", got)
|
|
}
|
|
for _, m := range shelf {
|
|
if got := declaredSeatProblems(m); len(got) > 0 {
|
|
t.Fatalf("%s: %v", m.Module, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestASecondClaimOfOneKindIsRefused(t *testing.T) {
|
|
got := problemsFor(t, Shelf{"messenger": router(), "telegram": aChannel("telegram", "telegram"),
|
|
"telegram-two": aChannel("telegram-two", "telegram")})
|
|
if !strings.Contains(got, `of kind "telegram", which telegram already claims`) {
|
|
t.Fatalf("a second holder of one kind stood: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestAKindedBenchNeedsAKindAndNoOtherSeatTakesOne(t *testing.T) {
|
|
got := problemsFor(t, Shelf{"messenger": router(), "nameless": aChannel("nameless", "")})
|
|
if !strings.Contains(got, "claims the kinded bench channel and names no kind") {
|
|
t.Fatalf("a claim without a kind stood: %s", got)
|
|
}
|
|
odd := Manifest{Module: "odd", Claims: []Claim{{Name: "operator-channel", Scope: ScopeMesh, Kind: "telegram"}}}
|
|
got = problemsFor(t, Shelf{"messenger": router(), "odd": odd})
|
|
if !strings.Contains(got, "only a kinded bench takes a kind") {
|
|
t.Fatalf("a kind on a seat that is not kinded stood: %s", got)
|
|
}
|
|
dotted := problemsFor(t, Shelf{"messenger": router(), "dotted": aChannel("dotted", "a.b")})
|
|
if !strings.Contains(dotted, "not a usable name") {
|
|
t.Fatalf("a kind that would widen a subject stood: %s", dotted)
|
|
}
|
|
}
|
|
|
|
func TestOnlyChannelAndIntakeAreKinded(t *testing.T) {
|
|
m := Manifest{Module: "x", DefinesSeats: []SeatDeclaration{{Name: "pager", Kinded: true, Accepts: []string{"page"}}}}
|
|
if got := strings.Join(declaredSeatProblems(m), "; "); !strings.Contains(got, "only channel and intake are kinded") {
|
|
t.Fatalf("another kinded bench was declared: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestTheNewRulesAreHeldToWhatTheSeatSays(t *testing.T) {
|
|
m := Manifest{Module: "x", DefinesSeats: []SeatDeclaration{{Name: "thing", Accepts: []string{"do"},
|
|
ByCaller: []string{"undo"}, Proofs: []string{"code"}, Records: []string{"nothing"}}}}
|
|
got := strings.Join(declaredSeatProblems(m), "; ")
|
|
for _, want := range []string{"names thing.undo by its caller, which the seat neither accepts nor emits",
|
|
"declares proofs on thing, which is not kinded", `read its records "nothing", which it keeps no state of`} {
|
|
if !strings.Contains(got, want) {
|
|
t.Errorf("not refused: %q in %s", want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Two kinds on one machine are two holders, and one kind on two machines is a second claimant.
|
|
func TestEachKindIsItsOwnHolderWhenResolved(t *testing.T) {
|
|
modules := []Manifest{aChannel("telegram", "telegram"), aChannel("desk-channel", "desktop")}
|
|
held, problems := checkClaims(modules, Node{Name: "anchor"}, nil, nil)
|
|
if len(problems) > 0 || len(held) != 4 {
|
|
t.Fatalf("two kinds on one machine: held %v, problems %v", held, problems)
|
|
}
|
|
_, problems = checkClaims([]Manifest{aChannel("telegram", "telegram")}, Node{Name: "home"}, held, nil)
|
|
if len(problems) == 0 {
|
|
t.Fatal("one kind held on two machines was not refused")
|
|
}
|
|
}
|
|
|
|
// A channel's capabilities come from the fixed vocabulary, and only a kinded claim carries any.
|
|
func TestCapabilitiesAreTheVocabularysAndOnlyOnAKindedClaim(t *testing.T) {
|
|
good := aChannel("telegram", "telegram")
|
|
good.Claims[0].Capabilities = []string{"deliver", "choice", "verified-sender", "max-length:4096"}
|
|
good.RunsAs = "telegram"
|
|
if got := problemsFor(t, Shelf{"messenger": router(), "telegram": good}); got != "" {
|
|
t.Fatalf("the vocabulary was refused: %s", got)
|
|
}
|
|
bad := aChannel("telegram", "telegram")
|
|
bad.Claims[0].Capabilities = []string{"trusted", "max-length:lots"}
|
|
got := problemsFor(t, Shelf{"messenger": router(), "telegram": bad})
|
|
for _, w := range []string{`"trusted"`, `"max-length:lots"`} {
|
|
if !strings.Contains(got, w+", which channel-capabilities/1 does not have") {
|
|
t.Errorf("%s was not refused: %s", w, got)
|
|
}
|
|
}
|
|
odd := Manifest{Module: "odd", Claims: []Claim{{Name: "operator-channel", Scope: ScopeMesh, Capabilities: []string{"deliver"}}}}
|
|
if got := problemsFor(t, Shelf{"messenger": router(), "odd": odd}); !strings.Contains(got, "only a kinded bench's claim carries them") {
|
|
t.Errorf("capabilities on a seat that is not kinded stood: %s", got)
|
|
}
|
|
}
|
|
|
|
// novox/hq ADR 0259 §8: a module saying warrants, or speaking for a kind that proves its sender, runs as an
|
|
// account of its own — never carried by the machine's runtime, which runs as the operator's account.
|
|
func TestATrustedHolderMustRunAsAnAccountOfItsOwn(t *testing.T) {
|
|
r := router()
|
|
r.RunsAs = ""
|
|
if got := problemsFor(t, Shelf{"messenger": r}); !strings.Contains(got, "messenger must run as an account of its own") {
|
|
t.Errorf("a router on the machine's runtime stood: %s", got)
|
|
}
|
|
tg := aChannel("telegram", "telegram")
|
|
tg.Claims[0].Capabilities = []string{"choice", "verified-sender"}
|
|
if got := problemsFor(t, Shelf{"messenger": router(), "telegram": tg}); !strings.Contains(got, "telegram must run as an account of its own") {
|
|
t.Errorf("a verified channel on the machine's runtime stood: %s", got)
|
|
}
|
|
desk := aChannel("desk-channel", "desktop")
|
|
desk.Claims[0].Capabilities = []string{"choice"}
|
|
if got := problemsFor(t, Shelf{"messenger": router(), "desk-channel": desk}); got != "" {
|
|
t.Errorf("a channel proving nothing was held to it: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestRunsAsIsAnAccountOfTheModulesOwn(t *testing.T) {
|
|
ok := Manifest{Module: "telegram", RunsAs: "telegram", SecretsOwner: "telegram",
|
|
OwnSecrets: OwnSecrets{"broker": {Path: "/var/lib/telegram/broker"}},
|
|
Resources: []map[string]any{{"id": "account", "type": "user", "name": "telegram"}}}
|
|
if got := RunsAsProblems(ok); len(got) != 0 {
|
|
t.Fatalf("a sound runs-as was refused: %v", got)
|
|
}
|
|
for want, change := range map[string]func(*Manifest){
|
|
"never root": func(m *Manifest) { m.RunsAs, m.SecretsOwner = "root", "root" },
|
|
"not an account name": func(m *Manifest) { m.RunsAs = "${machine:account}" },
|
|
"which it does not make": func(m *Manifest) { m.Resources = nil },
|
|
"declares no own secret": func(m *Manifest) { m.OwnSecrets = nil },
|
|
"they are the account's own": func(m *Manifest) { m.SecretsOwner = "" },
|
|
} {
|
|
m := ok
|
|
m.Resources = append([]map[string]any(nil), ok.Resources...)
|
|
m.OwnSecrets = OwnSecrets{"broker": {Path: "/x"}}
|
|
change(&m)
|
|
if got := strings.Join(RunsAsProblems(m), "; "); !strings.Contains(got, want) {
|
|
t.Errorf("want %q, got %q", want, got)
|
|
}
|
|
}
|
|
}
|