Files
felhom.eu/hub/internal/monitor/r723_new_box_test.go
T

59 lines
2.1 KiB
Go

package monitor
import (
"database/sql"
"testing"
"time"
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
)
func hostFor(t *testing.T, st *store.Store, path string, createdAgo time.Duration) {
t.Helper()
if err := st.UpsertHost(&store.Host{HostID: "c1-abc123", CustomerID: "c1", APIKey: "hk"}); err != nil {
t.Fatal(err)
}
db, err := sql.Open("sqlite", path)
if err != nil {
t.Fatal(err)
}
defer db.Close()
if _, err := db.Exec(`UPDATE hosts SET created_at = ? WHERE host_id = 'c1-abc123'`,
time.Now().UTC().Add(-createdAgo).Format("2006-01-02 15:04:05")); err != nil {
t.Fatal(err)
}
}
// R-723 (v0.126.0) — the 2026-09-29 shape: the customer's previous box went silent 12 days ago (the hub
// seeds the customer as down), a NEW box enrolls and sends its first report. The CONSEQUENCE asserted: no
// `node_recovered` (it is the operator mail „recovered" for an outage the new box never had).
// COMPANION RED-PROOF: make isNewBox return false → the event list is [node_recovered].
func TestR723_ANewBoxIsNotARecovery(t *testing.T) {
st, path := seedStalenessCustomer(t, "ok", 12*24*time.Hour)
sc, events := newChecker(t, st) // seeds c1 as down (the old box's silence)
if sc.GetState("c1") != "down" {
t.Fatalf("setup: want down, got %q", sc.GetState("c1"))
}
hostFor(t, st, path, 2*time.Minute) // the new box enrolled 2 min ago
saveReportAged(t, st, path, "ok", 0) // …and reported
sc.Check()
if len(*events) != 0 {
t.Fatalf("a new box's first report emitted %v", *events)
}
if sc.GetState("c1") != "ok" {
t.Fatalf("state %q, want ok", sc.GetState("c1"))
}
}
// Control: the SAME box, enrolled long ago, coming back after an outage IS a recovery and still mails.
func TestR723_AnOldBoxComingBackIsStillARecovery(t *testing.T) {
st, path := seedStalenessCustomer(t, "ok", 12*24*time.Hour)
sc, events := newChecker(t, st)
hostFor(t, st, path, 40*24*time.Hour)
saveReportAged(t, st, path, "ok", 0)
sc.Check()
if len(*events) != 1 || (*events)[0] != "node_recovered" {
t.Fatalf("a real recovery must still emit node_recovered, got %v", *events)
}
}