Files
felhom.eu/hub/internal/api/deliberate_off_test.go
T
admin 50e47e29af
gates / gates (push) Successful in 6m40s
Hub v0.149.0: a box the household switched off is not an outage for 14 days (R-998)
hosts.deliberate_off_at follows the agent's deliberate_off field (set, then cleared by the first
report of a new boot; one info event each way). While held, every silence alarm is held: host and
node staleness, the 05:00 backup deadlines, off-site and restore-test freshness, the OS alarms and
the kernel notice. After 14 days the usual alarms fire once, naming the power-off. Pages show
KIKAPCSOLVA and the time. Consequence tests with controls; each guard red-proofed.
2026-10-13 19:47:02 +02:00

45 lines
1.8 KiB
Go

package api
import (
"net/http"
"strings"
"testing"
"time"
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
)
// R-998: the agent's deliberate_off field sets the host row's hold; the next report without it (a new boot — the box
// was switched back on) clears it. Each change leaves one info event on the customer's list; a repeat leaves none.
func TestHostReport_DeliberateOffSetThenCleared(t *testing.T) {
h, st, _ := newTestHandler(t)
st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c1", APIKey: "ckey", RetrievalPassword: "p"})
st.UpsertHost(&store.Host{HostID: "h1", CustomerID: "c1", APIKey: "HKEY"})
withOff := strings.TrimSuffix(validReportBody("h1"), "}") + `,"deliberate_off":{"requested_at":"2026-10-13T18:00:00Z"}}`
for i := 0; i < 2; i++ { // the field rides every report until the box is down; the second must not re-record
if rr := do(h, http.MethodPost, "/host-report", "HKEY", withOff); rr.Code != 200 {
t.Fatalf("report %d: %d %s", i, rr.Code, rr.Body.String())
}
}
at := st.HostDeliberateOffAt("h1")
if at == nil || !at.Equal(time.Date(2026, 10, 13, 18, 0, 0, 0, time.UTC)) {
t.Fatalf("hold = %v, want 2026-10-13 18:00 UTC", at)
}
evs, _ := st.GetEventsByType("c1", eventHostDeliberateOff, time.Now().Add(-time.Hour))
if len(evs) != 1 || evs[0].Severity != "info" {
t.Fatalf("set events = %+v, want exactly one info event", evs)
}
if rr := do(h, http.MethodPost, "/host-report", "HKEY", validReportBody("h1")); rr.Code != 200 {
t.Fatalf("plain report: %d", rr.Code)
}
if st.HostDeliberateOffAt("h1") != nil {
t.Fatal("a report without the field must clear the hold")
}
ended, _ := st.GetEventsByType("c1", eventHostDeliberateOffEnded, time.Now().Add(-time.Hour))
if len(ended) != 1 {
t.Fatalf("ended events = %d, want 1", len(ended))
}
}