docs+skills: felhom-{build-deploy,ui-design,testing} skills + install_skills.py (junction); CLAUDE.md refresh (version-free); consolidated REPORT
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
---
|
||||
name: felhom-testing
|
||||
description: 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:
|
||||
|
||||
1. Temporarily restore the PRE-FIX shape (revert the fixed line, or model the old predicate inline).
|
||||
2. Run the test → it must **FAIL**, with the wrong value visible in the failure message.
|
||||
3. Restore the fix → test passes. `git diff` clean.
|
||||
4. Record the red-proof outcome in `REPORT.md` (what failed, with what value).
|
||||
|
||||
In-tree exemplars (verified):
|
||||
- `felhom.eu/hub/internal/notify/dispatcher_test.go` `TestSeverityNotifies` (~L27–49) — models the
|
||||
pre-fix `warning||error` predicate inline and asserts the fix routes what it dropped.
|
||||
- `felhom.eu/hub/internal/api/event_test.go` `TestHandleEvent_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`, localapi `Options` fakes,
|
||||
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)
|
||||
|
||||
```bash
|
||||
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.
|
||||
Reference in New Issue
Block a user