package server import ( "bytes" "html/template" "io/fs" "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" ) // TestOperatorTemplateRendersBothBranches parses operator.html and renders // both dispatch branches (curated landing surface and the MPA BodyTemplate // dispatch) to catch Go-template syntax errors that would otherwise only // surface at request time. The legacy tabstrip branch was retired in slice // 8 of operator-mpa-conversion §5. func TestOperatorTemplateRendersBothBranches(t *testing.T) { sub, err := fs.Sub(embeds.Templates, "templates") if err != nil { t.Fatalf("fs.Sub: %v", err) } partialsSub, err := fs.Sub(embeds.Templates, "templates/partials") if err != nil { t.Fatalf("fs.Sub partials: %v", err) } // operator.html references the `renderBody` and `routeURL` template // funcs and includes partials like operator_lookup_result.html via // `{{ template "..." . }}`. Mirror NewOperatorPartialsHandler: load // partials first, then the shell. tmpl := template.New("operator").Funcs(template.FuncMap{ "renderBody": func(string, any) (template.HTML, error) { return "", nil }, "routeURL": web.RouteURL, "fieldErr": func(_, _, _, _ string, _, _ any) string { return "" }, "stripeEntityURL": func(string, string) string { return "" }, "deploymentName": config.DeploymentName, }) tmpl, err = web.ParseUIPartials(template.Must(tmpl.ParseFS(partialsSub, "operator_*.html"))) if err != nil { t.Fatalf("ParseFS partials: %v", err) } tmpl, err = tmpl.ParseFS(sub, "operator.html") if err != nil { t.Fatalf("ParseFS operator.html: %v", err) } cases := []struct { name string data OperatorPageData want string }{ { name: "landing", data: OperatorPageData{ CSRFToken: "csrf", IAPosition: "runtime:landing", }, want: "Find a person or organization", }, { // BodyTemplate dispatch branch must short-circuit the landing // surface; the stub renderBody returns "" so the assertion is // that the shell still renders (no landing markup leaks in). name: "body-template-dispatch", data: OperatorPageData{ CSRFToken: "csrf", IAPosition: "runtime:organizations", ActiveCapability: "organizations", BodyTemplate: "operator_organizations.html", BodyData: nil, }, want: `id="operator-main"`, }, { // Dedicated Integrations section: the sidebar renders a labeled // "Integrations" home link to /operator/integrations. Provider // surfaces are reached from that home's provider cards, not from // sidebar children (maintainer 2026-08-23). name: "integration-section-home", data: OperatorPageData{ CSRFToken: "csrf", ActiveCapability: "integrations", }, want: `href="/operator/integrations">Integrations`, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { var buf bytes.Buffer if err := tmpl.ExecuteTemplate(&buf, "operator.html", tc.data); err != nil { t.Fatalf("ExecuteTemplate: %v", err) } out := buf.String() if !strings.Contains(out, tc.want) { t.Errorf("rendered output missing %q", tc.want) } if !strings.Contains(out, `hx-boost="true"`) { t.Errorf("rendered output missing hx-boost on body") } if !strings.Contains(out, `id="operator-main"`) { t.Errorf("rendered output missing main#operator-main swap target") } // Slice 8 guard: legacy tabstrip markup must not reappear. if strings.Contains(out, `id="operatorTabs"`) { t.Errorf("legacy tabstrip leaked back into operator.html — re-check §5 retirement") } }) } } // TestOperatorIntegrationsTemplate renders the Integrations landing body // partial directly (the operator.html smoke test stubs renderBody, so the body // markup is not exercised there). Covers a populated provider row and the // empty state. func TestOperatorIntegrationsTemplate(t *testing.T) { partialsSub, err := fs.Sub(embeds.Templates, "templates/partials") if err != nil { t.Fatalf("fs.Sub partials: %v", err) } tmpl := template.New("integrations").Funcs(template.FuncMap{ "renderBody": func(string, any) (template.HTML, error) { return "", nil }, "routeURL": web.RouteURL, "fieldErr": func(_, _, _, _ string, _, _ any) string { return "" }, "stripeEntityURL": func(string, string) string { return "" }, }) tmpl, err = web.ParseUIPartials(template.Must(tmpl.ParseFS(partialsSub, "operator_*.html"))) if err != nil { t.Fatalf("ParseFS partials: %v", err) } t.Run("populated", func(t *testing.T) { var buf bytes.Buffer data := IntegrationsData{Integrations: []IntegrationRow{{ Key: "example", DisplayName: "Example", Kind: "provisioning", Status: "active", SurfacePath: "/operator/example-sites", Operations: []string{"create", "delete", "set_status"}, ResourceKeys: []ResourceKeyOption{{ResourceKey: "example_sites", DisplayName: "Example sites"}}, }}} if err := tmpl.ExecuteTemplate(&buf, "operator_integrations.html", data); err != nil { t.Fatalf("ExecuteTemplate: %v", err) } out := buf.String() for _, want := range []string{"Example", "example_sites", "Example sites", "set_status", `href="/operator/example-sites"`, "Active"} { if !strings.Contains(out, want) { t.Errorf("integrations landing missing %q", want) } } }) t.Run("empty", func(t *testing.T) { var buf bytes.Buffer if err := tmpl.ExecuteTemplate(&buf, "operator_integrations.html", IntegrationsData{}); err != nil { t.Fatalf("ExecuteTemplate empty: %v", err) } if !strings.Contains(buf.String(), "No integrations registered") { t.Errorf("empty state not rendered") } }) }