package web import ( "net/http" "net/http/httptest" "os" "strings" "testing" "github.com/yuin/goldmark" "khosra/internal/content" "khosra/internal/ext/shortcodes" "khosra/internal/render" ) // exampleSite serves examples/demo-site the way the binary does. // // The real site root, not a fixture: this is what makes the demo a test rather than a brochure. When a feature // changes shape and the example stops showing it, this fails — which is the only way a demo stays current // (ADR-0051). func exampleSite(t *testing.T) http.Handler { t.Helper() const dir = "../../examples/demo-site" if _, err := os.Stat(dir); err != nil { t.Fatalf("the example site is missing: %v", err) } fsys := os.DirFS(dir) bundles, problems, err := content.ScanReport(fsys) if err != nil { t.Fatal(err) } if len(problems) != 0 { t.Fatalf("the engine cannot read its own example site: %v", problems) } settings, err := content.LoadSettings(fsys) if err != nil { t.Fatal(err) } // Derivatives into a throwaway directory: the example must never gain generated files of its own. if _, err := shortcodes.Derive(fsys, t.TempDir()); err != nil { t.Fatal(err) } r, err := render.New(fsys, settings, func(p render.Partial) []goldmark.Extender { return []goldmark.Extender{shortcodes.New(p)} }) if err != nil { t.Fatal(err) } site := content.NewSite(bundles) r.Navigation(site.Sections) return Handler(Fixed(site), r, fsys, nil, settings) } func get(t *testing.T, h http.Handler, path string) (int, string) { t.Helper() rec := httptest.NewRecorder() h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, path, nil)) return rec.Code, rec.Body.String() } // featureCase is one thing the example site must still show. type featureCase struct { what, path string code int expect []string absent []string } // exampleFeatures is the demo's contract with the engine: one case per feature, each naming what it proves. // // A feature added without a case here is a feature the demo does not show; a case that fails is a demo that has // gone stale. Both are the point, and both are why this is a table rather than prose in a README (ADR-0051). var exampleFeatures = []featureCase{ {what: "the root lists everything", path: "/", code: 200, expect: []string{"A Khosra Demo", `href="/comics/`, `href="/writing/`}}, {what: "a section listing paginates", path: "/posts/", code: 200, expect: []string{`rel="next" href="/posts/page/2/"`, "Day 11"}}, {what: "page two links back", path: "/posts/page/2/", code: 200, expect: []string{`rel="prev" href="/posts/"`}}, {what: "a site template overrides one block and inherits the document", path: "/posts/", code: 200, expect: []string{"rendered by the site's own template", "", `class="entry"`}}, {what: "navigation, feed discovery and OpenGraph are in the head", path: "/posts/first-light/", code: 200, expect: []string{`