Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
4.0 KiB
name, description
| name | description |
|---|---|
| felhom-testing | Felhom testing doctrine — use when writing or reviewing ANY Go test in felhom-controller, felhom-agent, or the felhom.eu hub, and for EVERY correctness or security fix (the red-proof is mandatory there). Triggers - "write a test", "add tests", reviewing a diff that changes logic, fixing a bug, hardening a guard, or validating a fix live. Contains the non-hollow rules, the companion red-proof procedure, seam locations, and the green-gate command. |
Felhom testing doctrine
Non-hollow rule (the cardinal one)
A test must assert the effect, not the absence of error. Wrong: HTTP 200 came back, err == nil,
"function ran". Right: the stored row has the expected value, the rendered HTML contains the badge,
the state file transitioned, the fake recorded the exact command. If deleting the fix wouldn't fail
the test, the test is hollow.
Companion red-proof (mandatory for every correctness/security fix)
Prove the test detects the bug it guards against:
- Temporarily restore the PRE-FIX shape (revert the fixed line, or model the old predicate inline).
- Run the test → it must FAIL, with the wrong value visible in the failure message.
- Restore the fix → test passes.
git diffclean. - Record the red-proof outcome in
REPORT.md(what failed, with what value).
In-tree exemplars (verified):
felhom.eu/hub/internal/notify/dispatcher_test.goTestSeverityNotifies(~L27–49) — models the pre-fixwarning||errorpredicate inline and asserts the fix routes what it dropped.felhom.eu/hub/internal/api/event_test.goTestHandleEvent_CriticalPreserved— asserts the STORED severity; its red-proof was run by reverting the one-line switch (stored"info"→ FAIL).
Seams over shell-outs
Never let a unit test touch docker/pct/real /dev. Every repo's REUSE.md §4 lists its seams and
existing fakes — inject there:
- controller:
diskAgent(mockAgent),quiesce.Backend/Stacks,channelhealth.Probe/Sink,selfupdate.AgentSwapper,offboxRunner,bootstrap.PullFunc. - agent:
proxmox.Runner(mockRunner),storage.HostOps/HostReader, localapiOptionsfakes, Server seam funcs (reresolveWipe,deviceDurableID— override in tests, no real /dev). - hub:
Dispatcher.sendEmailFn,mailrelay.Sender,mailRateLimiter.now(clock),monitor.EventNotifyFunc, provider interfaces on the api Handler.
Test harness conventions: real store on t.TempDir() DB (hub/internal/api/host_test.go
newTestHandler pattern); t.Cleanup for teardown; table-driven where natural; pure
classifier functions get fixture tables (agent classifyClaim style).
What every test suite should also cover
- Negative cases: the 400/401/403/refusal paths, not just the happy path (e.g. unknown event_type → 400 AND nothing stored).
- Idempotency: re-running the op is a clean no-op where the contract says so (registry add, intent set, mount ensure).
- Fail-safe direction: for guards, ambiguity must refuse (agent claim/role classifiers are the canon — any read error ⇒ most-protected verdict).
Green gate (run before every commit that touches Go code)
go build ./... && go vet ./... && go test ./...
Run it in the module dir: felhom.eu/hub/, felhom-controller/controller/, felhom-agent/ root.
Known flake: agent TestGenerateRecoveryCode_EntropyAndFormat fails ~1/5 (hyphenated wordlist word) —
re-run before diagnosing; it is not a regression.
Live validation doctrine (after unit-land)
Exercise the SERVER-SIDE PIPELINE a real user triggers, end-to-end — never hand-set state around it
(the F9 lesson). Invoking the exact endpoint the UI invokes is an acceptable proxy when a browser
isn't available; the residual is client-side rendering only — SAY which method was used. Test crash
behavior with kill -9 / OOM, never docker kill (containers' restart policy masks the difference).
Don't echo API keys/tokens into logs or REPORT — extract into shell vars, print lengths only.