75 lines
2.8 KiB
Go
75 lines
2.8 KiB
Go
// 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)
|
|
}
|
|
}
|