// DB-free render coverage for the operator Domains moderation list's // force-release affordance: offered exactly where the registry would admit // it. A rendered control the server always refuses is the dead-end-affordance // failure mode (issues.md 2026-07-26 — the domains walkthrough timed out on // a placed claim's button). package server import ( "html/template" "io/fs" "log/slog" "net/http/httptest" "strings" "testing" "git.coopcloud.tech/wiki-cafe/member-console/internal/embeds" "git.coopcloud.tech/wiki-cafe/member-console/internal/web" ) func renderOperatorDomains(t *testing.T, data OperatorDomainsData) string { t.Helper() sub, err := fs.Sub(embeds.Templates, "templates/partials") if err != nil { t.Fatalf("sub templates FS: %v", err) } tmpl, err := template.New("operator-domains").Funcs(template.FuncMap{ "routeURL": web.RouteURL, }).ParseFS(sub, "operator_domains.html") if err != nil { t.Fatalf("parse operator_domains.html: %v", err) } rec := httptest.NewRecorder() NewSafeTemplates(tmpl, slog.Default()).Render(rec, "operator_domains.html", data) if rec.Code != 200 { t.Fatalf("render = %d: %s", rec.Code, rec.Body.String()) } return rec.Body.String() } // TestOperatorDomainsForceReleaseOnlyWhereAdmissible pins the affordance to // the registry's own guards: unplaced non-root claims get the button; placed // claims read "in use"; operator roots read "operator root" even when they // hold placements (roots always do — every hosted site places under one). func TestOperatorDomainsForceReleaseOnlyWhereAdmissible(t *testing.T) { body := renderOperatorDomains(t, OperatorDomainsData{ Claims: []OperatorDomainClaimRow{ {ClaimID: "claim-free", Root: "spare.example.org", Kind: "external", Status: "active", Releasable: true}, {ClaimID: "claim-used", Root: "busy.example.org", Kind: "external", Status: "active", Placements: 2, Placed: true}, {ClaimID: "claim-root", Root: "hosting.test", Kind: "operator_root", Status: "active", Placements: 5}, }, }) if !strings.Contains(body, "/partials/operator/domains/claim-free/force-release") { t.Error("unplaced non-root claim must offer force-release") } if strings.Contains(body, "/partials/operator/domains/claim-used/force-release") { t.Error("placed claim must not offer force-release — the registry's placement guard refuses it") } if strings.Contains(body, "/partials/operator/domains/claim-root/force-release") { t.Error("operator root must not offer force-release") } if !strings.Contains(body, ">in use<") { t.Error("placed claim should be labeled 'in use'") } if !strings.Contains(body, ">operator root<") { t.Error("operator root should keep its label even when it holds placements") } if got := strings.Count(body, "Force-release {{"); got != 0 { t.Errorf("unexecuted template fragments in output: %d", got) } }