package link import ( "encoding/json" "testing" ) func TestTheWireFormatIsExactlyTheseFieldNames(t *testing.T) { // The contract with the control plane, which defines these separately. A matching test lives // there; rename a field on either side and both fail. for _, c := range []struct { value any expect []string }{ // novox/hq ADR 0539 §9: the node's facts carry the node-engine's own writer, epoch and sequence. {Report{Node: "n", Writer: Writer, Order: Order{Epoch: 1, Sequence: 2}}, []string{"node", "writer", "epoch", "sequence"}}, {Report{Node: "n", Applied: []string{"a"}, Failed: map[string]string{"k": "v"}, Refused: "r"}, []string{"node", "applied", "failed", "refused"}}, // novox/hq ADR 0100: what an adopted node holds, the firewall it was found with, and what // is reachable on it. {Report{Node: "n", Held: []Held{{ID: "i"}}, Firewall: "ufw", Reachable: []Reach{{Port: 1}}}, []string{"node", "held", "firewall", "reachable"}}, {Held{ID: "i", Module: "m", Kind: "file", Target: "/t", Changed: "rewritten", Kept: "/k"}, []string{"id", "module", "kind", "target", "since", "changed", "kept"}}, {Reach{Protocol: "tcp", Address: "0.0.0.0", Port: 8080, By: "c", Published: true, ContainerPort: 80}, []string{"protocol", "address", "port", "by", "published", "container-port"}}, // novox/hq to-be 45 §8: a witness's verdicts, said on every report while they stand, and the // witness contract this host keeps. {Report{Node: "n", Rollbacks: []Rollback{{Component: ComponentController}}, Witness: WitnessContract}, []string{"node", "rollbacks", "witness"}}, {Rollback{Component: ComponentNodeTools, From: "sha256:b", To: "sha256:a", Outcome: RolledBack, Why: "w"}, []string{"component", "from", "to", "outcome", "why", "at"}}, // novox/hq ADR 0240: every long-running resource's health, in every report and in its own event. {Report{Node: "n", Health: &Health{Contract: LivenessContract}}, []string{"node", "health"}}, {Health{Contract: LivenessContract, Resources: []ResourceHealth{}}, []string{"contract", "at", "resources"}}, {ResourceHealth{Module: "m", Resource: "m.r", Kind: "container", Target: "t", State: StateUnhealthy, Reason: ReasonRestarting, Streak: 2, Restarts: 3, Check: "http", Needs: "postgres-database"}, []string{"module", "resource", "kind", "target", "state", "reason", "since", "streak", "restarts", "check", "needs"}}, {HealthSaid{Node: "n"}, []string{"node", "health"}}, // novox/hq ADR 0266: an account judged for whether it can become root without a person. {ResourceHealth{Module: "m", Resource: "m.agent", Kind: KindAccount, Account: "agent", Root: "never"}, []string{"module", "resource", "kind", "target", "state", "since", "account", "root"}}, // novox/hq ADR 0241: the machine's own networking, beside its resources. {Health{Contract: ReadinessContract, Resources: []ResourceHealth{}, Network: &NetworkHealth{State: "unhealthy", Parts: []NetworkPart{}}}, []string{"contract", "at", "resources", "network"}}, {NetworkHealth{State: "unhealthy", Parts: []NetworkPart{}}, []string{"state", "since", "parts"}}, {NetworkPart{Part: "resolv-conf", State: "unhealthy", Reason: "r", Said: "s", Writer: "w", Owner: "o", Toward: []string{"hub"}, Streak: 2}, []string{"part", "state", "reason", "said", "writer", "owner", "toward", "since", "streak"}}, } { raw, err := json.Marshal(c.value) if err != nil { t.Fatal(err) } var fields map[string]any if err := json.Unmarshal(raw, &fields); err != nil { t.Fatal(err) } for _, want := range c.expect { if _, ok := fields[want]; !ok { t.Errorf("%T has no %q field; the control plane uses that name", c.value, want) } } if len(fields) != len(c.expect) { t.Errorf("%T has %d fields, expected %d: %v", c.value, len(fields), len(c.expect), fields) } } }