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
341 lines
14 KiB
Go
341 lines
14 KiB
Go
package broker
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// The seats of novox/hq ADR 0259 §3, as the messenger declares them.
|
|
func operatorChannel() Seat {
|
|
return Seat{Name: "operator-channel", Scope: "mesh", Accepts: []string{"ask", "cancel"},
|
|
Emits: []string{"decided"}, Serves: []string{"open", "history", "notify"},
|
|
ByCaller: []string{"ask", "cancel", "decided"}, Records: []string{"messenger_asks"}}
|
|
}
|
|
|
|
func channelSeat(kind string) Seat {
|
|
return Seat{Name: "channel", Scope: "mesh", Accepts: []string{"show", "edit", "send"}, Kinded: true, Kind: kind,
|
|
DeclaredBy: "messenger"}
|
|
}
|
|
|
|
func intakeSeat(kind string) Seat {
|
|
return Seat{Name: "intake", Scope: "mesh", Emits: []string{"choice", "link"}, Proofs: []string{"code"},
|
|
Kinded: true, Kind: kind, DeclaredBy: "messenger"}
|
|
}
|
|
|
|
func allowed(patterns []string, subject string) bool {
|
|
for _, p := range patterns {
|
|
if subjectMatches(p, subject) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func perms(t *testing.T, p Principal) Permissions {
|
|
t.Helper()
|
|
got, err := PermissionsFor(p)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return got
|
|
}
|
|
|
|
func TestAnAskerAsksAndHearsUnderItsOwnNameOnly(t *testing.T) {
|
|
asker := Principal{Kind: KindModule, Node: "anchor", Module: "mesh-delivery", Uses: []Seat{operatorChannel()}}
|
|
got := perms(t, asker)
|
|
for _, s := range []string{
|
|
"mesh.seat.operator-channel.accept.ask.mesh-delivery",
|
|
"mesh.seat.operator-channel.accept.cancel.mesh-delivery",
|
|
"$JS.API.DIRECT.GET.KV_messenger_asks.$KV.messenger_asks.mesh-delivery.a1",
|
|
} {
|
|
if !allowed(got.Publish, s) {
|
|
t.Errorf("an asker may not publish %s", s)
|
|
}
|
|
}
|
|
for _, s := range []string{
|
|
"mesh.seat.operator-channel.accept.ask.mesh-controller",
|
|
"mesh.seat.operator-channel.accept.ask.*",
|
|
"mesh.seat.operator-channel.event.decided.mesh-delivery",
|
|
"$JS.API.DIRECT.GET.KV_messenger_asks.$KV.messenger_asks.mesh-controller.a1",
|
|
"$KV.messenger_asks.mesh-delivery.a1",
|
|
} {
|
|
if allowed(got.Publish, s) {
|
|
t.Errorf("an asker may publish %s, which is not its own to submit", s)
|
|
}
|
|
}
|
|
if !allowed(got.Subscribe, "mesh.seat.operator-channel.event.decided.mesh-delivery") {
|
|
t.Error("an asker does not hear its own warrants")
|
|
}
|
|
if allowed(got.Subscribe, "mesh.seat.operator-channel.event.decided.mesh-controller") {
|
|
t.Error("an asker hears another asker's warrants")
|
|
}
|
|
// And its own consumer carries its warrants, so a restart catches up.
|
|
c, ok := ConsumerFor(asker)
|
|
if !ok || !allowed(c.Filters, "mesh.seat.operator-channel.event.decided.mesh-delivery") {
|
|
t.Errorf("the asker's consumer does not carry its warrants: %v", c.Filters)
|
|
}
|
|
}
|
|
|
|
func TestOnlyTheHolderPublishesAWarrant(t *testing.T) {
|
|
users, err := Users(Records{
|
|
Nodes: []string{"anchor"},
|
|
Assigned: map[string][]Declared{"anchor": {
|
|
{Module: "messenger", Holds: []Seat{operatorChannel()}, Uses: []Seat{channelSeat("")},
|
|
Watches: []Seat{{Name: "intake", Emits: []string{"choice", "link"}, Kinded: true, Proofs: []string{"code"}, DeclaredBy: "messenger"}}},
|
|
{Module: "mesh-delivery", Uses: []Seat{operatorChannel()}},
|
|
{Module: "telegram", Holds: []Seat{channelSeat("telegram"), intakeSeat("telegram")}},
|
|
}},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, u := range users {
|
|
got := perms(t, u)
|
|
says := allowed(got.Publish, "mesh.seat.operator-channel.event.decided.mesh-delivery")
|
|
if says != (u.Module == "messenger") {
|
|
t.Errorf("%s %s publish a warrant", u.Username(), map[bool]string{true: "may", false: "may not"}[says])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTheHolderTakesEveryCallersAskThroughItsWorker(t *testing.T) {
|
|
got := perms(t, Principal{Kind: KindModule, Node: "anchor", Module: "messenger", Holds: []Seat{operatorChannel()}})
|
|
if !allowed(got.Subscribe, "mesh.seat.operator-channel.accept.ask.mesh-delivery") {
|
|
t.Error("the router does not take an ask")
|
|
}
|
|
for _, s := range []string{
|
|
"$JS.API.CONSUMER.MSG.NEXT.SEAT_OPERATOR_CHANNEL.SEAT_OPERATOR_CHANNEL_worker",
|
|
"$JS.ACK.SEAT_OPERATOR_CHANNEL.SEAT_OPERATOR_CHANNEL_worker.x",
|
|
"mesh.seat.operator-channel.event.decided.mesh-controller",
|
|
} {
|
|
if !allowed(got.Publish, s) {
|
|
t.Errorf("the router may not publish %s", s)
|
|
}
|
|
}
|
|
if allowed(got.Publish, "mesh.seat.operator-channel.accept.ask.messenger") {
|
|
t.Error("the holder may ask its own seat without using it")
|
|
}
|
|
}
|
|
|
|
func TestAKindedHolderReachesItsOwnKindAndNoOther(t *testing.T) {
|
|
got := perms(t, Principal{Kind: KindModule, Node: "anchor", Module: "telegram",
|
|
Holds: []Seat{channelSeat("telegram"), intakeSeat("telegram")}})
|
|
for _, s := range []string{
|
|
"mesh.seat.intake.event.choice.telegram", "mesh.seat.intake.event.link.telegram",
|
|
"mesh.seat.intake.proof.code.telegram",
|
|
"$JS.API.CONSUMER.MSG.NEXT.SEAT_CHANNEL.SEAT_CHANNEL_TELEGRAM_worker",
|
|
} {
|
|
if !allowed(got.Publish, s) {
|
|
t.Errorf("telegram may not publish %s", s)
|
|
}
|
|
}
|
|
for _, s := range []string{
|
|
"mesh.seat.intake.event.choice.desktop", "mesh.seat.intake.proof.code.desktop",
|
|
"mesh.seat.channel.accept.show.telegram",
|
|
"$JS.API.CONSUMER.MSG.NEXT.SEAT_CHANNEL.SEAT_CHANNEL_DESKTOP_worker",
|
|
"mesh.seat.operator-channel.event.decided.mesh-delivery",
|
|
} {
|
|
if allowed(got.Publish, s) {
|
|
t.Errorf("telegram may publish %s", s)
|
|
}
|
|
}
|
|
if !allowed(got.Subscribe, "mesh.seat.channel.accept.show.telegram") ||
|
|
allowed(got.Subscribe, "mesh.seat.channel.accept.show.desktop") {
|
|
t.Error("telegram does not take exactly its own kind's work")
|
|
}
|
|
if allowed(got.Subscribe, "mesh.seat.intake.proof.code.telegram") {
|
|
t.Error("a channel answers its own proofs")
|
|
}
|
|
}
|
|
|
|
func TestTheWatcherAnswersProofsAndHearsEveryKind(t *testing.T) {
|
|
got := perms(t, Principal{Kind: KindModule, Node: "anchor", Module: "messenger",
|
|
Uses: []Seat{channelSeat("")},
|
|
Watches: []Seat{{Name: "intake", Emits: []string{"choice"}, Kinded: true, Proofs: []string{"code"}, DeclaredBy: "messenger"}}})
|
|
for _, s := range []string{"mesh.seat.intake.event.choice.telegram", "mesh.seat.intake.proof.code.desktop"} {
|
|
if !allowed(got.Subscribe, s) {
|
|
t.Errorf("the router does not hear %s", s)
|
|
}
|
|
}
|
|
if !allowed(got.Publish, "mesh.seat.channel.accept.show.telegram") {
|
|
t.Error("the router cannot send a channel its work")
|
|
}
|
|
if allowed(got.Publish, "mesh.seat.intake.event.choice.telegram") || allowed(got.Publish, "mesh.seat.intake.proof.code.telegram") {
|
|
t.Error("the router may say a channel's answer or proof")
|
|
}
|
|
}
|
|
|
|
func TestNoStreamKeepsAProof(t *testing.T) {
|
|
users, _ := Users(Records{Nodes: []string{"anchor"}, Assigned: map[string][]Declared{"anchor": {
|
|
{Module: "telegram", Holds: []Seat{channelSeat("telegram"), intakeSeat("telegram")}},
|
|
{Module: "messenger", Holds: []Seat{operatorChannel()}},
|
|
}}})
|
|
streams, _ := SeatTrafficObjects(users)
|
|
streams = append(streams, MeshStreams()...)
|
|
for _, s := range streams {
|
|
for _, subject := range s.Subjects {
|
|
if subjectMatches(subject, "mesh.seat.intake.proof.code.telegram") {
|
|
t.Errorf("%s keeps a proof (%s)", s.Name, subject)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEachKindHasAWorkerOfItsOwn(t *testing.T) {
|
|
users, _ := Users(Records{Nodes: []string{"anchor"}, Assigned: map[string][]Declared{"anchor": {
|
|
{Module: "telegram", Holds: []Seat{channelSeat("telegram")}},
|
|
{Module: "desk-channel", Holds: []Seat{channelSeat("desktop")}},
|
|
{Module: "messenger", Holds: []Seat{operatorChannel()}},
|
|
}}})
|
|
streams, workers := SeatTrafficObjects(users)
|
|
names := map[string]string{}
|
|
for _, w := range workers {
|
|
names[w.Name] = strings.Join(w.Filters, ",")
|
|
}
|
|
want := map[string]string{
|
|
"SEAT_CHANNEL_TELEGRAM_worker": "mesh.seat.channel.accept.*.telegram",
|
|
"SEAT_CHANNEL_DESKTOP_worker": "mesh.seat.channel.accept.*.desktop",
|
|
"SEAT_OPERATOR_CHANNEL_worker": "mesh.seat.operator-channel.accept.>",
|
|
}
|
|
for n, f := range want {
|
|
if names[n] != f {
|
|
t.Errorf("worker %s filters %q, want %q", n, names[n], f)
|
|
}
|
|
}
|
|
if len(streams) != 2 {
|
|
t.Errorf("want the queues of channel and operator-channel, got %v", streams)
|
|
}
|
|
}
|
|
|
|
func TestTheRuntimeIsGrantedTheUnionAndTheMembershipEachModulesShare(t *testing.T) {
|
|
telegram := Declared{Module: "telegram", Holds: []Seat{channelSeat("telegram"), intakeSeat("telegram")}}
|
|
desk := Declared{Module: "desk-channel", Holds: []Seat{channelSeat("desktop"), intakeSeat("desktop")}}
|
|
got := perms(t, Principal{Kind: KindNodeTools, Node: "anchor", Module: RuntimeModule, Carries: []Declared{telegram, desk}})
|
|
for _, s := range []string{"mesh.seat.intake.event.choice.telegram", "mesh.seat.intake.event.choice.desktop"} {
|
|
if !allowed(got.Publish, s) {
|
|
t.Errorf("the runtime may not publish %s for a module it carries", s)
|
|
}
|
|
}
|
|
m := MembershipFor("anchor", telegram, Placements{})
|
|
if m.SeatTraffic == nil || !allowed(m.SeatTraffic.Publish, "mesh.seat.intake.event.choice.telegram") ||
|
|
allowed(m.SeatTraffic.Publish, "mesh.seat.intake.event.choice.desktop") {
|
|
t.Errorf("telegram's membership does not list exactly its own kind: %+v", m.SeatTraffic)
|
|
}
|
|
if plain := MembershipFor("anchor", Declared{Module: "plain"}, Placements{}); plain.SeatTraffic != nil {
|
|
t.Error("a module with no such seat is given seat traffic")
|
|
}
|
|
}
|
|
|
|
func TestASeatWithoutTheNewRulesIsComposedAsBefore(t *testing.T) {
|
|
old := Seat{Name: "node-build-agent", Scope: "node", Accepts: []string{"build"}, Emits: []string{"built"}}
|
|
got := perms(t, Principal{Kind: KindModule, Node: "anchor", Module: "builder", Holds: []Seat{old}})
|
|
for _, s := range []string{"mesh.seat.node-build-agent.event.built",
|
|
"$JS.API.CONSUMER.MSG.NEXT.SEAT_NODE_BUILD_AGENT.SEAT_NODE_BUILD_AGENT_worker"} {
|
|
if !allowed(got.Publish, s) {
|
|
t.Errorf("an old seat's holder lost %s", s)
|
|
}
|
|
}
|
|
if !allowed(got.Subscribe, "mesh.seat.node-build-agent.accept.build") {
|
|
t.Error("an old seat's holder lost its accept")
|
|
}
|
|
}
|
|
|
|
// The router learns which channel is which, and what each promises, from the controller's membership:
|
|
// the claims, never a channel's word (ADR 0259 §5).
|
|
func TestTheRoutersMembershipNamesEveryKindAndItsCapabilities(t *testing.T) {
|
|
tg := channelSeat("telegram")
|
|
tg.Capabilities = []string{"choice", "verified-sender"}
|
|
desk := channelSeat("desktop")
|
|
desk.Capabilities = []string{"choice"}
|
|
router := Declared{Module: "messenger", Holds: []Seat{operatorChannel()}, Uses: []Seat{channelSeat("")}}
|
|
records := Records{Nodes: []string{"anchor", "laptop"}, Assigned: map[string][]Declared{
|
|
"anchor": {router, {Module: "telegram", Holds: []Seat{tg}, RunsAs: "telegram"}},
|
|
"laptop": {{Module: "desk-channel", Holds: []Seat{desk}}},
|
|
}}
|
|
where := PlacementsOf(records, nil)
|
|
m := MembershipFor("anchor", router, where)
|
|
if m.SeatTraffic == nil || len(m.SeatTraffic.Kinds) != 2 {
|
|
t.Fatalf("the router is not told the kinds: %+v", m.SeatTraffic)
|
|
}
|
|
byKind := map[string]KindHeld{}
|
|
for _, k := range m.SeatTraffic.Kinds {
|
|
byKind[k.Kind] = k
|
|
}
|
|
if k := byKind["telegram"]; k.Module != "telegram" || k.Node != "anchor" || !namesVerb(k.Capabilities, "verified-sender") {
|
|
t.Errorf("telegram is %+v", k)
|
|
}
|
|
if k := byKind["desktop"]; k.Module != "desk-channel" || namesVerb(k.Capabilities, "verified-sender") {
|
|
t.Errorf("the desk is %+v", k)
|
|
}
|
|
if other := MembershipFor("anchor", Declared{Module: "mesh-delivery", Uses: []Seat{operatorChannel()}}, where); other.SeatTraffic != nil && len(other.SeatTraffic.Kinds) > 0 {
|
|
t.Error("an asker is told the channels")
|
|
}
|
|
}
|
|
|
|
// novox/hq ADR 0259 §8: only the bench's own router answers its proofs and puts work on a kind's queue.
|
|
func TestOnlyTheBenchsRouterAnswersProofsAndSubmitsWork(t *testing.T) {
|
|
other := perms(t, Principal{Kind: KindModule, Node: "anchor", Module: "eavesdropper",
|
|
Uses: []Seat{channelSeat("")},
|
|
Watches: []Seat{{Name: "intake", Emits: []string{"choice"}, Kinded: true, Proofs: []string{"code"}, DeclaredBy: "messenger"}}})
|
|
if allowed(other.Subscribe, "mesh.seat.intake.proof.code.telegram") {
|
|
t.Error("a watcher that is not the router answers codes")
|
|
}
|
|
if allowed(other.Publish, "mesh.seat.channel.accept.show.telegram") {
|
|
t.Error("a user that is not the router puts work on a kind's queue")
|
|
}
|
|
if !allowed(other.Subscribe, "mesh.seat.intake.event.choice.telegram") {
|
|
t.Error("a watcher no longer hears the bench's events")
|
|
}
|
|
}
|
|
|
|
// novox/hq ADR 0259 §8: the machine's runtime runs as the operator's account; it never carries a module
|
|
// that says warrants or speaks for a kind proving its sender, and such a module has its own account.
|
|
func TestTheMachinesRuntimeNeverCarriesATrustedHolder(t *testing.T) {
|
|
tg := channelSeat("telegram")
|
|
tg.Capabilities = []string{"choice", "verified-sender"}
|
|
for name, d := range map[string]Declared{
|
|
"the router": {Module: "messenger", Holds: []Seat{operatorChannel()}},
|
|
"a verified channel": {Module: "telegram", Holds: []Seat{tg}},
|
|
} {
|
|
if _, err := PermissionsFor(Principal{Kind: KindNodeTools, Node: "anchor", Module: RuntimeModule,
|
|
Carries: []Declared{d}}); err == nil || !strings.Contains(err.Error(), "an account of its own") {
|
|
t.Errorf("%s was composed into the machine's runtime: %v", name, err)
|
|
}
|
|
}
|
|
desk := channelSeat("desktop")
|
|
desk.Capabilities = []string{"choice"}
|
|
if _, err := PermissionsFor(Principal{Kind: KindNodeTools, Node: "anchor", Module: RuntimeModule,
|
|
Carries: []Declared{{Module: "desk-channel", Holds: []Seat{desk}}}}); err != nil {
|
|
t.Errorf("a channel proving nothing was refused: %v", err)
|
|
}
|
|
users, err := Users(Records{Nodes: []string{"anchor"}, Assigned: map[string][]Declared{"anchor": {
|
|
{Module: RuntimeModule}, {Module: "telegram", Holds: []Seat{tg}, RunsAs: "telegram"},
|
|
{Module: "messenger", Holds: []Seat{operatorChannel()}, RunsAs: "messenger"},
|
|
}}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, u := range users {
|
|
if u.Kind == KindNodeTools {
|
|
for _, d := range u.Carries {
|
|
if d.RunsAs != "" {
|
|
t.Errorf("the machine's runtime carries %s", d.Module)
|
|
}
|
|
}
|
|
if _, err := PermissionsFor(u); err != nil {
|
|
t.Errorf("the runtime could not be composed: %v", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// novox/hq ADR 0259 §8: verified-sender reaches the router only from a holder of its own account.
|
|
func TestVerifiedSenderIsBelievedOnlyFromAHolderOfItsOwnAccount(t *testing.T) {
|
|
if got := placedCapabilities([]string{"choice", "verified-sender"}, ""); namesVerb(got, "verified-sender") {
|
|
t.Errorf("a carried holder keeps verified-sender: %v", got)
|
|
}
|
|
if got := placedCapabilities([]string{"choice", "verified-sender"}, "telegram"); !namesVerb(got, "verified-sender") {
|
|
t.Errorf("a holder of its own account lost verified-sender: %v", got)
|
|
}
|
|
}
|