package web // Guest-network card (R-319) — the first R-264 reader. // // THE FIXTURES BELOW ARE THE REAL WIRE. `liveGuestNetJSON` is the `guest_net` stanza copied verbatim // out of `demo-felhom-8363b5`'s newest row in the live hub's `host_reports` table on 2026-08-13 // (agent 0.129.0). Testing against a hand-written shape would have proved only that the parser // matches my own idea of the format — and the defect this whole class comes from (R-260) was exactly // a hub-side struct that did not match what the agent actually sends. // // Every test drives ServeHTTP, so a template gate that never renders is visible here. That is the // seam-wiring rule: handler tests prove nothing about reachability, and this project has shipped four // features whose entry point was never wired. import ( "strings" "testing" ) // The real stanza: one owned guest, healthy, DHCP, no repairs. `heals_last_hour` and friends are // ABSENT because the agent marks them `omitempty` and this box has never needed a repair — which is // itself the shape scenario A has to survive. const liveGuestNetJSON = `{ "host": {"cpu_percent": 4.0, "memory_percent": 30.0, "disk_percent": 20.0}, "guest_net": { "checked_at": "2026-08-13T08:15:33Z", "guests": [ {"vmid": 9201, "state": "healthy", "mode": "dhcp", "ip": "192.168.0.149", "has_route": true, "dhclient_alive": true, "checked_at": "2026-08-13T08:14:30Z", "message": "address, default route and dhclient all present"} ] } }` // The same box after the watchdog has had to keep fixing it — the R-54 heal fields populated. This is // the shape the incident of 2026-07-20 would have produced had the watchdog existed then. const repairingGuestNetJSON = `{ "host": {"cpu_percent": 4.0, "memory_percent": 30.0, "disk_percent": 20.0}, "guest_net": { "checked_at": "2026-08-13T08:15:33Z", "guests": [ {"vmid": 9201, "state": "healthy", "mode": "dhcp", "ip": "192.168.0.149", "has_route": true, "dhclient_alive": true, "checked_at": "2026-08-13T08:14:30Z", "healed": true, "heal_succeeded": true, "last_heal_at": "2026-08-13T08:09:12Z", "heals_last_hour": 6, "message": "dhclient was absent; restarted"} ] } }` // A report from a capable agent with NO guest_net stanza at all — the watchdog switched off, or a // report predating the feature on this box. const silentGuestNetJSON = `{"host": {"cpu_percent": 4.0, "memory_percent": 30.0, "disk_percent": 20.0}}` // The stanza arrives, but its contents are not what the hub expects: `guests` is an object where an // array belongs, and `heals_last_hour` is a string. This is what a wire drift or a truncated write // looks like from the hub's side. const malformedGuestNetJSON = `{ "host": {"cpu_percent": 4.0}, "guest_net": {"checked_at": "2026-08-13T08:15:33Z", "guests": {"vmid": "nine-two-oh-one"}} }` // ── A — a machine reporting healthy guest networking, no repairs → shown as healthy ───────────── // // WRONG OUTCOME GUARDED: an empty or alarming state on a machine that is fine. A card that cried // unknown on every healthy box would be switched off within a week, and then the B case below would // never be seen either. func TestGuestNet_A_HealthyRendersHealthy(t *testing.T) { s, st, _ := newRevealServer(t) cookie, _ := newRevealSession(t, s) seedNetHost(t, st, "demo-felhom-8363b5", "0.129.0", liveGuestNetJSON, "") body := getHostPage(t, s, cookie, "demo-felhom-8363b5") if !strings.Contains(body, "Guest network") { t.Fatal("no Guest network card — the reader shows nothing") } if !strings.Contains(body, "192.168.0.149") { t.Error("the guest's address did not reach the page") } if !strings.Contains(body, "badge-ok") { t.Error("a healthy guest must render as healthy, not as an empty or alarming state") } if strings.Contains(body, "needs attention") { t.Error("a machine that is fine is being alarmed on") } // The unknown branches must NOT fire for a box that reported properly. if strings.Contains(body, "does not run the guest-network watchdog") || strings.Contains(body, "reported no guest-network state") { t.Error("a reporting box rendered one of the unknown sentences") } } // ── B — a machine whose watchdog has repaired the guest repeatedly ────────────────────────────── // // WRONG OUTCOME GUARDED: a green tick because the CURRENT state is fine. This is the exact shape of // the failed-disk-drawn-as-a-healthy-empty-disk defect, and it is the reason this card exists at all: // `state` says "healthy" in this fixture, because between repairs it IS. func TestGuestNet_B_RepeatedRepairsAreVisible(t *testing.T) { s, st, _ := newRevealServer(t) cookie, _ := newRevealSession(t, s) seedNetHost(t, st, "demo-felhom-8363b5", "0.129.0", repairingGuestNetJSON, "") body := getHostPage(t, s, cookie, "demo-felhom-8363b5") if !strings.Contains(body, "needs attention") { t.Fatal("a guest repaired 6 times in an hour is reported as fine — the whole point of the card") } if !strings.Contains(body, "had their networking repaired in the last hour") { t.Error("the climbing-repairs sentence is missing") } if !strings.Contains(body, ">6<") { t.Error("the repair COUNT is not rendered; 'some repairs' is not the signal, the number is") } if !strings.Contains(body, "2026-08-13T08:09:12Z") { t.Error("the last repair time is not offered (it is in the title attribute)") } } // ── C — a machine that does not report the fact at all → drawn as unknown, never healthy ──────── // // WRONG OUTCOME GUARDED: absence read as good news. THE ONE THAT MATTERS. Two distinct absences are // checked because they need different words: an agent that cannot report it, and a capable agent that // said nothing. func TestGuestNet_C_SilentIsUnknownNotHealthy(t *testing.T) { t.Run("capable agent, no stanza", func(t *testing.T) { s, st, _ := newRevealServer(t) cookie, _ := newRevealSession(t, s) seedNetHost(t, st, "demo-hp-bb76ea", "0.129.0", silentGuestNetJSON, "") body := getHostPage(t, s, cookie, "demo-hp-bb76ea") if !strings.Contains(body, "reported no guest-network state") { t.Fatal("a silent box must SAY it is unknown") } if strings.Contains(body, "badge-ok") && strings.Contains(body, "Guest network") { assertNoHealthyBadgeInGuestNetCard(t, body) } }) t.Run("agent too old to report it", func(t *testing.T) { s, st, _ := newRevealServer(t) cookie, _ := newRevealSession(t, s) // 0.91.0 predates the R-54 watchdog (v0.92.0): the absence is expected AND still unknown. seedNetHost(t, st, "old-agent-box", "0.91.0", silentGuestNetJSON, "") body := getHostPage(t, s, cookie, "old-agent-box") if !strings.Contains(body, "does not run the guest-network watchdog") { t.Fatal("an old agent's silence must be named as an old agent's silence") } if !strings.Contains(body, "unknown, not healthy") { t.Error("the unknown-is-not-healthy sentence is missing") } assertNoHealthyBadgeInGuestNetCard(t, body) }) } // ── D — the fact arrives malformed → unknown, and the hub does not 500 ────────────────────────── // // WRONG OUTCOME GUARDED: a page that breaks on one machine's bad field. getHostPage fails the test on // any non-200, so surviving the call IS half the assertion. func TestGuestNet_D_MalformedIsUnknownAndDoesNotBreakThePage(t *testing.T) { s, st, _ := newRevealServer(t) cookie, _ := newRevealSession(t, s) seedNetHost(t, st, "demo-felhom-8363b5", "0.129.0", malformedGuestNetJSON, "") body := getHostPage(t, s, cookie, "demo-felhom-8363b5") // 200 or the test dies here if !strings.Contains(body, "reported no guest-network state") { t.Fatal("a malformed stanza must degrade to unknown") } assertNoHealthyBadgeInGuestNetCard(t, body) } // A guest whose state string is one the watchdog never asserts (a future value, or a truncated field) // is drawn as unknown rather than falling through to the healthy branch. The switch in // guestNetGuestView.Unknown is an ALLOW-LIST for exactly this reason: an unrecognised value defaults // to unknown, and adding a state to the agent cannot silently paint it green here. func TestGuestNet_UnrecognisedStateIsUnknown(t *testing.T) { s, st, _ := newRevealServer(t) cookie, _ := newRevealSession(t, s) seedNetHost(t, st, "demo-felhom-8363b5", "0.129.0", `{ "host": {"cpu_percent": 1.0}, "guest_net": {"checked_at": "2026-08-13T08:15:33Z", "guests": [{"vmid": 9201, "state": "quantum", "mode": "dhcp", "ip": "10.0.0.5"}]}}`, "") body := getHostPage(t, s, cookie, "demo-felhom-8363b5") if !strings.Contains(body, "partly unknown") { t.Fatal("an unrecognised guest state must summarise as unknown, not healthy") } } // The empty-list case is deliberately NOT the same as silence: the agent's own contract says a stanza // with a fresh checked_at and no guests means "the watchdog ran and found nothing to report", and that // must stay distinguishable from "the watchdog is not wired" — the shape the v0.91.0 inert seam hid // behind. func TestGuestNet_EmptyListIsNotSilence(t *testing.T) { s, st, _ := newRevealServer(t) cookie, _ := newRevealSession(t, s) seedNetHost(t, st, "demo-felhom-8363b5", "0.129.0", `{"host": {"cpu_percent": 1.0}, "guest_net": {"checked_at": "2026-08-13T08:15:33Z"}}`, "") body := getHostPage(t, s, cookie, "demo-felhom-8363b5") if !strings.Contains(body, "The watchdog ran and has no guest to report") { t.Fatal("an empty guest list must say the watchdog RAN") } if strings.Contains(body, "reported no guest-network state") { t.Error("an empty list was collapsed into silence — the two are different facts") } } // assertNoHealthyBadgeInGuestNetCard checks that the Guest network card contains no healthy badge. // It slices the card out of the page rather than searching the whole body, because `badge-ok` occurs // all over a host page (capabilities, storage, WireGuard) and a whole-page search would pass // vacuously — which is the "instrument that can drop results silently" trap. func assertNoHealthyBadgeInGuestNetCard(t *testing.T, body string) { t.Helper() i := strings.Index(body, "Guest network") if i < 0 { t.Fatal("no Guest network card to slice — the assertion would have passed vacuously") } card := body[i:] if j := strings.Index(card, ""); j >= 0 { card = card[:j] } else { t.Fatal("could not find the end of the Guest network card") } if strings.Contains(card, "badge-ok") { t.Errorf("an UNKNOWN guest-network state rendered a healthy badge:\n%s", card) } } // A repair that was ATTEMPTED and FAILED is a harder fact than a repair that worked, and the count // alone cannot express it: six successful repairs is a nuisance, six failed ones is a guest that is // down right now. Dropping `heal_succeeded` from the decoder would be R-260 exactly — the OOB decoder // mirrored five of eight fields and the three it dropped included the deciding one. func TestGuestNet_FailedRepairIsDistinctFromFrequentRepair(t *testing.T) { s, st, _ := newRevealServer(t) cookie, _ := newRevealSession(t, s) seedNetHost(t, st, "demo-felhom-8363b5", "0.129.0", `{ "host": {"cpu_percent": 1.0}, "guest_net": {"checked_at": "2026-08-13T08:15:33Z", "guests": [{"vmid": 9201, "state": "unhealthy", "mode": "dhcp", "has_route": false, "dhclient_alive": false, "healed": true, "heal_succeeded": false, "heals_last_hour": 3, "message": "dhclient restart did not restore the lease"}]}}`, "") body := getHostPage(t, s, cookie, "demo-felhom-8363b5") if !strings.Contains(body, "repair failed") { t.Fatal("a repair that was attempted and failed is not distinguished from one that worked") } if !strings.Contains(body, "needs attention") { t.Error("a guest whose repair failed must summarise as needing attention") } assertNoHealthyBadgeInGuestNetCard(t, body) }