Research 031 counted the repairs people made by hand: a push to unstick a plan waiting on a report, a controller restarted to make an object again, a plan closed, a consumer re-made from now. Each was the ordinary path taken again by someone who noticed. The healer registry makes each a registered response to one condition kind, with a budget, a settle and its event: - H1 sent-not-reported: ask the machine's node-engine to report again (mesh.node.<n>.ask.report); if it does not report what it was sent, send it again, never moving a build a policy or a plan holds back - H2 stalled: close a plan whose wait is superseded or finished - H3 holder-silent / consumer-lost: the send's own assertion of the bus's objects (issue 208's note) - H4 consumer-behind: consumer-reset, only for a consumer the stream table marks resettable (the controller's own events consumer) - H5 is the identity provider's own repair (ADR 0224 §5), registered only Success is the observation clearing the condition, never the healer; a spent budget hands the condition to the operator, urgent, with what was tried, and no healer touches it again. Every act is begun in the store before it is made (migration 0070), kept in the condition's tried as "healer Hn" and said as the seat event healer-acted; a heal is never a hand act. More than twelve acts in an hour stop every healer until an hour after the last, said urgently. Only the lease holder heals. S15 is live: a cause repaired by hand twice in a fortnight raises healer-wanted, naming the healer that was not enough where one exists. D6's far-behind finding has its own kind, consumer-behind. Nodes are granted the question; the controller's grant gains healer-acted (genesis lock in mesh-host). `healers` lists the registry, the acts and the brake; status counts the week's heals.
53 lines
2.4 KiB
Go
53 lines
2.4 KiB
Go
package broker_test
|
|
|
|
import (
|
|
"slices"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-controller/internal/broker"
|
|
"github.com/novox/mesh-controller/internal/catalogue"
|
|
"github.com/novox/mesh-controller/internal/conditions"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
// The facts the control plane states are named twice — in the grant that permits them and in the code
|
|
// that states them — because `link` imports `broker` and the dependency cannot go the other way. So a
|
|
// test keeps them agreeing: a subject the grant omits is refused at the moment the mesh has something
|
|
// to say, and one the grant adds that nothing states is authority nobody uses.
|
|
//
|
|
// An external test package, because it may import both while neither imports the other.
|
|
func TestTheFactsTheGrantPermitsAreTheFactsTheMeshStates(t *testing.T) {
|
|
if broker.ControllerSeat != link.MeshControllerSeat {
|
|
t.Fatalf("the grant is written for the %q seat and the mesh states its facts under %q",
|
|
broker.ControllerSeat, link.MeshControllerSeat)
|
|
}
|
|
// And what is wrong, as it changes, and the self-check's heartbeat (novox/hq to-be 45 §2, §4).
|
|
states := append([]string{link.KeyApplied, link.KeyRefused, link.KeyBuiltBefore}, conditions.Events...)
|
|
states = append(states, conditions.HeartbeatEvent)
|
|
// And a value given by hand, replaced after its module's first good start (novox/hq ADR 0228).
|
|
states = append(states, link.KeySecretReplaced)
|
|
// And every act a healer takes (novox/hq to-be 45 §7).
|
|
states = append(states, link.KeyHealerActed)
|
|
for _, event := range states {
|
|
if !slices.Contains(broker.ControllerStates, event) {
|
|
t.Errorf("the mesh states %q and its account may not publish it", event)
|
|
}
|
|
}
|
|
if len(broker.ControllerStates) != len(states) {
|
|
t.Errorf("the grant permits %v, which is more than the mesh states", broker.ControllerStates)
|
|
}
|
|
// **And the seat says it.** A seat carries the protocol of its role (novox/hq ADR 0129), so the
|
|
// facts the control plane states are the seat's `emits` — which is what lets anything else declare
|
|
// that it consumes them, and what the subject-agreement check reads to know they have an owner.
|
|
var declared []string
|
|
for _, seat := range catalogue.SeatsWithAProtocol() {
|
|
if seat.Name == broker.ControllerSeat {
|
|
declared = seat.Emits
|
|
}
|
|
}
|
|
if !slices.Equal(declared, broker.ControllerStates) {
|
|
t.Errorf("the %s seat emits %v and the grant permits %v", broker.ControllerSeat,
|
|
declared, broker.ControllerStates)
|
|
}
|
|
}
|