Files
mesh-controller/cmd/mesh-control/acts_test.go
T
jschoubben d032fe6e8d assign: what it checks is the mesh, not the one machine
An assignment was verified by resolving the node it was made on. The verify that matters
is resolution over all of them: a module offering a mesh-scoped provision stops offering
it the moment its own node stops resolving, so an assignment could be reported as fine
while it took that provision away from every consumer elsewhere. Those consumers were
then told "nothing in this mesh provides it", naming as the remedy a module that was
already assigned — a wrong answer about a machine nobody had touched.

That is novox/hq 04-ISSUES/017's shape exactly: an action succeeds into a state its own
verify rejects, and it does so because the action's own test is not the test the verify
uses. 017's remedy was to make them the same test, and this makes them the same test.

The assignment is still kept, and that is the other half of the decision. Assignment is
not an ordering: a consumer assigned before its provider does not resolve for as long as
it takes to assign the provider, and refusing the first half of a pair would make the
order somebody types two commands in part of the mesh's rules. So `assign` and `unassign`
now name every OTHER machine that cannot be worked out as things stand, in the mesh's own
words, beside whatever they already said about this one. It reports the state and never
claims causation — saying "this assignment broke laptop" would mean resolving the whole
mesh twice and would still be a guess about which of several changes did it.

It costs a resolution per machine. Assignment is a person typing a command, and being
told which machines this just blocked is worth more than the milliseconds.

This is what a four-node raise read as "bumping a module's version broke provider
recognition". It was neither the version nor the provider: nothing in this codebase reads
the version column, every lookup is keyed on the module name alone, and a test in the
previous commit now says so. It was one machine's set of assignments, and nothing said so.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-10 21:11:33 +02:00

134 lines
5.0 KiB
Go

package main
import (
"strings"
"testing"
"github.com/novox/mesh-control/internal/catalogue"
)
// What an assignment says about the machines it was not about.
// **An assignment that blocks another machine says so.**
//
// The shape found on a four-node raise: a module offering a mesh-scoped provision stops offering it
// the moment its own node stops resolving, so an assignment to the provider's machine silently took
// a provision away from every consumer elsewhere. Those consumers were then told *nothing in this
// mesh provides it*, naming as the remedy a module that was already assigned — which read, on the
// way back, as a version bump breaking provider recognition. It was neither the version nor the
// provider: it was one machine's set of assignments, and nothing said so.
//
// novox/hq 04-ISSUES/017 names the general shape — an action succeeding into a state its own verify
// rejects, because the action's test is not the test the verify uses. `assign` tested one node; the
// verify is resolution over all of them.
func TestAnAssignmentThatBlocksAnotherMachineSaysWhichAndWhy(t *testing.T) {
open := aMesh(t)
ctx := t.Context()
// A provider on the hub and a consumer on the other machine, both resolving.
register(t, open, catalogue.Manifest{Module: "step-ca", Version: "1",
Provides: []catalogue.Offer{{Name: "acme-ca", Scope: catalogue.ScopeMesh}},
Serves: map[string]map[string]any{"acme-ca": {"path": "/acme/directory"}}})
register(t, open, catalogue.Manifest{Module: "route-proxy", Version: "1",
Requires: []string{"acme-ca"}})
if _, err := assign(ctx, open, "anchor", "step-ca"); err != nil {
t.Fatal(err)
}
said, err := assign(ctx, open, "laptop", "route-proxy")
if err != nil {
t.Fatalf("a mesh that should resolve did not: %v\n%s", err, said)
}
if strings.Contains(said, "cannot be worked out") {
t.Fatalf("a well mesh was reported as blocked:\n%s", said)
}
// Now break the hub's own set, with nothing but the command a person has.
one, two := rivals()
register(t, open, one)
register(t, open, two)
if _, err := assign(ctx, open, "anchor", "rival-one"); err != nil {
t.Fatal(err)
}
said, err = assign(ctx, open, "anchor", "rival-two")
if err == nil {
t.Fatal("an assignment that makes its own node incoherent was not reported at all")
}
// The node's own refusal is the error, as it always was. What is new is that the machines this
// just took a provision away from are named in the same breath.
if !strings.Contains(said, "laptop") {
t.Fatalf("the machine this blocked is not named:\n%s\n\n%v", said, err)
}
if !strings.Contains(said, "acme-ca") {
t.Fatalf("what laptop is now missing is not said:\n%s", said)
}
if !strings.Contains(said, "cannot be worked out as things stand") {
t.Fatalf("the report does not say what state the mesh is in:\n%s", said)
}
// And the assignment is kept: it is what a person meant, and assignment is not an ordering.
assigned, err := open.inventory.Assigned(ctx, "anchor")
if err != nil {
t.Fatal(err)
}
if !contains(assigned, "rival-two") {
t.Fatalf("the assignment was not kept: %v", assigned)
}
}
// A consumer assigned before its provider is refused for itself and kept, because assignment is not
// an ordering — refusing the first half of a pair would make the order somebody types two commands
// in part of the mesh's rules.
func TestAConsumerAssignedBeforeItsProviderIsStillAssigned(t *testing.T) {
open := aMesh(t)
ctx := t.Context()
register(t, open, catalogue.Manifest{Module: "route-proxy", Version: "1",
Requires: []string{"acme-ca"}})
said, err := assign(ctx, open, "laptop", "route-proxy")
if err == nil {
t.Fatalf("a consumer with nothing to consume resolved:\n%s", said)
}
assigned, err := open.inventory.Assigned(ctx, "laptop")
if err != nil {
t.Fatal(err)
}
if !contains(assigned, "route-proxy") {
t.Fatalf("assignment became an ordering: %v", assigned)
}
}
// Unassigning is the ordinary way to stop providing something to another machine, and it reports
// the same way for the same reason.
func TestUnassigningAProviderNamesWhoIsNowBlocked(t *testing.T) {
open := aMesh(t)
ctx := t.Context()
register(t, open, catalogue.Manifest{Module: "step-ca", Version: "1",
Provides: []catalogue.Offer{{Name: "acme-ca", Scope: catalogue.ScopeMesh}},
Serves: map[string]map[string]any{"acme-ca": {"path": "/acme/directory"}}})
register(t, open, catalogue.Manifest{Module: "route-proxy", Version: "1",
Requires: []string{"acme-ca"}})
if _, err := assign(ctx, open, "anchor", "step-ca"); err != nil {
t.Fatal(err)
}
if _, err := assign(ctx, open, "laptop", "route-proxy"); err != nil {
t.Fatal(err)
}
said, err := unassign(ctx, open, "anchor", "step-ca")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(said, "laptop") || !strings.Contains(said, "acme-ca") {
t.Fatalf("taking the provider away said nothing about who was consuming it:\n%s", said)
}
}
func contains(all []string, one string) bool {
for _, s := range all {
if s == one {
return true
}
}
return false
}