Files
felhom.eu/hub/internal/web/r283_box_claim_test.go
T
admin daeed175ca R-283: customer page shows the box's own claim state beside the hub's, and warns on a mismatch
After a guest rebuild the hub (set-only claim, by design) said "Claimed" while the box showed its
first-run setup page. The latest report's `claimed` field is now shown; hub-claimed + box-not-claimed
renders an amber warning. No claim state is changed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-05 21:45:04 +02:00

71 lines
3.0 KiB
Go

package web
import (
"strings"
"testing"
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
)
// R-283: after a guest rebuild the hub's claim (set-only by design) says "Claimed" while the box serves
// its first-run setup page. The customer page now shows the BOX's side too, from its latest report, and
// warns on the mismatch. One render per branch of the template gate (seam rule): unknown, box claimed,
// box not claimed with an unclaimed hub, and the mismatch.
// RED-PROOF: drop the ClaimMismatch assignment in configs.go → the mismatch branch assertion fails.
func TestR283_CustomerPageShowsBoxClaimState(t *testing.T) {
s, st := newTestServer(t)
e, _ := withUIClaim(t, s, st)
cc := &store.CustomerConfig{CustomerID: "acme", CustomerName: "Acme", Domain: "acme.hu",
RetrievalPassword: "pw", APIKey: "k", Status: "active", Email: "owner@acme.hu"}
if err := st.SaveCustomerConfig(cc); err != nil {
t.Fatal(err)
}
if _, err := e.EnsureIssued(cc); err != nil {
t.Fatal(err)
}
// No report at all → unknown.
html := renderCustomerPage(t, s, "acme")
if !strings.Contains(html, `id="box-claim-unknown"`) || strings.Contains(html, `id="box-claim-mismatch"`) {
t.Fatal("no report: want the unknown line and no mismatch")
}
// An old controller that does not send the field → still unknown.
if err := st.SaveReport("acme", []byte(`{"customer_id":"acme"}`)); err != nil {
t.Fatal(err)
}
if html = renderCustomerPage(t, s, "acme"); !strings.Contains(html, `id="box-claim-unknown"`) {
t.Fatal("a report without the claimed field must read unknown, never not-claimed")
}
// Box not claimed, hub not claimed → neutral, no mismatch.
if err := st.SaveReport("acme", []byte(`{"customer_id":"acme","claimed":false}`)); err != nil {
t.Fatal(err)
}
html = renderCustomerPage(t, s, "acme")
if !strings.Contains(html, "Box: not claimed yet") || strings.Contains(html, `id="box-claim-mismatch"`) {
t.Fatal("unclaimed on both sides: want the neutral box line, no mismatch")
}
// Box claims → hub marked claimed; both say claimed.
if err := e.MarkClaimed(cc); err != nil {
t.Fatal(err)
}
if err := st.SaveReport("acme", []byte(`{"customer_id":"acme","claimed":true}`)); err != nil {
t.Fatal(err)
}
html = renderCustomerPage(t, s, "acme")
if !strings.Contains(html, "Box: claimed") || strings.Contains(html, `id="box-claim-mismatch"`) {
t.Fatal("claimed on both sides: want 'Box: claimed', no mismatch")
}
// The guest is rebuilt: the box reports claimed=false while the hub keeps its claim → mismatch.
if err := st.SaveReport("acme", []byte(`{"customer_id":"acme","claimed":false}`)); err != nil {
t.Fatal(err)
}
html = renderCustomerPage(t, s, "acme")
if !strings.Contains(html, `id="box-claim-mismatch"`) || !strings.Contains(html, ">Claimed ") {
t.Fatal("after a rebuild: want the hub's Claimed chip AND the box mismatch warning")
}
// Showing it changes nothing: the hub's claim is still set (set-only, by design).
if cs, _ := st.GetClaim("acme"); cs == nil || cs.ClaimedAt == nil {
t.Fatal("rendering the page un-claimed the hub")
}
}