package web import ( "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "gitea.dooplex.hu/admin/felhom-controller/internal/stacks" ) // R-1028: the claim box renders only when the household still has to give the code — render test // per branch (the seam-wiring rule). // // v0.315.0: the box is no longer Plex-specific markup behind a `.PlexClaim` flag. It is the GENERIC // after-start card, and the thing it branches on is whether afterStartFields put the field in the // page data at all — which is where the „registered action AND Needed()" decision now lives. // COMPANION RED-PROOF: render the card unconditionally (drop the range over .AfterStartFields) → // the two negative cases fail. Restored. func TestPlexClaimBox_RendersPerBranch(t *testing.T) { st := lcStack("plex", "", true) st.State = stacks.StateRunning claimField := stacks.DeployField{EnvVar: "PLEX_CLAIM", Label: "Plex kod", AfterStart: true, Placeholder: "claim-xxxxxxxxxxxxxxxxxxxx"} for _, c := range []struct { name string fields []stacks.DeployField want bool }{ {"the code is still wanted", []stacks.DeployField{claimField}, true}, {"nothing is wanted", nil, false}, } { d := appInfoData(st) d["AfterStartFields"] = c.fields h := renderBackupPage(t, "app_settings", d) if got := strings.Contains(h, `data-afterstart="PLEX_CLAIM"`) && strings.Contains(h, `id="after-start-PLEX_CLAIM"`); 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) } }