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) } }