Files
felhom.eu/hub/internal/web/deliberate_off_view_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

64 lines
2.2 KiB
Go

package web
import (
"database/sql"
"io"
"log"
"net/http"
"net/http/httptest"
"path/filepath"
"strings"
"testing"
"time"
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
)
// R-998: a box the household switched off reads KIKAPCSOLVA with the power-off line on the hosts page — not DOWN —
// exactly while the alarms are held; after the 14-day hold it reads DOWN again.
func TestHostsPage_DeliberatelyOffBox(t *testing.T) {
path := filepath.Join(t.TempDir(), "t.db")
st, err := store.New(path, log.New(io.Discard, "", 0))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { st.Close() })
s := New(st, "", "", "test", 30*time.Minute, log.New(io.Discard, "", 0))
st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c1", APIKey: "ck", RetrievalPassword: "p"})
st.UpsertHost(&store.Host{HostID: "h1", CustomerID: "c1", APIKey: "k1"})
st.SaveHostReport("h1", "c1", []byte(`{}`), store.HostReportDenorm{})
age := func(d time.Duration) {
db, _ := sql.Open("sqlite", path)
defer db.Close()
db.Exec(`UPDATE hosts SET last_report_at = ? WHERE host_id = 'h1'`, time.Now().UTC().Add(-d).Format("2006-01-02 15:04:05"))
}
page := func() string {
rr := httptest.NewRecorder()
s.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/hosts", nil))
if rr.Code != 200 {
t.Fatalf("/hosts: %d", rr.Code)
}
return rr.Body.String()
}
age(3 * time.Hour)
if p := page(); !strings.Contains(p, ">DOWN<") || strings.Contains(p, "data-deliberate-off") {
t.Fatal("CONTROL: a silent box without a power-off must read DOWN")
}
at := time.Now().UTC().Add(-3 * time.Hour).Truncate(time.Second)
st.SetHostDeliberateOff("h1", &at)
p := page()
if !strings.Contains(p, ">KIKAPCSOLVA<") || strings.Contains(p, ">DOWN<") {
t.Fatal("a box switched off by the household must read KIKAPCSOLVA, not DOWN")
}
if !strings.Contains(p, "Kikapcsolva a háztartás által, "+at.Format("2006-01-02 15:04")) {
t.Fatal("the hosts page does not name the household power-off and its time")
}
old := time.Now().UTC().Add(-15 * 24 * time.Hour)
st.SetHostDeliberateOff("h1", &old)
age(15 * 24 * time.Hour)
if p := page(); !strings.Contains(p, ">DOWN<") {
t.Fatal("after the 14-day hold the box must read DOWN again")
}
}