Files
felhom.eu/hub/internal/api/r366_foreign_key_archives_test.go

74 lines
3.1 KiB
Go

package api
import (
"net/http"
"strings"
"testing"
"time"
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
)
// R-366 slice 2 (`09` §3 decision 168): archives the box's restore-test skipped as written with ANOTHER key reach the
// operator as ONE line per change of the set — not per archive, not per report; never mailed (info), never the
// household's.
func reportWithForeign(foreign string) string {
body := validReportBody("h1")
return strings.TrimSuffix(body, "}") + `,"foreign_key_archives":{"tiers":` + foreign + `}}`
}
func foreignEvents(t *testing.T, st *store.Store) []store.Event {
t.Helper()
ev, err := st.GetEventsByType("c1", eventForeignKeyArchives, time.Now().Add(-time.Hour))
if err != nil {
t.Fatal(err)
}
return ev
}
// COMPANION RED-PROOF (observed): remove the noteForeignKeyArchives call from handleHostReport → this fails with
// "a new set of other-key archives must record exactly one operator event; got 0". Restored.
func TestR366_ForeignKeyArchivesOneEventPerChange(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"})
set := `[{"target":"felhom-pbs","count":2,"oldest":"2026-08-18T03:58:43Z","newest":"2026-08-19T03:58:43Z"}]`
for i := 0; i < 3; i++ { // the same set on three reports → one event
if rr := do(h, http.MethodPost, "/host-report", "HKEY", reportWithForeign(set)); rr.Code != http.StatusOK {
t.Fatalf("report %d: %d %s", i, rr.Code, rr.Body.String())
}
}
ev := foreignEvents(t, st)
if len(ev) != 1 {
t.Fatalf("a new set of other-key archives must record exactly one operator event; got %d", len(ev))
}
if ev[0].Severity != "info" || !strings.Contains(ev[0].Message, "2 whole-guest backup archive(s)") ||
!strings.Contains(ev[0].Message, "felhom-pbs (2026-08-18 … 2026-08-19)") {
t.Fatalf("the line must name the count, the tier and the date range; got %s %q", ev[0].Severity, ev[0].Message)
}
// An older agent or a fresh restart (the stanza absent) keeps the state: no new event.
do(h, http.MethodPost, "/host-report", "HKEY", validReportBody("h1"))
if n := len(foreignEvents(t, st)); n != 1 {
t.Fatalf("an absent stanza must keep the state; got %d events", n)
}
// The set changes (a third archive) → one more line.
do(h, http.MethodPost, "/host-report", "HKEY", reportWithForeign(`[{"target":"felhom-pbs","count":3,"oldest":"2026-08-18T03:58:43Z","newest":"2026-08-25T03:58:43Z"}]`))
if n := len(foreignEvents(t, st)); n != 2 {
t.Fatalf("a changed set must record one more line; got %d events", n)
}
// Evaluated, none left → the state clears silently; the same set coming back later is news again.
do(h, http.MethodPost, "/host-report", "HKEY", reportWithForeign(`[]`))
if n := len(foreignEvents(t, st)); n != 2 {
t.Fatalf("an empty list clears without an event; got %d", n)
}
do(h, http.MethodPost, "/host-report", "HKEY", reportWithForeign(set))
if n := len(foreignEvents(t, st)); n != 3 {
t.Fatalf("after a clear, a set is news again; got %d", n)
}
}