package main import ( "strings" "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 TestTheMeshNamesWhichMachinesRunWhichHost(t *testing.T) { split := hostSplit([]inventory.Node{ {Name: "anchor", HostVersion: "04a27ca"}, {Name: "laptop", HostVersion: "ced54d4"}, {Name: "spare", HostVersion: "04a27ca"}, }) if len(split) != 2 { t.Fatalf("two versions were reported and the split has %d: %v", len(split), split) } if got := strings.Join(split["04a27ca"], ","); got != "anchor,spare" && got != "spare,anchor" { t.Fatalf("04a27ca is held by %q", got) } if got := strings.Join(split["ced54d4"], ","); got != "laptop" { t.Fatalf("ced54d4 is held by %q", got) } } func TestTheMeshDoesNotClaimWhichHostIsNewer(t *testing.T) { // **The fault this replaced.** A host reports its version as a commit, and commits have no order. // The first version compared them as strings and, on the live mesh, named the three machines // running the NEWER host as the ones behind: `ced54d4` sorts above `04a27ca` and means nothing. // // There is no assertion to make about which is newer, and that is the point — the type says so. // hostSplit returns who runs what, and nothing that could be read as an ordering. split := hostSplit([]inventory.Node{ {Name: "old-but-sorts-high", HostVersion: "ced54d4"}, {Name: "new-but-sorts-low", HostVersion: "04a27ca"}, }) for version, machines := range split { if len(machines) != 1 { t.Fatalf("%s is held by %v", version, machines) } } } func TestAMachineThatHasNotSaidIsNotAVersion(t *testing.T) { // It may be running anything. Counting it as a version would invent a disagreement; `node show` // says per machine that it has not said. split := hostSplit([]inventory.Node{ {Name: "anchor", HostVersion: "04a27ca"}, {Name: "quiet"}, }) if split != nil { t.Fatalf("one reported version and one silence read as a disagreement: %v", split) } } func TestMachinesAgreeingOnTheirHostAreNotADisagreement(t *testing.T) { if split := hostSplit([]inventory.Node{ {Name: "anchor", HostVersion: "v2"}, {Name: "laptop", HostVersion: "v2"}, }); split != nil { t.Fatalf("machines agreeing reported a split: %v", split) } } func TestAMeshWhereNothingReportedAHostStatesNoDisagreement(t *testing.T) { if split := hostSplit([]inventory.Node{{Name: "anchor"}, {Name: "laptop"}}); split != nil { t.Fatalf("a mesh told no host version reported a split: %v", split) } } 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, "ced54d4"); err != nil { t.Fatal(err) } again, err := open.inventory.NodeByName(t.Context(), "anchor") if err != nil { t.Fatal(err) } if again.HostVersion != "ced54d4" { 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 != "ced54d4" { t.Fatalf("a report carrying no host version cleared what the machine had said: %q", kept.HostVersion) } }