package web import ( "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "gitea.dooplex.hu/admin/felhom-controller/internal/stacks" ) // R-1028: the Plex claim box renders only for a closed Plex gate — render test per branch (the seam-wiring rule). // COMPANION RED-PROOF: remove the {{if .PlexClaim}} block → the first case fails. Restored. func TestPlexClaimBox_RendersPerBranch(t *testing.T) { st := lcStack("plex", "", true) st.State = stacks.StateRunning for _, c := range []struct { name string plexClaim bool gateClosed bool want bool }{{"closed plex", true, true, true}, {"gate open", false, false, false}, {"other app closed", false, true, false}} { d := appInfoData(st) d["SetupGateClosed"] = c.gateClosed d["PlexClaim"] = c.plexClaim h := renderBackupPage(t, "app_info", d) if got := strings.Contains(h, `data-plex-claim="true"`) && strings.Contains(h, `id="plex-claim-code"`); got != c.want { t.Errorf("%s: claim box shown=%v, want %v", c.name, got, c.want) } } } // The code is checked for its shape before any call: no call for something that is not a claim code. func TestPlexClaimCodeShape(t *testing.T) { for code, ok := range map[string]bool{"claim-AbCdEf123456789_-xyz": true, "claim-short": false, "claim-abcdefgh": true, "claim-": false, "https://plex.tv/claim": false, "claim-abc def": false, "": false} { if got := plexClaimCode.MatchString(code); got != ok { t.Errorf("%q: accepted=%v, want %v", code, got, ok) } } } // R-1045: the household's „Kész" never opens an UNCLAIMED Plex (it answers everything to a private address with no // token); once Plex says claimed, the press opens it. No template probe is needed (an old controller would refuse the // press forever with one). COMPANION RED-PROOF: drop the plexClaimed check in appSetupGateOpenHandler → the first press // opens the gate (200). Restored. func TestPlexDonePress_RefusedWhileUnclaimed(t *testing.T) { s := gateHarness(t) app := filepath.Join(s.cfg.Paths.StacksDir, "gapp") if err := os.WriteFile(filepath.Join(app, ".felhom.yml"), []byte("display_name: Plex\nslug: plex\nsetup_gate: true\n"), 0o644); err != nil { t.Fatal(err) } if err := s.stackMgr.ScanStacks(); err != nil { t.Fatal(err) } claimed := false old := plexClaimed plexClaimed = func() bool { return claimed } defer func() { plexClaimed = old }() press := func() int { w := httptest.NewRecorder() s.appSetupGateOpenHandler(w, httptest.NewRequest(http.MethodPost, "/apps/plex/setup-gate/open", nil), "plex") return w.Code } if c := press(); c != http.StatusConflict { t.Fatalf("„Kész” on an unclaimed Plex: HTTP %d, want 409 — the gate must stay closed", c) } claimed = true if c := press(); c != http.StatusOK { t.Fatalf("„Kész” on a claimed Plex: HTTP %d, want 200", c) } }