Files
member-console/internal/server/member_entitlements_render_test.go
T
cgalo5758 2816c1e0b0 Make member dashboard registry-driven
Render generic HTMX card shells from integration declarations. Move the
FedWiki UI into its partial, add Discourse forum status, and label
member
entitlements with provider attribution.
2026-07-21 22:50:52 -05:00

108 lines
3.6 KiB
Go

package server
import (
"html/template"
"io/fs"
"net/http/httptest"
"strings"
"testing"
"git.coopcloud.tech/wiki-cafe/member-console/internal/embeds"
"log/slog"
)
func renderEntitlements(t *testing.T, data EntitlementsData) string {
t.Helper()
sub, err := fs.Sub(embeds.Templates, "templates")
if err != nil {
t.Fatalf("sub templates FS: %v", err)
}
tmpl, err := template.New("root").ParseFS(sub, "partials/member_entitlements.html")
if err != nil {
t.Fatalf("parse member_entitlements.html: %v", err)
}
rec := httptest.NewRecorder()
NewSafeTemplates(tmpl, slog.Default()).Render(rec, "member_entitlements.html", data)
if rec.Code != 200 {
t.Fatalf("render = %d, body: %s", rec.Code, rec.Body.String())
}
return rec.Body.String()
}
// Resource rows lead with the human label and integration attribution;
// the raw key is demoted to a title attribute. Two integrations'
// entitlements stay distinguishable by their attributions.
func TestEntitlementRowsLabeledAndAttributed(t *testing.T) {
body := renderEntitlements(t, EntitlementsData{
HasEntitlements: true,
Entitlements: []EntitlementViewModel{
{ResourceKey: "alpha_sites", Label: "Wiki Sites", IntegrationName: "Alpha", ResourceLimit: 2, CurrentUsage: 1},
{ResourceKey: "beta_widgets", Label: "Widgets", IntegrationName: "Beta", ResourceLimit: 5, CurrentUsage: 0},
},
})
for _, want := range []string{
`title="alpha_sites"`, ">Wiki Sites", "Alpha",
`title="beta_widgets"`, ">Widgets", "Beta",
} {
if !strings.Contains(body, want) {
t.Errorf("rendered entitlements missing %q", want)
}
}
// The raw key appears only as the demoted title attribute, never as
// headline text content.
if strings.Contains(body, ">alpha_sites<") {
t.Errorf("raw resource key rendered as headline text")
}
}
// A key with no display metadata falls back to the raw key, un-attributed
// (labelFor's contract) — and the view still renders.
func TestEntitlementLabelFallback(t *testing.T) {
got := labelFor(map[string]resourceLabel{}, "mystery_key")
if got.Label != "mystery_key" || got.IntegrationName != "" {
t.Errorf("labelFor fallback = %+v, want raw key un-attributed", got)
}
// Present but empty display name also falls back rather than
// rendering a blank headline.
got = labelFor(map[string]resourceLabel{"k": {Label: ""}}, "k")
if got.Label != "k" {
t.Errorf("empty display name should fall back to raw key, got %+v", got)
}
body := renderEntitlements(t, EntitlementsData{
HasEntitlements: true,
Entitlements: []EntitlementViewModel{{ResourceKey: "mystery_key", Label: "mystery_key", ResourceLimit: 1}},
})
if !strings.Contains(body, ">mystery_key") {
t.Errorf("fallback raw key not rendered")
}
}
// Granted booleans render as labeled "Included" rows without a usage
// meter; with no granted booleans, no Included badge appears (the handler
// filters lapsed rows server-side, so the template only ever sees granted
// ones).
func TestBooleanEntitlementRows(t *testing.T) {
body := renderEntitlements(t, EntitlementsData{
HasEntitlements: true,
BooleanEntitlements: []BooleanEntitlementViewModel{
{ResourceKey: "gamma_forum_access", Label: "Forum Access", IntegrationName: "Gamma"},
},
})
for _, want := range []string{">Forum Access", "Gamma", ">Included<", `title="gamma_forum_access"`} {
if !strings.Contains(body, want) {
t.Errorf("boolean row missing %q", want)
}
}
if strings.Contains(body, "<meter") {
t.Errorf("boolean-only view must not render a usage meter")
}
empty := renderEntitlements(t, EntitlementsData{HasEntitlements: true})
if strings.Contains(empty, ">Included<") {
t.Errorf("Included badge rendered with no granted booleans")
}
}