test(11.1-06): branch-level tests for every docsite stage (94.8% coverage)

- load, render, emit, snippet, highlight, serve and checker branch tests;
  docs:build --check, docs:sync and docs:serve output tests in cmd/summer
- fix: a CRLF or BOM page is reported as such, not as a page without a
  frontmatter block (TestFrontmatterEncodingProblems)
- fix: src=#Type.Method finds a method with a parenthesised receiver
  (TestSnippetGenericsAndGroups, Box.Paren)
- fix: docs:serve adds its watches before announcing the server, so an
  edit made right after the serving line rebuilds (TestServeWatchRebuilds)
This commit is contained in:
Jakub Zych
2026-10-01 00:05:12 +02:00
parent 1d38e73f03
commit 28afd4d197
13 changed files with 2276 additions and 11 deletions

View File

@@ -409,3 +409,160 @@ func TestDocsCommandsMirrorGeneratedMain(t *testing.T) {
}
}
}
// cleanDocsFixture is the internal/docsite fixture that passes every check.
const cleanDocsFixture = "../../internal/docsite/testdata/clean"
// scratchDocsRoot copies the clean docs fixture into a temp dir.
func scratchDocsRoot(t *testing.T) string {
t.Helper()
root := t.TempDir()
err := filepath.WalkDir(cleanDocsFixture, func(p string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
rel, _ := filepath.Rel(cleanDocsFixture, p)
target := filepath.Join(root, rel)
if d.IsDir() {
return os.MkdirAll(target, 0o755)
}
data, err := os.ReadFile(p)
if err != nil {
return err
}
return os.WriteFile(target, data, 0o644)
})
if err != nil {
t.Fatal(err)
}
return root
}
// runSummer runs the summer tool with args and returns its output.
func runSummer(t *testing.T, args ...string) (string, error) {
t.Helper()
var buf bytes.Buffer
root, err := bonfire.NewRoot("summer", toolCommands(), &buf)
if err != nil {
t.Fatal(err)
}
root.SetArgs(args)
err = root.Execute()
return buf.String(), err
}
func appendDoc(t *testing.T, root, name, text string) {
t.Helper()
f, err := os.OpenFile(filepath.Join(root, filepath.FromSlash(name)), os.O_APPEND|os.O_WRONLY, 0)
if err != nil {
t.Fatal(err)
}
defer f.Close()
if _, err := f.WriteString(text); err != nil {
t.Fatal(err)
}
}
func TestDocsBuildCheckOutput(t *testing.T) {
root := scratchDocsRoot(t)
out, err := runSummer(t, "docs:build", "--check", "--root", root)
if err != nil || out != "docs:build: no problems found\n" {
t.Fatalf("docs:build --check on the clean fixture = %q, %v", out, err)
}
appendDoc(t, root, "docs/extras/faq.md", "\nSee `demo.Missing` and `summer no:such`.\n")
site := filepath.Join(t.TempDir(), "site")
for _, args := range [][]string{
{"docs:build", "--check", "--root", root},
{"docs:build", "--root", root, "--out", site},
} {
out, err := runSummer(t, args...)
if err == nil || err.Error() != "docs:build failed" {
t.Fatalf("%v = %v, want docs:build failed", args, err)
}
want := "docs/extras/faq.md:17: identifier: demo.Missing does not exist in modules/demo\n" +
"docs/extras/faq.md:17: command: \"no:such\" is not a summer or application command\n" +
"docs:build: 2 problems, nothing written\n"
if out != want {
t.Fatalf("%v output =\n%s\nwant\n%s", args, out, want)
}
}
if _, err := os.Stat(site); err == nil {
t.Fatal("docs:build wrote output despite problems")
}
// A refused --out is an error without problem lines.
if _, err := runSummer(t, "docs:build", "--root", root, "--out", filepath.Join(root, "docs", "site")); err == nil ||
!strings.Contains(err.Error(), "--out must not be inside --src") {
t.Fatalf("docs:build --out inside docs = %v", err)
}
// A missing site.yaml is an error for --check too.
if _, err := runSummer(t, "docs:build", "--check", "--root", t.TempDir()); err == nil || !strings.Contains(err.Error(), "read site config") {
t.Fatalf("docs:build --check without site.yaml = %v", err)
}
// --src and --base-url are honoured.
clean := scratchDocsRoot(t)
out, err = runSummer(t, "docs:build", "--root", clean, "--src", filepath.Join(clean, "docs"), "--out", site, "--base-url", "/v2/")
if err != nil || !strings.HasPrefix(out, "docs:build: wrote 6 pages to ") {
t.Fatalf("docs:build --src --base-url = %q, %v", out, err)
}
if html, err := os.ReadFile(filepath.Join(site, "index.html")); err != nil || !strings.Contains(string(html), `href="/v2/assets/site.css"`) {
t.Fatalf("--base-url not applied: %v", err)
}
}
func TestDocsSyncOutput(t *testing.T) {
root := scratchDocsRoot(t)
out, err := runSummer(t, "docs:sync", "--root", root)
if err != nil || out != "docs:sync: all snippets up to date\n" {
t.Fatalf("docs:sync on the clean fixture = %q, %v", out, err)
}
start := filepath.Join(root, "docs", "guide", "start.md")
raw, err := os.ReadFile(start)
if err != nil {
t.Fatal(err)
}
drifted := strings.Replace(string(raw), `demo.Hello("blog")`, `demo.Hello("stale")`, 1)
if err := os.WriteFile(start, []byte(drifted), 0o644); err != nil {
t.Fatal(err)
}
out, err = runSummer(t, "docs:sync", "--root", root)
if err != nil || out != "docs:sync: updated 1 snippets in 1 files\n" {
t.Fatalf("docs:sync with drift = %q, %v", out, err)
}
if got, _ := os.ReadFile(start); string(got) != string(raw) {
t.Fatal("docs:sync did not restore the source text")
}
appendDoc(t, root, "docs/extras/faq.md", "\n```go src=modules/demo/demo.go#Nope\n```\n")
out, err = runSummer(t, "docs:sync", "--root", root)
if err == nil || err.Error() != "docs:sync failed" {
t.Fatalf("docs:sync with a broken ref = %v", err)
}
if want := "docs/extras/faq.md:17: snippet: modules/demo/demo.go#Nope not found\ndocs:sync: 1 problems, nothing written\n"; out != want {
t.Fatalf("docs:sync output =\n%s\nwant\n%s", out, want)
}
}
func TestDocsServeRefusal(t *testing.T) {
root := scratchDocsRoot(t)
for _, addr := range []string{"0.0.0.0:8088", "[::]:8088", "192.0.2.10:8088"} {
_, err := runSummer(t, "docs:serve", "--root", root, "--addr", addr)
want := "docs:serve: refusing to listen on " + addr + ": not a loopback address. Pass --allow-remote to serve on the network."
if err == nil || err.Error() != want {
t.Errorf("docs:serve --addr %s = %v, want %q", addr, err, want)
}
}
// --allow-remote passes the address check; the planted problem then
// stops the first build before anything listens.
appendDoc(t, root, "docs/extras/faq.md", "\n`summer no:such`\n")
out, err := runSummer(t, "docs:serve", "--root", root, "--addr", "0.0.0.0:0", "--allow-remote")
if err == nil || err.Error() != "docs:serve: build failed" || !strings.Contains(out, `"no:such"`) {
t.Fatalf("docs:serve --allow-remote with a problem = %q, %v", out, err)
}
if strings.Contains(out, "Serving docs") {
t.Fatal("docs:serve listened despite a failed build")
}
}