package check import ( "strings" "testing" "testing/fstest" "khosra/internal/content" ) func run(t *testing.T, fsys fstest.MapFS) []Finding { t.Helper() bundles, problems, err := content.ScanReport(fsys) if err != nil { t.Fatal(err) } return Run(fsys, bundles, content.NewSite(bundles), problems) } // says reports whether any finding mentions the fragment, and whether it was fatal. func says(found []Finding, fragment string) (bool, bool) { for _, f := range found { if strings.Contains(f.What, fragment) || strings.Contains(f.Where, fragment) { return true, f.Fatal } } return false, false } func TestWhatTheEngineWorkedAroundIsReportedAsFatal(t *testing.T) { // The server never fails on these; it works around them and logs. `check` is the place that looks on // purpose (ADR-0029). found := run(t, fstest.MapFS{ "content/posts/broken.md": {Data: []byte("---\ntitle: [unclosed\n---\nx\n")}, "content/posts/twice.md": {Data: []byte("---\ntitle: One\n---\nx\n")}, "content/posts/twice/index.md": {Data: []byte("---\ntitle: Two\n---\nx\n")}, "content/posts/a.md": {Data: []byte("---\ntitle: A\nslug: taken\n---\nx\n")}, "content/posts/taken.md": {Data: []byte("---\ntitle: Taken\n---\nx\n")}, }) for _, fragment := range []string{"cannot be parsed", "same key", "slug ignored"} { said, fatal := says(found, fragment) if !said { t.Errorf("nothing reported %q:\n%v", fragment, found) continue } if !fatal { t.Errorf("%q is content the site is not serving, so it must be fatal", fragment) } } } func TestABrokenInternalLinkIsFatal(t *testing.T) { found := run(t, fstest.MapFS{ "content/posts/one.md": {Data: []byte("---\ntitle: One\n---\n" + "[good](/posts/two/) and [gone](/posts/never/) and [section](/posts/) and [alias](/posts/old/)\n")}, "content/posts/two.md": {Data: []byte("---\ntitle: Two\naliases: [posts/old]\n---\nx\n")}, }) if !brokenLink(found, "/posts/never/") { t.Errorf("a link that 404s must be reported:\n%v", found) } if !Fatal(found) { t.Error("a broken link makes the site wrong, so it must fail the check") } // Matched exactly, since "/posts/never/" contains "/posts/" — a substring test would pass either way. for _, fine := range []string{"/posts/two/", "/posts/", "/posts/old/"} { if brokenLink(found, fine) { t.Errorf("%s resolves — a bundle, a listing and an alias are all real:\n%v", fine, found) } } } // brokenLink reports whether exactly this target was called out, rather than one that merely contains it. func brokenLink(found []Finding, target string) bool { for _, f := range found { if strings.HasSuffix(f.What, "link goes nowhere: "+target) { return true } } return false } func TestEngineOwnedAndAssetLinksAreNotComplaints(t *testing.T) { found := run(t, fstest.MapFS{ "content/art/set/index.md": {Data: []byte("---\ntitle: Set\n---\n" + "[css](/static/x.css) [feed](/feed.xml) [tag](/tags/monsoon/) [home](/) " + "![pic](/art/set/one.jpg) ![missing](/art/set/nope.jpg)\n")}, "content/art/set/one.jpg": {Data: []byte("bytes")}, }) for _, quiet := range []string{"/static/x.css", "/feed.xml", "/tags/monsoon/", "/art/set/one.jpg"} { if brokenLink(found, quiet) { t.Errorf("%s should not be a finding:\n%v", quiet, found) } } if !brokenLink(found, "/art/set/nope.jpg") || !Fatal(found) { t.Errorf("a missing asset is a broken link:\n%v", found) } } func TestTitlesAltTextAndMixedOrderingAreWarnings(t *testing.T) { found := run(t, fstest.MapFS{ "content/posts/untitled.md": {Data: []byte("no frontmatter at all\n")}, "content/posts/pics.md": {Data: []byte("---\ntitle: Pics\n---\n" + "{{< figure src=\"a.jpg\" alt=\"A described picture\" >}}\n\n{{< figure src=\"b.jpg\" >}}\n")}, "content/comics/s/_index.md": {Data: []byte("---\ntitle: S\n---\nx\n")}, "content/comics/s/one.md": {Data: []byte("---\ntitle: One\norder: 10\n---\nx\n")}, "content/comics/s/two.md": {Data: []byte("---\ntitle: Two\n---\nx\n")}, }) for _, fragment := range []string{"no title", "no alt text", "sort last"} { said, fatal := says(found, fragment) if !said { t.Errorf("nothing reported %q:\n%v", fragment, found) continue } if fatal { t.Errorf("%q is untidy, not wrong — it must not fail the check", fragment) } } // The described figure is not a finding. if strings.Count(findingsAbout(found, "no alt text"), "\n") > 1 { t.Errorf("only the undescribed figure should be reported:\n%v", found) } if Fatal(found) { t.Errorf("nothing here is fatal, so the command should exit zero:\n%v", found) } } func findingsAbout(found []Finding, fragment string) string { var out strings.Builder for _, f := range found { if strings.Contains(f.What, fragment) { out.WriteString(f.What + "\n") } } return out.String() } func TestACleanSiteHasNothingToSay(t *testing.T) { found := run(t, fstest.MapFS{ "content/posts/one.md": {Data: []byte("---\ntitle: One\ndate: 2026-01-01\n---\nSee [two](/posts/two/).\n")}, "content/posts/two.md": {Data: []byte("---\ntitle: Two\ndate: 2026-01-02\n---\nx\n")}, "content/art/set/index.md": {Data: []byte("---\ntitle: Set\n---\n{{< figure src=\"one.jpg\" alt=\"Described\" >}}\n")}, "content/art/set/one.jpg": {Data: []byte("bytes")}, }) if len(found) != 0 { t.Errorf("a clean site should produce no findings, got:\n%v", found) } }