Files
felhom-controller/controller/cmd/controller/r399_no_event_test.go
T
admin 3c49dc8ea4
gates / gates (push) Successful in 12s
v0.228.0 — the off-site check reads the data; the debug page stops lying (R-399 + R-400)
R-399: monitoring.integrity.read_data_subset defaults to 100%. A pack damaged
without changing its size made plain `restic check` report "no errors were found"
on demo-hp 2026-08-30; every read-data form caught it. Cost on that 134 MB store:
35.0s structure vs 39.2s at 100%. "off" (any case) is the off token; empty means
not-configured, therefore the default; a malformed value falls back to the DEFAULT,
never to structure. A completed check over 5 minutes logs a WARN naming the
duration, the depth and R-401 — operator log only, no hub event, no depth change.
The depth is now recorded with the verdict (LastIntegrityDepth; empty = NOT
RECORDED, never "structure").

R-400: 24 debug-page references, 17 dispatched, 7 dead — three of which fetched on
page LOAD, so those panels were permanently blank. backup/crossdrive implemented;
backup/infra, hub/infra-push, dr/infra-status, storage/watchdog-status and both
storage/simulate-* deleted with their panels and JavaScript.
scripts/debug_route_gate.py fails in both directions and is registered after the
seven were resolved. 18 referenced, 18 dispatched, none orphaned.

Corrections: the dead-field warning in report/types.go said the controller runs no
integrity check and the notifiers are called from nowhere — both false since
v0.227.0. controller.yaml.example gains its missing integrity: block.
integrityCheckTimeout's "ships OFF" comment rewritten.
2026-08-31 10:24:29 +02:00

72 lines
2.4 KiB
Go

package main
import (
"go/ast"
"go/parser"
"go/token"
"testing"
)
// B6 — R-399/R-401: SLOWNESS RAISES NO HUB EVENT. This asserts a NON-EFFECT.
//
// A "this took a while" event type would cost the severity contract, the grain table and three
// registers, and 08 §6.2's coarse-by-default rule points the other way. Worse, this project's
// dispatcher coerces any severity outside {info,warning,error,critical} to `info` and mails nobody
// while still returning 200 — so an event added carelessly here would be indistinguishable from one
// that works.
//
// AST, not strings.Contains: a commented-out call still contains the string, and this repo has a
// recorded case of a text-based wiring test passing the very red-proof it existed to fail.
func TestR399_SlownessRaisesNoHubEvent(t *testing.T) {
body := funcBody(t, "runOffsiteIntegrityCheck")
// Every notifier method the one integrity caller invokes.
var notifies []string
ast.Inspect(body, func(n ast.Node) bool {
call, ok := n.(*ast.CallExpr)
if !ok {
return true
}
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
return true
}
if len(sel.Sel.Name) >= 6 && sel.Sel.Name[:6] == "Notify" {
notifies = append(notifies, sel.Sel.Name)
}
return true
})
// POSITIVE CONTROL. If this list is empty the test proves nothing — it would pass just as happily
// against a file it failed to walk.
allowed := map[string]bool{"NotifyIntegrityOK": true, "NotifyIntegrityFailed": true}
if len(notifies) == 0 {
t.Fatal("no Notify* call was found in runOffsiteIntegrityCheck — the walk found nothing, so " +
"the non-effect below would be asserted against an empty set and would pass vacuously")
}
for _, name := range notifies {
if !allowed[name] {
t.Errorf("runOffsiteIntegrityCheck calls %s — slowness and depth are OPERATOR notices in "+
"the log, and the only two customer-facing notifications this job may raise are the "+
"integrity pass and the integrity failure", name)
}
}
}
// funcBody returns the body of a named top-level function in main.go.
func funcBody(t *testing.T, name string) *ast.BlockStmt {
t.Helper()
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "main.go", nil, 0)
if err != nil {
t.Fatalf("parse main.go: %v", err)
}
for _, decl := range f.Decls {
if fn, ok := decl.(*ast.FuncDecl); ok && fn.Name.Name == name && fn.Body != nil {
return fn.Body
}
}
t.Fatalf("func %s not found in main.go", name)
return nil
}