package main import ( "encoding/json" "strings" "testing" "time" "github.com/novox/mesh-controller/internal/inventory" "github.com/novox/mesh-controller/internal/link" ) // The assignment generation a declaration was composed from (novox/hq issue 234). func bodyKeys(t *testing.T, s sendable) map[string]any { t.Helper() raw, err := s.Body() if err != nil { t.Fatal(err) } var keys map[string]any if err := json.Unmarshal(raw, &keys); err != nil { t.Fatal(err) } return keys } // **The generation goes on the wire only to a machine whose node-engine said it reads one**: an older // node-engine decodes strictly and refuses an unknown key, whole. No put-back mark is ever sent: a gate // composes its put-back afresh, with the generation as it stands. func TestTheGenerationIsSentOnlyToAMachineThatReadsOne(t *testing.T) { resources := []map[string]any{{"id": "a", "type": "file", "path": "/etc/a", "content": "x\n"}} reads := order{sequence: 12, epoch: 57, generation: 40, readsGeneration: true, acting: 57} var told sendable told.Resources = resources reads.stamp(&told) keys := bodyKeys(t, told) if keys["generation"] != float64(40) || keys["sequence"] != float64(12) { t.Fatalf("a machine that reads a generation was sent %v", keys) } if _, there := keys["put_back"]; there { t.Fatalf("a put-back mark was sent: %v", keys) } if told.composedFrom != 40 || told.actingEpoch != 57 { t.Errorf("the send does not keep what it was composed from for its record: %+v", told) } older := order{sequence: 12, epoch: 57, generation: 40, readsGeneration: false, acting: 57} var untold sendable untold.Resources = resources older.stamp(&untold) if _, there := bodyKeys(t, untold)["generation"]; there { t.Fatal("a machine whose node-engine never said it reads a generation was sent one") } if untold.composedFrom != 40 { t.Errorf("the generation it was composed from is recorded whether or not it was sent: %+v", untold) } } // **The sender is named**: the caller of the seat call when there is one, else the shell's account, and the // process and build that composed it. func TestASendNamesItsSender(t *testing.T) { said := senderOf("g14.node-tools, through the mesh-controller seat") if !strings.Contains(said, "g14.node-tools") || !strings.Contains(said, "pid ") || !strings.Contains(said, "build ") { t.Fatalf("the sender reads %q", said) } } // refusedSend is a send a machine refused for its generation at a moment. func refusedSend(at time.Time) inventory.Send { return inventory.Send{NodeName: "anchor", Sequence: 12, Epoch: 57, Generation: 38, RefusedApplied: 40, Sender: "a one-shot push by jochen at a shell on anchor (pid 4242 on anchor, build 2026.10.11)", Modules: []string{"docker"}, SentAt: at.Add(-time.Second), RefusedAt: &at, Recorded: true} } // **A refused send is raised naming its sender** (novox/hq issue 234): who sent it, from which generation, // against which the machine applied, and its sequence — the facts that took a morning to look for. func TestARefusedSendIsRaisedNamingItsSender(t *testing.T) { now := time.Date(2026, 10, 11, 12, 0, 0, 0, time.UTC) f := calm(now) f.refusedSends = []inventory.Send{refusedSend(now.Add(-time.Minute))} got := watchGenerationRefusals(f) if len(got) != 1 { t.Fatalf("%+v", got) } o := got[0] if o.Key() != "machine.anchor.older-generation" || o.Machine != "anchor" { t.Errorf("raised as %s about %q", o.Key(), o.Machine) } for _, want := range []string{"a one-shot push by jochen", "generation 38", "generation 40", "sequence 12"} { if !strings.Contains(o.Summary, want) { t.Errorf("the summary does not say %q: %s", want, o.Summary) } } if o.Headline == "" || o.Explanation == "" || o.Resolved == "" { t.Errorf("the condition is not worded for the operator: %+v", o) } } // **A refusal of a send the mesh has no record of is raised too, its sender said to be unknown** — never // only a line on stderr. func TestARefusalOfAnUnrecordedSendIsRaisedSayingTheSenderIsUnknown(t *testing.T) { now := time.Date(2026, 10, 11, 12, 0, 0, 0, time.UTC) f := calm(now) s := refusedSend(now.Add(-time.Minute)) s.Recorded, s.Sender, s.Modules = false, "a sender the mesh has no record of (no send of this sequence was recorded)", nil f.refusedSends = []inventory.Send{s} got := watchGenerationRefusals(f) if len(got) != 1 || !strings.Contains(got[0].Summary, "no record of") { t.Fatalf("an unattributed refusal was not raised as one: %+v", got) } } // **When the refusal showed the mesh's counter behind the machine, the condition asks for a push** — it was // raised, and nothing has sent the machine its declaration since — and does not say there is nothing to do. func TestACounterBehindTheMachineAsksForAPush(t *testing.T) { now := time.Date(2026, 10, 11, 12, 0, 0, 0, time.UTC) f := calm(now) s := refusedSend(now.Add(-time.Minute)) s.Generation, s.RefusedApplied, s.CounterRaisedTo = 12, 40, 41 f.refusedSends = []inventory.Send{s} got := watchGenerationRefusals(f) if len(got) != 1 { t.Fatalf("%+v", got) } if !strings.Contains(got[0].Summary, "`push anchor`") || !strings.Contains(got[0].Explanation, "push anchor") || strings.Contains(got[0].Explanation, "Nothing for you to do") { t.Errorf("a counter behind the machine does not ask for a push: %s / %s", got[0].Summary, got[0].Explanation) } } // **Only a refusal of the counter as it stands is the counter behind**: a stale sender's generation is below // it, and the counter is left alone for that. func TestOnlyARefusalOfTheCounterAsItStandsRaisesIt(t *testing.T) { for _, c := range []struct { refused, applied, now int64 behind bool }{ {refused: 12, applied: 40, now: 12, behind: true}, // the store was put back: the mesh says 12, the machine had 40 {refused: 38, applied: 40, now: 41, behind: false}, // a stale sender: the counter is already past {refused: 38, applied: 40, now: 40, behind: false}, // a stale sender: the counter is where the machine is } { r := link.GenerationRefusal{Generation: c.refused, Applied: c.applied} if got := counterBehind(r, c.now); got != c.behind { t.Errorf("refused %d, applied %d, counter %d: behind %v, want %v", c.refused, c.applied, c.now, got, c.behind) } } }