novox/hq 04-ISSUES/087. A host refuses a declaration carrying a field it does not know, and refuses it WHOLE — deliberately, because that keeps a half-understood declaration off a machine. It makes every new declaration field a flag day: hosts first, then the controller. The mesh had no record of which host any machine ran, so that order was kept by somebody remembering it, and a machine that refused for this reason reported a failure with nothing saying why. The machine has reported its host version since ADR 0141. The controller's own copy of the report did not have the field, so it was unmarshalled into nothing and thrown away on arrival. It has it now, records it, and shows it in `node show` — "not reported" rather than blank, because a machine that has not said is not a machine running nothing. Status says which machines run an older host than another machine does, and which is newest. Deliberately disagreement rather than staleness: nothing delivers a host version yet (ADR 0141, accepted and not built), so the mesh holds no canonical current version and cannot honestly say a machine is behind THE host. What it can say is that the oldest host in the mesh is what the mesh may send. A machine that has reported nothing is left out rather than called behind. Versions compare as strings, which suits the timestamps and commits this mesh uses and is wrong for a scheme where "10" sorts before "9" — said in the code, at the place that would have to learn.
99 lines
3.5 KiB
Go
99 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// A host refuses a declaration carrying a field it does not know, and refuses it whole — so every new
|
|
// field is a flag day, and the mesh had no record of which host any machine ran (novox/hq
|
|
// 04-ISSUES/087). The order was kept by somebody remembering it.
|
|
|
|
func TestTheMeshNamesEveryMachineOnAnOlderHostThanAnother(t *testing.T) {
|
|
older, newest := hostsBehind([]inventory.Node{
|
|
{Name: "anchor", HostVersion: "2026-09-29-0918"},
|
|
{Name: "laptop", HostVersion: "2026-09-29-0113"},
|
|
{Name: "spare", HostVersion: "2026-09-29-0918"},
|
|
})
|
|
if newest != "2026-09-29-0918" {
|
|
t.Fatalf("the newest reported host is %q", newest)
|
|
}
|
|
if len(older) != 1 || older[0].Name != "laptop" {
|
|
t.Fatalf("the machines behind another are %v, wanted laptop alone", older)
|
|
}
|
|
}
|
|
|
|
func TestAMachineThatHasNotSaidIsNotCalledBehind(t *testing.T) {
|
|
// It may be running anything. Guessing either way is worse than saying it has not said, which
|
|
// `node show` does per machine.
|
|
older, newest := hostsBehind([]inventory.Node{
|
|
{Name: "anchor", HostVersion: "2026-09-29-0918"},
|
|
{Name: "quiet"},
|
|
})
|
|
if newest != "2026-09-29-0918" {
|
|
t.Fatalf("the newest reported host is %q", newest)
|
|
}
|
|
for _, n := range older {
|
|
if n.Name == "quiet" {
|
|
t.Fatal("a machine that reported no host version was called behind")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAMeshWhereNothingReportedAHostStatesNoDisagreement(t *testing.T) {
|
|
// Every machine predating ADR 0141, or a mesh that has heard nothing since the column existed.
|
|
// Saying "0 machines behind" would be a claim the mesh cannot make.
|
|
older, newest := hostsBehind([]inventory.Node{{Name: "anchor"}, {Name: "laptop"}})
|
|
if len(older) != 0 || newest != "" {
|
|
t.Fatalf("a mesh that has been told no host version reported %v / %q", older, newest)
|
|
}
|
|
}
|
|
|
|
func TestMachinesAllOnOneHostAreNotBehind(t *testing.T) {
|
|
older, _ := hostsBehind([]inventory.Node{
|
|
{Name: "anchor", HostVersion: "v2"},
|
|
{Name: "laptop", HostVersion: "v2"},
|
|
})
|
|
if len(older) != 0 {
|
|
t.Fatalf("machines agreeing on their host were reported as behind: %v", older)
|
|
}
|
|
}
|
|
|
|
func TestAReportedHostVersionIsKeptAndReadBack(t *testing.T) {
|
|
// The machine has sent this since ADR 0141 and the controller's own copy of the report did not
|
|
// have the field, so it was unmarshalled into nothing. End to end through the store, because the
|
|
// fault was a field that existed on one side of the wire only.
|
|
open := aMesh(t)
|
|
record, err := open.inventory.NodeByName(t.Context(), "anchor")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if record.HostVersion != "" {
|
|
t.Fatalf("a machine that never reported one has host version %q", record.HostVersion)
|
|
}
|
|
if err := open.inventory.RecordHostVersion(t.Context(), record.ID, "2026-09-30-0214"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
again, err := open.inventory.NodeByName(t.Context(), "anchor")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if again.HostVersion != "2026-09-30-0214" {
|
|
t.Fatalf("the reported host version read back as %q", again.HostVersion)
|
|
}
|
|
// An empty report never clears what a machine last said: a bare word that the node is there
|
|
// says nothing about its host.
|
|
if err := open.inventory.RecordHostVersion(t.Context(), record.ID, " "); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
kept, err := open.inventory.NodeByName(t.Context(), "anchor")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if kept.HostVersion != "2026-09-30-0214" {
|
|
t.Fatalf("a report carrying no host version cleared what the machine had said: %q",
|
|
kept.HostVersion)
|
|
}
|
|
}
|