- Add deployment-name branding to titles, mastheads, and OG tags - Share one grant delivery-state query with lineage across grants surfaces - Show pool status/usage, org owners, and config readiness - Make billing views projection-aware with recency and sync vocabulary - Guard FedWiki creation without domains and render route-aware 404s
130 lines
4.2 KiB
Go
130 lines
4.2 KiB
Go
package server
|
|
|
|
import (
|
|
"html/template"
|
|
"io/fs"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/config"
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/embeds"
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/web"
|
|
"log/slog"
|
|
)
|
|
|
|
// indexPageData mirrors the anonymous struct the "/" handler feeds
|
|
// index.html (server.go); keep the field sets in sync.
|
|
type indexPageData struct {
|
|
Name string
|
|
Username string
|
|
Email string
|
|
KeycloakAccountURL string
|
|
CSRFToken string
|
|
IsOperator bool
|
|
HasMultipleWorkspaces bool
|
|
CheckoutStatus string
|
|
DashboardCards []DashboardCard
|
|
PendingDomainClaims []PendingDomainClaim
|
|
}
|
|
|
|
func renderIndex(t *testing.T, data indexPageData) string {
|
|
t.Helper()
|
|
sub, err := fs.Sub(embeds.Templates, "templates")
|
|
if err != nil {
|
|
t.Fatalf("sub templates FS: %v", err)
|
|
}
|
|
// index.html references no other templates, so it parses standalone; the
|
|
// pending-domain notice uses routeURL, registered for real.
|
|
tmpl, err := template.New("root").Funcs(template.FuncMap{
|
|
"routeURL": web.RouteURL,
|
|
"deploymentName": config.DeploymentName,
|
|
}).ParseFS(sub, "index.html")
|
|
if err != nil {
|
|
t.Fatalf("parse index.html: %v", err)
|
|
}
|
|
rec := httptest.NewRecorder()
|
|
NewSafeTemplates(tmpl, slog.Default()).Render(rec, "index.html", data)
|
|
if rec.Code != 200 {
|
|
t.Fatalf("render = %d, body: %s", rec.Code, rec.Body.String())
|
|
}
|
|
return rec.Body.String()
|
|
}
|
|
|
|
// The raw template bytes must be integration-free too — a rendered-output
|
|
// check could miss a reference hidden behind an untaken template branch.
|
|
func TestIndexTemplateNamesNoIntegration(t *testing.T) {
|
|
raw, err := fs.ReadFile(embeds.Templates, "templates/index.html")
|
|
if err != nil {
|
|
t.Fatalf("read embedded index.html: %v", err)
|
|
}
|
|
for _, banned := range []string{"fedwiki", "discourse", "stripe"} {
|
|
if strings.Contains(strings.ToLower(string(raw)), banned) {
|
|
t.Errorf("core index.html template references integration %q", banned)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The dashboard is registry-driven: core's index.html must not name any
|
|
// integration, and with no declared cards it must render no card section
|
|
// chrome and no integration script tags.
|
|
func TestIndexNoDashboardCards(t *testing.T) {
|
|
body := renderIndex(t, indexPageData{CSRFToken: "tok"})
|
|
|
|
if strings.Contains(strings.ToLower(body), "fedwiki") {
|
|
t.Errorf("index.html without cards still references fedwiki")
|
|
}
|
|
if strings.Contains(strings.ToLower(body), "discourse") {
|
|
t.Errorf("index.html without cards references discourse")
|
|
}
|
|
if strings.Contains(body, "hx-trigger=\"load, ") {
|
|
t.Errorf("card shell rendered despite zero declared cards")
|
|
}
|
|
}
|
|
|
|
// Declared cards render as generic shells in declaration (registry) order,
|
|
// with the declared partial path, trigger spec, and page-level deferred
|
|
// script tags.
|
|
func TestIndexRendersDeclaredCards(t *testing.T) {
|
|
body := renderIndex(t, indexPageData{
|
|
CSRFToken: "tok",
|
|
DashboardCards: []DashboardCard{
|
|
{
|
|
Title: "Stub Sites",
|
|
PartialPath: "/partials/stub/sites",
|
|
RefreshEvent: "refreshStub",
|
|
Scripts: []string{"/static/stub/stub-form.js"},
|
|
},
|
|
{
|
|
Title: "Other Service",
|
|
PartialPath: "/partials/other/status",
|
|
},
|
|
},
|
|
})
|
|
|
|
first := strings.Index(body, "Stub Sites")
|
|
second := strings.Index(body, "Other Service")
|
|
if first == -1 || second == -1 {
|
|
t.Fatalf("declared card titles missing (first=%d second=%d)", first, second)
|
|
}
|
|
if first > second {
|
|
t.Errorf("cards rendered out of declaration order")
|
|
}
|
|
if !strings.Contains(body, `hx-get="/partials/stub/sites"`) ||
|
|
!strings.Contains(body, `hx-get="/partials/other/status"`) {
|
|
t.Errorf("card body hx-get partial paths missing")
|
|
}
|
|
if !strings.Contains(body, `hx-trigger="load, refreshStub from:body"`) {
|
|
t.Errorf("refresh-event trigger spec missing")
|
|
}
|
|
if !strings.Contains(body, `hx-trigger="load"`) {
|
|
t.Errorf("plain load trigger missing for card without refresh event")
|
|
}
|
|
if !strings.Contains(body, `<script defer src="/static/stub/stub-form.js"></script>`) {
|
|
t.Errorf("declared card script not rendered as page-level deferred tag")
|
|
}
|
|
if strings.Contains(strings.ToLower(body), "fedwiki") {
|
|
t.Errorf("generic shell leaked an integration name")
|
|
}
|
|
}
|