Judge a seat's holder silent only on verbs it must serve (hq issue 299)
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 superseded: a newer delivery to the same trunk took over its walk
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 superseded: a newer delivery to the same trunk took over its walk
node-uplink gained its first verbs in #116, both optional while its holders catch up (ADR 0246). D3 read any verb as one the holder must answer for, and raised a silent condition on every machine holding the seat. A seat whose verbs are all optional now asks nothing of its holders' silence.
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/catalogue"
|
||||
)
|
||||
|
||||
// The live case of 2026-10-07 (novox/hq issue 299), replayed: the node-uplink seat gained its first
|
||||
// verbs, `resolvers` and `links`, both optional while its holders catch up, and the holders serve
|
||||
// neither. The working set is read from the store, whose rows carry no optional mark, so the seat comes
|
||||
// back through UseSeats as it does live. Its holder on every machine answers nothing for it on the bus,
|
||||
// and must not be judged silent: a seat whose verbs are all optional asks no holder to answer.
|
||||
func TestAHolderOfASeatWhoseVerbsAreAllOptionalIsNotSilent(t *testing.T) {
|
||||
defer catalogue.UseSeats(catalogue.DefaultSeats())
|
||||
var rows []catalogue.Seat
|
||||
for _, s := range catalogue.DefaultSeats() {
|
||||
stored := s
|
||||
stored.Serves = nil
|
||||
for _, v := range s.Serves {
|
||||
v.Optional = false // the store never keeps the mark
|
||||
stored.Serves = append(stored.Serves, v)
|
||||
}
|
||||
rows = append(rows, stored)
|
||||
}
|
||||
catalogue.UseSeats(rows)
|
||||
|
||||
uplink, ok := catalogue.SeatNamed("node-uplink")
|
||||
if !ok || len(uplink.Serves) == 0 {
|
||||
t.Fatalf("node-uplink must serve verbs for this replay: %+v", uplink)
|
||||
}
|
||||
for _, v := range uplink.Serves {
|
||||
if !v.Optional {
|
||||
t.Fatalf("node-uplink's %s is required; this replay is of a seat whose verbs are all optional", v.Name)
|
||||
}
|
||||
}
|
||||
|
||||
nodes := []string{"anchor", "home-server", "workstation", "laptop"}
|
||||
heard := map[string]bool{}
|
||||
var recorded []catalogue.Held
|
||||
for _, n := range nodes {
|
||||
heard[n] = true
|
||||
recorded = append(recorded, catalogue.Held{Claim: "node-uplink", Scope: catalogue.ScopeNode, Node: n,
|
||||
Module: "networkmanager"})
|
||||
}
|
||||
expected := holdersToHear(catalogue.SeatsWithAProtocol(), recorded, nil, heard, nil, time.Now())
|
||||
if _, asked := expected["node-uplink"]; asked {
|
||||
t.Fatalf("node-uplink's holders are expected to answer though every verb of the seat is optional: %v",
|
||||
expected["node-uplink"])
|
||||
}
|
||||
// Nothing answers for the seat, as live: no silence is said about it.
|
||||
for _, o := range silentHolders(expected, map[string]map[string]bool{}) {
|
||||
if o.ID == "node-uplink."+o.Machine {
|
||||
t.Errorf("a holder serving none of a seat's optional verbs was judged silent: %s", o.Summary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Silence is still judged on a required verb: a seat with one required and one optional verb, held on a
|
||||
// machine that is heard from, whose holder answers nothing — silent.
|
||||
func TestAHolderServingNoRequiredVerbIsStillSilent(t *testing.T) {
|
||||
seat := catalogue.Seat{Name: "example-seat", Scope: catalogue.ScopeNode, Serves: []catalogue.Verb{
|
||||
{Name: "state"}, {Name: "later", Optional: true}}}
|
||||
recorded := []catalogue.Held{{Claim: "example-seat", Scope: catalogue.ScopeNode, Node: "laptop", Module: "m"}}
|
||||
expected := holdersToHear([]catalogue.Seat{seat}, recorded, nil, map[string]bool{"laptop": true}, nil, time.Now())
|
||||
if !expected["example-seat"]["laptop"] {
|
||||
t.Fatalf("a holder of a seat with a required verb is not expected to answer: %v", expected)
|
||||
}
|
||||
out := silentHolders(expected, map[string]map[string]bool{})
|
||||
if len(out) != 1 || out[0].ID != "example-seat.laptop" || out[0].Token != "silent" {
|
||||
t.Fatalf("a holder serving no required verb is not said to be silent: %+v", out)
|
||||
}
|
||||
if got := silentHolders(expected, map[string]map[string]bool{"example-seat": {"laptop": true}}); len(got) != 0 {
|
||||
t.Fatalf("a holder that answers is said to be silent: %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHoldingNeedsAnAnswer(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
serves []catalogue.Verb
|
||||
want bool
|
||||
}{
|
||||
{"no verb", nil, false},
|
||||
{"only optional verbs", []catalogue.Verb{{Name: "a", Optional: true}, {Name: "b", Optional: true}}, false},
|
||||
{"a required verb among optional ones", []catalogue.Verb{{Name: "a", Optional: true}, {Name: "b"}}, true},
|
||||
{"only required verbs", []catalogue.Verb{{Name: "a"}}, true},
|
||||
} {
|
||||
if got := holdingNeedsAnAnswer(catalogue.Seat{Name: "s", Serves: c.serves}); got != c.want {
|
||||
t.Errorf("%s: holdingNeedsAnAnswer = %v, want %v", c.name, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user