Files
felhom.eu/hub/internal/api/host_swap_test.go
T

60 lines
2.5 KiB
Go

package api
import (
"net/http"
"strconv"
"strings"
"testing"
"time"
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
)
// R-1192 follow-up (night 2026-10-21): a host whose swap is below its guests' swap allowance raises host_swap_short
// ONCE (edge-triggered from the host's newest swap event, so repeats and a hub restart stay quiet), host_swap_ok once
// when covered, and an older agent (no numbers) is not evaluated. Operator-only (TestR1192_HostSwapEventsAreOperatorOnly
// in notify).
// RED-PROOF: with checkHostSwap not called from handleHostReport the first assertion fails (0 events).
func swapReportBody(hostID string, hostMB, guestMB int64) string {
b := validReportBody(hostID)
return strings.Replace(b, `"uptime_seconds":100}`, `"uptime_seconds":100,"host_swap_mb":`+strconv.FormatInt(hostMB, 10)+`,"guest_swap_mb":`+strconv.FormatInt(guestMB, 10)+`}`, 1)
}
func TestR1192_HostSwapShortIsRaisedOnceAndClearedOnce(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"})
post := func(body string) {
t.Helper()
if rr := do(h, http.MethodPost, "/host-report", "HKEY", body); rr.Code != http.StatusOK {
t.Fatalf("host-report = %d %s", rr.Code, rr.Body.String())
}
}
count := func(typ string) int {
evs, _ := st.GetEventsByType("c1", typ, time.Time{})
return len(evs)
}
post(validReportBody("h1")) // an older agent: no numbers → nothing
if count(eventHostSwapShort) != 0 {
t.Fatal("an agent that reports no swap numbers must not be evaluated")
}
post(swapReportBody("h1", 0, 4096)) // no host swap, a 4 GB guest allowance
post(swapReportBody("h1", 0, 4096))
if n := count(eventHostSwapShort); n != 1 {
t.Fatalf("host_swap_short events = %d, want exactly 1 (edge-triggered)", n)
}
evs, _ := st.GetEventsByType("c1", eventHostSwapShort, time.Time{})
if !strings.Contains(evs[0].Message, "Host h1:") || !strings.Contains(evs[0].DetailsJSON, `"guest_swap_mb":4096`) {
t.Fatalf("the alarm must name the host and both numbers: %q %s", evs[0].Message, evs[0].DetailsJSON)
}
post(swapReportBody("h1", 7935, 4096)) // the installer's 8 GB host
post(swapReportBody("h1", 7935, 4096))
if n := count(eventHostSwapOK); n != 1 {
t.Fatalf("host_swap_ok events = %d, want exactly 1", n)
}
post(swapReportBody("h1", 7935, 4096))
if count(eventHostSwapShort) != 1 {
t.Fatal("a covered host must not alarm again")
}
}