diff --git a/internal/docsite/testdata/clean/README.md b/internal/docsite/testdata/clean/README.md new file mode 100644 index 0000000..282dcfd --- /dev/null +++ b/internal/docsite/testdata/clean/README.md @@ -0,0 +1,3 @@ +# Docs fixture + +The clean fixture for the docs checkers. `demo.Hello` is checked here too. diff --git a/internal/docsite/testdata/clean/config/app.yaml b/internal/docsite/testdata/clean/config/app.yaml new file mode 100644 index 0000000..a3b51d5 --- /dev/null +++ b/internal/docsite/testdata/clean/config/app.yaml @@ -0,0 +1,6 @@ +app: + # docs:start db + db: + host: localhost + port: 5432 + # docs:end db diff --git a/internal/docsite/testdata/clean/docs/extras/faq.md b/internal/docsite/testdata/clean/docs/extras/faq.md new file mode 100644 index 0000000..c8c46d7 --- /dev/null +++ b/internal/docsite/testdata/clean/docs/extras/faq.md @@ -0,0 +1,15 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` diff --git a/internal/docsite/testdata/clean/docs/extras/usage.md b/internal/docsite/testdata/clean/docs/extras/usage.md new file mode 100644 index 0000000..6264a85 --- /dev/null +++ b/internal/docsite/testdata/clean/docs/extras/usage.md @@ -0,0 +1,18 @@ +--- +title: Usage +description: "Build a greeter and call it." +section: extras +order: 10 +--- +# Usage + +## Call it + +A `demo.Greeter` greets by name: + +```go src=modules/demo/demo_test.go#greet +g := Greeter{Name: "blog"} +got := g.Greet() +``` + +See [Greet someone](../guide/start.md#greet-someone). diff --git a/internal/docsite/testdata/clean/docs/guide/config.md b/internal/docsite/testdata/clean/docs/guide/config.md new file mode 100644 index 0000000..918d503 --- /dev/null +++ b/internal/docsite/testdata/clean/docs/guide/config.md @@ -0,0 +1,20 @@ +--- +title: Config +description: "Configure the demo database." +section: guide +order: 20 +--- +# Config + +The database block of `config/app.yaml`: + +```yaml src=config/app.yaml#db +db: + host: localhost + port: 5432 +``` + +## Settings + +> [!WARNING] +> Keep the port in step with the database server. diff --git a/internal/docsite/testdata/clean/docs/guide/start.md b/internal/docsite/testdata/clean/docs/guide/start.md new file mode 100644 index 0000000..f16675b --- /dev/null +++ b/internal/docsite/testdata/clean/docs/guide/start.md @@ -0,0 +1,32 @@ +--- +title: Start +description: "Install the demo and greet someone." +section: guide +order: 10 +--- +# Start + +The demo module greets people. + +## Install + +Build the docs with `summer docs:build`, or run the application: + +```sh +$ summer docs:build --out site +./bin/demo serve --addr 127.0.0.1:8080 +``` + +## Greet someone + +Call `demo.Hello`: + +```go src=modules/demo/example_test.go#ExampleHello +fmt.Println(demo.Hello("blog")) +// Output: Hello, blog +``` + +> [!TIP] +> The [demo reference](../../modules/demo/README.md#usage) lists the whole API. + +Continue with [configuration](config.md) or jump back to [install](#install). diff --git a/internal/docsite/testdata/clean/docs/index.md b/internal/docsite/testdata/clean/docs/index.md new file mode 100644 index 0000000..7b0e804 --- /dev/null +++ b/internal/docsite/testdata/clean/docs/index.md @@ -0,0 +1,12 @@ +--- +title: Demo docs +description: "The landing page of the clean fixture." +section: index +order: 0 +--- +# Demo docs + +Start with the [start guide](guide/start.md), then read [usage](extras/usage.md#call-it). + +> [!NOTE] +> Every page of this fixture passes every check. diff --git a/internal/docsite/testdata/clean/docs/site.yaml b/internal/docsite/testdata/clean/docs/site.yaml new file mode 100644 index 0000000..23de32a --- /dev/null +++ b/internal/docsite/testdata/clean/docs/site.yaml @@ -0,0 +1,14 @@ +title: Demo +description: "Demo is the clean docs checker fixture." +base_url: "" +edit_url: "https://forge.example/edit/{path}" +source_url: "https://forge.example/src/{path}" +llms_notes: + - "Every page of this fixture passes every check." +sections: + - name: guide + title: Guide + - name: extras + title: Extras + - name: api + title: API reference diff --git a/internal/docsite/testdata/clean/go.mod b/internal/docsite/testdata/clean/go.mod new file mode 100644 index 0000000..c468978 --- /dev/null +++ b/internal/docsite/testdata/clean/go.mod @@ -0,0 +1,3 @@ +module example.com/docfixture + +go 1.27.0 diff --git a/internal/docsite/testdata/clean/modules/demo/README.md b/internal/docsite/testdata/clean/modules/demo/README.md new file mode 100644 index 0000000..410dfc4 --- /dev/null +++ b/internal/docsite/testdata/clean/modules/demo/README.md @@ -0,0 +1,19 @@ +# demo + +Demo greets people by name. + +```go +import "example.com/docfixture/modules/demo" +``` + +## Usage + +Call `demo.Hello` or build a `demo.Greeter` and call `demo.Greeter.Greet`. + +See the [start guide](../../docs/guide/start.md#install). + +## Testing + +```sh +go test ./modules/demo +``` diff --git a/internal/docsite/testdata/clean/modules/demo/demo.go b/internal/docsite/testdata/clean/modules/demo/demo.go new file mode 100644 index 0000000..aa9b00a --- /dev/null +++ b/internal/docsite/testdata/clean/modules/demo/demo.go @@ -0,0 +1,17 @@ +// Package demo is the docs checker fixture module. +package demo + +// Hello returns a greeting for name. +func Hello(name string) string { + return "Hello, " + name +} + +// Greeter greets by name. +type Greeter struct { + Name string +} + +// Greet returns the greeting for the greeter's name. +func (g Greeter) Greet() string { + return Hello(g.Name) +} diff --git a/internal/docsite/testdata/clean/modules/demo/demo_test.go b/internal/docsite/testdata/clean/modules/demo/demo_test.go new file mode 100644 index 0000000..1efbfe9 --- /dev/null +++ b/internal/docsite/testdata/clean/modules/demo/demo_test.go @@ -0,0 +1,13 @@ +package demo + +import "testing" + +func TestGreet(t *testing.T) { + // docs:start greet + g := Greeter{Name: "blog"} + got := g.Greet() + // docs:end greet + if got != "Hello, blog" { + t.Fatalf("Greet() = %q", got) + } +} diff --git a/internal/docsite/testdata/clean/modules/demo/example_test.go b/internal/docsite/testdata/clean/modules/demo/example_test.go new file mode 100644 index 0000000..473b791 --- /dev/null +++ b/internal/docsite/testdata/clean/modules/demo/example_test.go @@ -0,0 +1,12 @@ +package demo_test + +import ( + "fmt" + + "example.com/docfixture/modules/demo" +) + +func ExampleHello() { + fmt.Println(demo.Hello("blog")) + // Output: Hello, blog +} diff --git a/internal/docsite/testdata/violations/anchor-cross-page/docs/guide/start.md b/internal/docsite/testdata/violations/anchor-cross-page/docs/guide/start.md new file mode 100644 index 0000000..b930cf5 --- /dev/null +++ b/internal/docsite/testdata/violations/anchor-cross-page/docs/guide/start.md @@ -0,0 +1,34 @@ +--- +title: Start +description: "Install the demo and greet someone." +section: guide +order: 10 +--- +# Start + +The demo module greets people. + +## Install + +Build the docs with `summer docs:build`, or run the application: + +```sh +$ summer docs:build --out site +./bin/demo serve --addr 127.0.0.1:8080 +``` + +## Greet someone + +Call `demo.Hello`: + +```go src=modules/demo/example_test.go#ExampleHello +fmt.Println(demo.Hello("blog")) +// Output: Hello, blog +``` + +> [!TIP] +> The [demo reference](../../modules/demo/README.md#usage) lists the whole API. + +Continue with [configuration](config.md) or jump back to [install](#install). + +See [usage](../extras/usage.md#nope). diff --git a/internal/docsite/testdata/violations/anchor-cross-page/want.txt b/internal/docsite/testdata/violations/anchor-cross-page/want.txt new file mode 100644 index 0000000..9ec2021 --- /dev/null +++ b/internal/docsite/testdata/violations/anchor-cross-page/want.txt @@ -0,0 +1,3 @@ +rule: link +file: docs/guide/start.md +message: #nope not found in docs/extras/usage.md diff --git a/internal/docsite/testdata/violations/anchor-missing/docs/guide/start.md b/internal/docsite/testdata/violations/anchor-missing/docs/guide/start.md new file mode 100644 index 0000000..009b756 --- /dev/null +++ b/internal/docsite/testdata/violations/anchor-missing/docs/guide/start.md @@ -0,0 +1,34 @@ +--- +title: Start +description: "Install the demo and greet someone." +section: guide +order: 10 +--- +# Start + +The demo module greets people. + +## Install + +Build the docs with `summer docs:build`, or run the application: + +```sh +$ summer docs:build --out site +./bin/demo serve --addr 127.0.0.1:8080 +``` + +## Greet someone + +Call `demo.Hello`: + +```go src=modules/demo/example_test.go#ExampleHello +fmt.Println(demo.Hello("blog")) +// Output: Hello, blog +``` + +> [!TIP] +> The [demo reference](../../modules/demo/README.md#usage) lists the whole API. + +Continue with [configuration](config.md) or jump back to [install](#install). + +Back to [nowhere](#nope). diff --git a/internal/docsite/testdata/violations/anchor-missing/want.txt b/internal/docsite/testdata/violations/anchor-missing/want.txt new file mode 100644 index 0000000..4ffa27f --- /dev/null +++ b/internal/docsite/testdata/violations/anchor-missing/want.txt @@ -0,0 +1,3 @@ +rule: link +file: docs/guide/start.md +message: #nope not found in docs/guide/start.md diff --git a/internal/docsite/testdata/violations/callout-unknown-readme/modules/demo/README.md b/internal/docsite/testdata/violations/callout-unknown-readme/modules/demo/README.md new file mode 100644 index 0000000..350f6dc --- /dev/null +++ b/internal/docsite/testdata/violations/callout-unknown-readme/modules/demo/README.md @@ -0,0 +1,22 @@ +# demo + +Demo greets people by name. + +```go +import "example.com/docfixture/modules/demo" +``` + +## Usage + +Call `demo.Hello` or build a `demo.Greeter` and call `demo.Greeter.Greet`. + +See the [start guide](../../docs/guide/start.md#install). + +## Testing + +```sh +go test ./modules/demo +``` + +> [!CAUTION] +> Planted. diff --git a/internal/docsite/testdata/violations/callout-unknown-readme/want.txt b/internal/docsite/testdata/violations/callout-unknown-readme/want.txt new file mode 100644 index 0000000..7da7192 --- /dev/null +++ b/internal/docsite/testdata/violations/callout-unknown-readme/want.txt @@ -0,0 +1,3 @@ +rule: callout +file: modules/demo/README.md +message: unknown type CAUTION diff --git a/internal/docsite/testdata/violations/callout-unknown/docs/extras/faq.md b/internal/docsite/testdata/violations/callout-unknown/docs/extras/faq.md new file mode 100644 index 0000000..cec4031 --- /dev/null +++ b/internal/docsite/testdata/violations/callout-unknown/docs/extras/faq.md @@ -0,0 +1,18 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +> [!DANGER] +> Planted. diff --git a/internal/docsite/testdata/violations/callout-unknown/want.txt b/internal/docsite/testdata/violations/callout-unknown/want.txt new file mode 100644 index 0000000..a6ffbd4 --- /dev/null +++ b/internal/docsite/testdata/violations/callout-unknown/want.txt @@ -0,0 +1,3 @@ +rule: callout +file: docs/extras/faq.md +message: unknown type DANGER (use NOTE, TIP or WARNING) diff --git a/internal/docsite/testdata/violations/command-app-name-as-tool/docs/extras/faq.md b/internal/docsite/testdata/violations/command-app-name-as-tool/docs/extras/faq.md new file mode 100644 index 0000000..fc89376 --- /dev/null +++ b/internal/docsite/testdata/violations/command-app-name-as-tool/docs/extras/faq.md @@ -0,0 +1,19 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```bash +cd app && summer serve +``` diff --git a/internal/docsite/testdata/violations/command-app-name-as-tool/want.txt b/internal/docsite/testdata/violations/command-app-name-as-tool/want.txt new file mode 100644 index 0000000..d6befac --- /dev/null +++ b/internal/docsite/testdata/violations/command-app-name-as-tool/want.txt @@ -0,0 +1,3 @@ +rule: command +file: docs/extras/faq.md +message: "serve" is not a summer or application command diff --git a/internal/docsite/testdata/violations/command-in-readme/modules/demo/README.md b/internal/docsite/testdata/violations/command-in-readme/modules/demo/README.md new file mode 100644 index 0000000..19c1c09 --- /dev/null +++ b/internal/docsite/testdata/violations/command-in-readme/modules/demo/README.md @@ -0,0 +1,19 @@ +# demo + +Demo greets people by name. + +```go +import "example.com/docfixture/modules/demo" +``` + +## Usage + +Call `demo.Hello` or build a `demo.Greeter` and call `demo.Greeter.Greet`. + +See the [start guide](../../docs/guide/start.md#install). + +## Testing + +```sh +./bin/demo demo:nope +``` diff --git a/internal/docsite/testdata/violations/command-in-readme/want.txt b/internal/docsite/testdata/violations/command-in-readme/want.txt new file mode 100644 index 0000000..05bae06 --- /dev/null +++ b/internal/docsite/testdata/violations/command-in-readme/want.txt @@ -0,0 +1,3 @@ +rule: command +file: modules/demo/README.md +message: "demo:nope" is not a summer or application command diff --git a/internal/docsite/testdata/violations/command-unknown-app/docs/extras/faq.md b/internal/docsite/testdata/violations/command-unknown-app/docs/extras/faq.md new file mode 100644 index 0000000..50a2ed8 --- /dev/null +++ b/internal/docsite/testdata/violations/command-unknown-app/docs/extras/faq.md @@ -0,0 +1,19 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```sh +./bin/demo no:such --force +``` diff --git a/internal/docsite/testdata/violations/command-unknown-app/want.txt b/internal/docsite/testdata/violations/command-unknown-app/want.txt new file mode 100644 index 0000000..d5f59c5 --- /dev/null +++ b/internal/docsite/testdata/violations/command-unknown-app/want.txt @@ -0,0 +1,3 @@ +rule: command +file: docs/extras/faq.md +message: "no:such" is not a summer or application command diff --git a/internal/docsite/testdata/violations/command-unknown-summer/docs/extras/faq.md b/internal/docsite/testdata/violations/command-unknown-summer/docs/extras/faq.md new file mode 100644 index 0000000..c54ec72 --- /dev/null +++ b/internal/docsite/testdata/violations/command-unknown-summer/docs/extras/faq.md @@ -0,0 +1,17 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +Then run `summer no:such`. diff --git a/internal/docsite/testdata/violations/command-unknown-summer/want.txt b/internal/docsite/testdata/violations/command-unknown-summer/want.txt new file mode 100644 index 0000000..d5f59c5 --- /dev/null +++ b/internal/docsite/testdata/violations/command-unknown-summer/want.txt @@ -0,0 +1,3 @@ +rule: command +file: docs/extras/faq.md +message: "no:such" is not a summer or application command diff --git a/internal/docsite/testdata/violations/frontmatter-api-section/docs/api/extra.md b/internal/docsite/testdata/violations/frontmatter-api-section/docs/api/extra.md new file mode 100644 index 0000000..efff704 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-api-section/docs/api/extra.md @@ -0,0 +1,9 @@ +--- +title: Extra +description: "Extra page." +section: api +order: 90 +--- +# Extra + +Text. diff --git a/internal/docsite/testdata/violations/frontmatter-api-section/want.txt b/internal/docsite/testdata/violations/frontmatter-api-section/want.txt new file mode 100644 index 0000000..248734b --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-api-section/want.txt @@ -0,0 +1,3 @@ +rule: frontmatter +file: docs/api/extra.md +message: section "api" is reserved for the ingested module READMEs diff --git a/internal/docsite/testdata/violations/frontmatter-decode-error/docs/extras/faq.md b/internal/docsite/testdata/violations/frontmatter-decode-error/docs/extras/faq.md new file mode 100644 index 0000000..bea9b63 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-decode-error/docs/extras/faq.md @@ -0,0 +1,15 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: twenty +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` diff --git a/internal/docsite/testdata/violations/frontmatter-decode-error/want.txt b/internal/docsite/testdata/violations/frontmatter-decode-error/want.txt new file mode 100644 index 0000000..203938d --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-decode-error/want.txt @@ -0,0 +1,3 @@ +rule: frontmatter +file: docs/extras/faq.md +message: cannot unmarshal string into Go struct field Frontmatter.Order diff --git a/internal/docsite/testdata/violations/frontmatter-duplicate-order/docs/guide/config.md b/internal/docsite/testdata/violations/frontmatter-duplicate-order/docs/guide/config.md new file mode 100644 index 0000000..9a2a623 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-duplicate-order/docs/guide/config.md @@ -0,0 +1,20 @@ +--- +title: Config +description: "Configure the demo database." +section: guide +order: 10 +--- +# Config + +The database block of `config/app.yaml`: + +```yaml src=config/app.yaml#db +db: + host: localhost + port: 5432 +``` + +## Settings + +> [!WARNING] +> Keep the port in step with the database server. diff --git a/internal/docsite/testdata/violations/frontmatter-duplicate-order/want.txt b/internal/docsite/testdata/violations/frontmatter-duplicate-order/want.txt new file mode 100644 index 0000000..0a90b46 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-duplicate-order/want.txt @@ -0,0 +1,3 @@ +rule: frontmatter +file: docs/guide/start.md +message: order 10 already used by docs/guide/config.md diff --git a/internal/docsite/testdata/violations/frontmatter-first-line/docs/guide/config.md b/internal/docsite/testdata/violations/frontmatter-first-line/docs/guide/config.md new file mode 100644 index 0000000..8c2b412 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-first-line/docs/guide/config.md @@ -0,0 +1,20 @@ +--- +title: Config +description: "Configure the demo database." +section: guide +order: 20 +--- +# Configuration + +The database block of `config/app.yaml`: + +```yaml src=config/app.yaml#db +db: + host: localhost + port: 5432 +``` + +## Settings + +> [!WARNING] +> Keep the port in step with the database server. diff --git a/internal/docsite/testdata/violations/frontmatter-first-line/want.txt b/internal/docsite/testdata/violations/frontmatter-first-line/want.txt new file mode 100644 index 0000000..60502b6 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-first-line/want.txt @@ -0,0 +1,3 @@ +rule: frontmatter +file: docs/guide/config.md +message: first line must be "# Config" diff --git a/internal/docsite/testdata/violations/frontmatter-index-section/docs/index.md b/internal/docsite/testdata/violations/frontmatter-index-section/docs/index.md new file mode 100644 index 0000000..fbc05a3 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-index-section/docs/index.md @@ -0,0 +1,12 @@ +--- +title: Demo docs +description: "The landing page of the clean fixture." +section: guide +order: 0 +--- +# Demo docs + +Start with the [start guide](guide/start.md), then read [usage](extras/usage.md#call-it). + +> [!NOTE] +> Every page of this fixture passes every check. diff --git a/internal/docsite/testdata/violations/frontmatter-index-section/want.txt b/internal/docsite/testdata/violations/frontmatter-index-section/want.txt new file mode 100644 index 0000000..7a815e0 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-index-section/want.txt @@ -0,0 +1,3 @@ +rule: frontmatter +file: docs/index.md +message: the landing page uses section "index" diff --git a/internal/docsite/testdata/violations/frontmatter-long-description/docs/guide/config.md b/internal/docsite/testdata/violations/frontmatter-long-description/docs/guide/config.md new file mode 100644 index 0000000..50dc135 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-long-description/docs/guide/config.md @@ -0,0 +1,20 @@ +--- +title: Config +description: "Configure the demo database. Configure the demo database. Configure the demo database. Configure the demo database. Configure the demo database. Configure the demo database. " +section: guide +order: 20 +--- +# Config + +The database block of `config/app.yaml`: + +```yaml src=config/app.yaml#db +db: + host: localhost + port: 5432 +``` + +## Settings + +> [!WARNING] +> Keep the port in step with the database server. diff --git a/internal/docsite/testdata/violations/frontmatter-long-description/want.txt b/internal/docsite/testdata/violations/frontmatter-long-description/want.txt new file mode 100644 index 0000000..b5694b0 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-long-description/want.txt @@ -0,0 +1,3 @@ +rule: frontmatter +file: docs/guide/config.md +message: description is longer than 160 characters diff --git a/internal/docsite/testdata/violations/frontmatter-missing-field/docs/extras/faq.md b/internal/docsite/testdata/violations/frontmatter-missing-field/docs/extras/faq.md new file mode 100644 index 0000000..7762676 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-missing-field/docs/extras/faq.md @@ -0,0 +1,14 @@ +--- +title: FAQ +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` diff --git a/internal/docsite/testdata/violations/frontmatter-missing-field/want.txt b/internal/docsite/testdata/violations/frontmatter-missing-field/want.txt new file mode 100644 index 0000000..849c202 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-missing-field/want.txt @@ -0,0 +1,3 @@ +rule: frontmatter +file: docs/extras/faq.md +message: missing field "description" diff --git a/internal/docsite/testdata/violations/frontmatter-no-block/docs/extras/faq.md b/internal/docsite/testdata/violations/frontmatter-no-block/docs/extras/faq.md new file mode 100644 index 0000000..c83268e --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-no-block/docs/extras/faq.md @@ -0,0 +1,14 @@ +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` diff --git a/internal/docsite/testdata/violations/frontmatter-no-block/want.txt b/internal/docsite/testdata/violations/frontmatter-no-block/want.txt new file mode 100644 index 0000000..d7a804e --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-no-block/want.txt @@ -0,0 +1,3 @@ +rule: frontmatter +file: docs/extras/faq.md +message: the file must start with a "---" frontmatter block diff --git a/internal/docsite/testdata/violations/frontmatter-root-page/docs/stray.md b/internal/docsite/testdata/violations/frontmatter-root-page/docs/stray.md new file mode 100644 index 0000000..658b597 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-root-page/docs/stray.md @@ -0,0 +1,9 @@ +--- +title: Stray +description: "Stray page." +section: guide +order: 90 +--- +# Stray + +Text. diff --git a/internal/docsite/testdata/violations/frontmatter-root-page/want.txt b/internal/docsite/testdata/violations/frontmatter-root-page/want.txt new file mode 100644 index 0000000..1e7d7f8 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-root-page/want.txt @@ -0,0 +1,3 @@ +rule: frontmatter +file: docs/stray.md +message: only index.md sits at the docs root diff --git a/internal/docsite/testdata/violations/frontmatter-section-mismatch/docs/guide/config.md b/internal/docsite/testdata/violations/frontmatter-section-mismatch/docs/guide/config.md new file mode 100644 index 0000000..9bb112d --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-section-mismatch/docs/guide/config.md @@ -0,0 +1,20 @@ +--- +title: Config +description: "Configure the demo database." +section: extras +order: 30 +--- +# Config + +The database block of `config/app.yaml`: + +```yaml src=config/app.yaml#db +db: + host: localhost + port: 5432 +``` + +## Settings + +> [!WARNING] +> Keep the port in step with the database server. diff --git a/internal/docsite/testdata/violations/frontmatter-section-mismatch/want.txt b/internal/docsite/testdata/violations/frontmatter-section-mismatch/want.txt new file mode 100644 index 0000000..a18031a --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-section-mismatch/want.txt @@ -0,0 +1,3 @@ +rule: frontmatter +file: docs/guide/config.md +message: section "extras" does not match directory "guide" diff --git a/internal/docsite/testdata/violations/frontmatter-unclosed-block/docs/extras/draft.md b/internal/docsite/testdata/violations/frontmatter-unclosed-block/docs/extras/draft.md new file mode 100644 index 0000000..269c991 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-unclosed-block/docs/extras/draft.md @@ -0,0 +1,6 @@ +--- +title: Draft +description: x +section: extras +order: 90 +# Draft diff --git a/internal/docsite/testdata/violations/frontmatter-unclosed-block/want.txt b/internal/docsite/testdata/violations/frontmatter-unclosed-block/want.txt new file mode 100644 index 0000000..a0561e3 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-unclosed-block/want.txt @@ -0,0 +1,3 @@ +rule: frontmatter +file: docs/extras/draft.md +message: the file must start with a "---" frontmatter block diff --git a/internal/docsite/testdata/violations/frontmatter-unknown-field/docs/extras/faq.md b/internal/docsite/testdata/violations/frontmatter-unknown-field/docs/extras/faq.md new file mode 100644 index 0000000..e1a5d6d --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-unknown-field/docs/extras/faq.md @@ -0,0 +1,16 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +colour: red +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` diff --git a/internal/docsite/testdata/violations/frontmatter-unknown-field/want.txt b/internal/docsite/testdata/violations/frontmatter-unknown-field/want.txt new file mode 100644 index 0000000..1be8e41 --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-unknown-field/want.txt @@ -0,0 +1,3 @@ +rule: frontmatter +file: docs/extras/faq.md +message: unknown field "colour" diff --git a/internal/docsite/testdata/violations/frontmatter-unlisted-section/docs/other/extra.md b/internal/docsite/testdata/violations/frontmatter-unlisted-section/docs/other/extra.md new file mode 100644 index 0000000..b72009a --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-unlisted-section/docs/other/extra.md @@ -0,0 +1,9 @@ +--- +title: Extra +description: "Extra page." +section: other +order: 90 +--- +# Extra + +Text. diff --git a/internal/docsite/testdata/violations/frontmatter-unlisted-section/want.txt b/internal/docsite/testdata/violations/frontmatter-unlisted-section/want.txt new file mode 100644 index 0000000..d1789df --- /dev/null +++ b/internal/docsite/testdata/violations/frontmatter-unlisted-section/want.txt @@ -0,0 +1,3 @@ +rule: frontmatter +file: docs/other/extra.md +message: section "other" is not listed in docs/site.yaml diff --git a/internal/docsite/testdata/violations/go-fence-no-src/docs/extras/faq.md b/internal/docsite/testdata/violations/go-fence-no-src/docs/extras/faq.md new file mode 100644 index 0000000..2da01fc --- /dev/null +++ b/internal/docsite/testdata/violations/go-fence-no-src/docs/extras/faq.md @@ -0,0 +1,19 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```go +x := 1 +``` diff --git a/internal/docsite/testdata/violations/go-fence-no-src/want.txt b/internal/docsite/testdata/violations/go-fence-no-src/want.txt new file mode 100644 index 0000000..83d9670 --- /dev/null +++ b/internal/docsite/testdata/violations/go-fence-no-src/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: go code block has no src= reference diff --git a/internal/docsite/testdata/violations/heading-autolink/docs/extras/faq.md b/internal/docsite/testdata/violations/heading-autolink/docs/extras/faq.md new file mode 100644 index 0000000..385adf7 --- /dev/null +++ b/internal/docsite/testdata/violations/heading-autolink/docs/extras/faq.md @@ -0,0 +1,17 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +## Visit diff --git a/internal/docsite/testdata/violations/heading-autolink/want.txt b/internal/docsite/testdata/violations/heading-autolink/want.txt new file mode 100644 index 0000000..65d544c --- /dev/null +++ b/internal/docsite/testdata/violations/heading-autolink/want.txt @@ -0,0 +1,3 @@ +rule: heading +file: docs/extras/faq.md +message: headings must be plain ASCII text without links or code diff --git a/internal/docsite/testdata/violations/heading-code/docs/extras/faq.md b/internal/docsite/testdata/violations/heading-code/docs/extras/faq.md new file mode 100644 index 0000000..7acbb2f --- /dev/null +++ b/internal/docsite/testdata/violations/heading-code/docs/extras/faq.md @@ -0,0 +1,17 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +## Use `demo.Hello` diff --git a/internal/docsite/testdata/violations/heading-code/want.txt b/internal/docsite/testdata/violations/heading-code/want.txt new file mode 100644 index 0000000..65d544c --- /dev/null +++ b/internal/docsite/testdata/violations/heading-code/want.txt @@ -0,0 +1,3 @@ +rule: heading +file: docs/extras/faq.md +message: headings must be plain ASCII text without links or code diff --git a/internal/docsite/testdata/violations/heading-link/docs/extras/faq.md b/internal/docsite/testdata/violations/heading-link/docs/extras/faq.md new file mode 100644 index 0000000..9f1abb6 --- /dev/null +++ b/internal/docsite/testdata/violations/heading-link/docs/extras/faq.md @@ -0,0 +1,17 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +## See [usage](usage.md) diff --git a/internal/docsite/testdata/violations/heading-link/want.txt b/internal/docsite/testdata/violations/heading-link/want.txt new file mode 100644 index 0000000..65d544c --- /dev/null +++ b/internal/docsite/testdata/violations/heading-link/want.txt @@ -0,0 +1,3 @@ +rule: heading +file: docs/extras/faq.md +message: headings must be plain ASCII text without links or code diff --git a/internal/docsite/testdata/violations/heading-non-ascii/docs/extras/faq.md b/internal/docsite/testdata/violations/heading-non-ascii/docs/extras/faq.md new file mode 100644 index 0000000..305392b --- /dev/null +++ b/internal/docsite/testdata/violations/heading-non-ascii/docs/extras/faq.md @@ -0,0 +1,17 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +## Zażółć gęślą diff --git a/internal/docsite/testdata/violations/heading-non-ascii/want.txt b/internal/docsite/testdata/violations/heading-non-ascii/want.txt new file mode 100644 index 0000000..65d544c --- /dev/null +++ b/internal/docsite/testdata/violations/heading-non-ascii/want.txt @@ -0,0 +1,3 @@ +rule: heading +file: docs/extras/faq.md +message: headings must be plain ASCII text without links or code diff --git a/internal/docsite/testdata/violations/identifier-ambiguous-package/modules/demo/util/util.go b/internal/docsite/testdata/violations/identifier-ambiguous-package/modules/demo/util/util.go new file mode 100644 index 0000000..c7d8682 --- /dev/null +++ b/internal/docsite/testdata/violations/identifier-ambiguous-package/modules/demo/util/util.go @@ -0,0 +1 @@ +package util diff --git a/internal/docsite/testdata/violations/identifier-ambiguous-package/modules/other/util/util.go b/internal/docsite/testdata/violations/identifier-ambiguous-package/modules/other/util/util.go new file mode 100644 index 0000000..c7d8682 --- /dev/null +++ b/internal/docsite/testdata/violations/identifier-ambiguous-package/modules/other/util/util.go @@ -0,0 +1 @@ +package util diff --git a/internal/docsite/testdata/violations/identifier-ambiguous-package/want.txt b/internal/docsite/testdata/violations/identifier-ambiguous-package/want.txt new file mode 100644 index 0000000..001de7e --- /dev/null +++ b/internal/docsite/testdata/violations/identifier-ambiguous-package/want.txt @@ -0,0 +1,3 @@ +rule: identifier +file: modules/other/util +message: package name "util" is used by modules/demo/util and modules/other/util diff --git a/internal/docsite/testdata/violations/identifier-module-readme/modules/demo/README.md b/internal/docsite/testdata/violations/identifier-module-readme/modules/demo/README.md new file mode 100644 index 0000000..d859e13 --- /dev/null +++ b/internal/docsite/testdata/violations/identifier-module-readme/modules/demo/README.md @@ -0,0 +1,19 @@ +# demo + +Demo greets people by name. + +```go +import "example.com/docfixture/modules/demo" +``` + +## Usage + +Call `demo.Gone` or build a `demo.Greeter` and call `demo.Greeter.Greet`. + +See the [start guide](../../docs/guide/start.md#install). + +## Testing + +```sh +go test ./modules/demo +``` diff --git a/internal/docsite/testdata/violations/identifier-module-readme/want.txt b/internal/docsite/testdata/violations/identifier-module-readme/want.txt new file mode 100644 index 0000000..e2cb747 --- /dev/null +++ b/internal/docsite/testdata/violations/identifier-module-readme/want.txt @@ -0,0 +1,3 @@ +rule: identifier +file: modules/demo/README.md +message: demo.Gone does not exist in modules/demo diff --git a/internal/docsite/testdata/violations/identifier-root-readme/README.md b/internal/docsite/testdata/violations/identifier-root-readme/README.md new file mode 100644 index 0000000..ddd50c3 --- /dev/null +++ b/internal/docsite/testdata/violations/identifier-root-readme/README.md @@ -0,0 +1,3 @@ +# Docs fixture + +The clean fixture for the docs checkers. `demo.Bye` is checked here too. diff --git a/internal/docsite/testdata/violations/identifier-root-readme/want.txt b/internal/docsite/testdata/violations/identifier-root-readme/want.txt new file mode 100644 index 0000000..7d88f16 --- /dev/null +++ b/internal/docsite/testdata/violations/identifier-root-readme/want.txt @@ -0,0 +1,3 @@ +rule: identifier +file: README.md +message: demo.Bye does not exist in modules/demo diff --git a/internal/docsite/testdata/violations/identifier-unknown-member/docs/extras/faq.md b/internal/docsite/testdata/violations/identifier-unknown-member/docs/extras/faq.md new file mode 100644 index 0000000..3d1cc06 --- /dev/null +++ b/internal/docsite/testdata/violations/identifier-unknown-member/docs/extras/faq.md @@ -0,0 +1,17 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +See `demo.Greeter.Nope`. diff --git a/internal/docsite/testdata/violations/identifier-unknown-member/want.txt b/internal/docsite/testdata/violations/identifier-unknown-member/want.txt new file mode 100644 index 0000000..353b61f --- /dev/null +++ b/internal/docsite/testdata/violations/identifier-unknown-member/want.txt @@ -0,0 +1,3 @@ +rule: identifier +file: docs/extras/faq.md +message: demo.Greeter.Nope does not exist in modules/demo diff --git a/internal/docsite/testdata/violations/identifier-unknown/docs/extras/faq.md b/internal/docsite/testdata/violations/identifier-unknown/docs/extras/faq.md new file mode 100644 index 0000000..e6460d3 --- /dev/null +++ b/internal/docsite/testdata/violations/identifier-unknown/docs/extras/faq.md @@ -0,0 +1,17 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +See `demo.Missing`. diff --git a/internal/docsite/testdata/violations/identifier-unknown/want.txt b/internal/docsite/testdata/violations/identifier-unknown/want.txt new file mode 100644 index 0000000..7685dac --- /dev/null +++ b/internal/docsite/testdata/violations/identifier-unknown/want.txt @@ -0,0 +1,3 @@ +rule: identifier +file: docs/extras/faq.md +message: demo.Missing does not exist in modules/demo diff --git a/internal/docsite/testdata/violations/link-absolute/docs/extras/faq.md b/internal/docsite/testdata/violations/link-absolute/docs/extras/faq.md new file mode 100644 index 0000000..078ae3f --- /dev/null +++ b/internal/docsite/testdata/violations/link-absolute/docs/extras/faq.md @@ -0,0 +1,17 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +See [root](/index.html). diff --git a/internal/docsite/testdata/violations/link-absolute/want.txt b/internal/docsite/testdata/violations/link-absolute/want.txt new file mode 100644 index 0000000..d306de2 --- /dev/null +++ b/internal/docsite/testdata/violations/link-absolute/want.txt @@ -0,0 +1,3 @@ +rule: link +file: docs/extras/faq.md +message: /index.html does not resolve diff --git a/internal/docsite/testdata/violations/link-broken/docs/extras/faq.md b/internal/docsite/testdata/violations/link-broken/docs/extras/faq.md new file mode 100644 index 0000000..3563488 --- /dev/null +++ b/internal/docsite/testdata/violations/link-broken/docs/extras/faq.md @@ -0,0 +1,17 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +See [missing](missing.md). diff --git a/internal/docsite/testdata/violations/link-broken/want.txt b/internal/docsite/testdata/violations/link-broken/want.txt new file mode 100644 index 0000000..0f35232 --- /dev/null +++ b/internal/docsite/testdata/violations/link-broken/want.txt @@ -0,0 +1,3 @@ +rule: link +file: docs/extras/faq.md +message: missing.md does not resolve diff --git a/internal/docsite/testdata/violations/link-empty/docs/extras/faq.md b/internal/docsite/testdata/violations/link-empty/docs/extras/faq.md new file mode 100644 index 0000000..d8f288e --- /dev/null +++ b/internal/docsite/testdata/violations/link-empty/docs/extras/faq.md @@ -0,0 +1,17 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +See [nothing](). diff --git a/internal/docsite/testdata/violations/link-empty/want.txt b/internal/docsite/testdata/violations/link-empty/want.txt new file mode 100644 index 0000000..3ff9d0f --- /dev/null +++ b/internal/docsite/testdata/violations/link-empty/want.txt @@ -0,0 +1,3 @@ +rule: link +file: docs/extras/faq.md +message: empty link destination does not resolve diff --git a/internal/docsite/testdata/violations/link-module-readme/modules/demo/README.md b/internal/docsite/testdata/violations/link-module-readme/modules/demo/README.md new file mode 100644 index 0000000..db96156 --- /dev/null +++ b/internal/docsite/testdata/violations/link-module-readme/modules/demo/README.md @@ -0,0 +1,19 @@ +# demo + +Demo greets people by name. + +```go +import "example.com/docfixture/modules/demo" +``` + +## Usage + +Call `demo.Hello` or build a `demo.Greeter` and call `demo.Greeter.Greet`. + +See the [start guide](../../docs/guide/nope.md). + +## Testing + +```sh +go test ./modules/demo +``` diff --git a/internal/docsite/testdata/violations/link-module-readme/want.txt b/internal/docsite/testdata/violations/link-module-readme/want.txt new file mode 100644 index 0000000..fd0116c --- /dev/null +++ b/internal/docsite/testdata/violations/link-module-readme/want.txt @@ -0,0 +1,3 @@ +rule: link +file: modules/demo/README.md +message: ../../docs/guide/nope.md does not resolve diff --git a/internal/docsite/testdata/violations/link-source-file/docs/extras/faq.md b/internal/docsite/testdata/violations/link-source-file/docs/extras/faq.md new file mode 100644 index 0000000..4fcf649 --- /dev/null +++ b/internal/docsite/testdata/violations/link-source-file/docs/extras/faq.md @@ -0,0 +1,17 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +See [source](../../modules/demo/demo.go). diff --git a/internal/docsite/testdata/violations/link-source-file/want.txt b/internal/docsite/testdata/violations/link-source-file/want.txt new file mode 100644 index 0000000..d38e5ec --- /dev/null +++ b/internal/docsite/testdata/violations/link-source-file/want.txt @@ -0,0 +1,3 @@ +rule: link +file: docs/extras/faq.md +message: ../../modules/demo/demo.go does not resolve diff --git a/internal/docsite/testdata/violations/page-missing-index/remove.txt b/internal/docsite/testdata/violations/page-missing-index/remove.txt new file mode 100644 index 0000000..3d16f0e --- /dev/null +++ b/internal/docsite/testdata/violations/page-missing-index/remove.txt @@ -0,0 +1 @@ +docs/index.md diff --git a/internal/docsite/testdata/violations/page-missing-index/want.txt b/internal/docsite/testdata/violations/page-missing-index/want.txt new file mode 100644 index 0000000..a60a9b0 --- /dev/null +++ b/internal/docsite/testdata/violations/page-missing-index/want.txt @@ -0,0 +1,3 @@ +rule: page +file: docs/index.md +message: the landing page is missing diff --git a/internal/docsite/testdata/violations/readme-missing/modules/extra/extra.go b/internal/docsite/testdata/violations/readme-missing/modules/extra/extra.go new file mode 100644 index 0000000..32a2dc9 --- /dev/null +++ b/internal/docsite/testdata/violations/readme-missing/modules/extra/extra.go @@ -0,0 +1,2 @@ +// Package extra has no README. +package extra diff --git a/internal/docsite/testdata/violations/readme-missing/want.txt b/internal/docsite/testdata/violations/readme-missing/want.txt new file mode 100644 index 0000000..026f9b4 --- /dev/null +++ b/internal/docsite/testdata/violations/readme-missing/want.txt @@ -0,0 +1,3 @@ +rule: readme +file: modules/extra +message: package has Go files but no README.md diff --git a/internal/docsite/testdata/violations/readme-no-title/modules/extra/README.md b/internal/docsite/testdata/violations/readme-no-title/modules/extra/README.md new file mode 100644 index 0000000..1d5028d --- /dev/null +++ b/internal/docsite/testdata/violations/readme-no-title/modules/extra/README.md @@ -0,0 +1,3 @@ +extra + +No H1 title. diff --git a/internal/docsite/testdata/violations/readme-no-title/modules/extra/extra.go b/internal/docsite/testdata/violations/readme-no-title/modules/extra/extra.go new file mode 100644 index 0000000..766c0ed --- /dev/null +++ b/internal/docsite/testdata/violations/readme-no-title/modules/extra/extra.go @@ -0,0 +1,2 @@ +// Package extra has a bad README. +package extra diff --git a/internal/docsite/testdata/violations/readme-no-title/want.txt b/internal/docsite/testdata/violations/readme-no-title/want.txt new file mode 100644 index 0000000..de3746a --- /dev/null +++ b/internal/docsite/testdata/violations/readme-no-title/want.txt @@ -0,0 +1,3 @@ +rule: readme +file: modules/extra/README.md +message: first line must be the "# " title diff --git a/internal/docsite/testdata/violations/section-no-pages/docs/site.yaml b/internal/docsite/testdata/violations/section-no-pages/docs/site.yaml new file mode 100644 index 0000000..b40808a --- /dev/null +++ b/internal/docsite/testdata/violations/section-no-pages/docs/site.yaml @@ -0,0 +1,16 @@ +title: Demo +description: "Demo is the clean docs checker fixture." +base_url: "" +edit_url: "https://forge.example/edit/{path}" +source_url: "https://forge.example/src/{path}" +llms_notes: + - "Every page of this fixture passes every check." +sections: + - name: guide + title: Guide + - name: extras + title: Extras + - name: empty + title: Empty + - name: api + title: API reference diff --git a/internal/docsite/testdata/violations/section-no-pages/want.txt b/internal/docsite/testdata/violations/section-no-pages/want.txt new file mode 100644 index 0000000..78dadbf --- /dev/null +++ b/internal/docsite/testdata/violations/section-no-pages/want.txt @@ -0,0 +1,3 @@ +rule: section +file: docs/site.yaml +message: "empty" has no pages diff --git a/internal/docsite/testdata/violations/site-duplicate-section/docs/site.yaml b/internal/docsite/testdata/violations/site-duplicate-section/docs/site.yaml new file mode 100644 index 0000000..6c4fa31 --- /dev/null +++ b/internal/docsite/testdata/violations/site-duplicate-section/docs/site.yaml @@ -0,0 +1,16 @@ +title: Demo +description: "Demo is the clean docs checker fixture." +base_url: "" +edit_url: "https://forge.example/edit/{path}" +source_url: "https://forge.example/src/{path}" +llms_notes: + - "Every page of this fixture passes every check." +sections: + - name: guide + title: Guide + - name: extras + title: Extras + - name: guide + title: Again + - name: api + title: API reference diff --git a/internal/docsite/testdata/violations/site-duplicate-section/want.txt b/internal/docsite/testdata/violations/site-duplicate-section/want.txt new file mode 100644 index 0000000..2d71849 --- /dev/null +++ b/internal/docsite/testdata/violations/site-duplicate-section/want.txt @@ -0,0 +1,3 @@ +rule: site +file: docs/site.yaml +message: section "guide" is listed twice diff --git a/internal/docsite/testdata/violations/site-unknown-key/docs/site.yaml b/internal/docsite/testdata/violations/site-unknown-key/docs/site.yaml new file mode 100644 index 0000000..ac5ab33 --- /dev/null +++ b/internal/docsite/testdata/violations/site-unknown-key/docs/site.yaml @@ -0,0 +1,15 @@ +title: Demo +description: "Demo is the clean docs checker fixture." +colour: red +base_url: "" +edit_url: "https://forge.example/edit/{path}" +source_url: "https://forge.example/src/{path}" +llms_notes: + - "Every page of this fixture passes every check." +sections: + - name: guide + title: Guide + - name: extras + title: Extras + - name: api + title: API reference diff --git a/internal/docsite/testdata/violations/site-unknown-key/want.txt b/internal/docsite/testdata/violations/site-unknown-key/want.txt new file mode 100644 index 0000000..ed6ffd1 --- /dev/null +++ b/internal/docsite/testdata/violations/site-unknown-key/want.txt @@ -0,0 +1,3 @@ +rule: site +file: docs/site.yaml +message: parse site config diff --git a/internal/docsite/testdata/violations/snippet-absolute-path/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-absolute-path/docs/extras/faq.md new file mode 100644 index 0000000..32a45a4 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-absolute-path/docs/extras/faq.md @@ -0,0 +1,18 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```text src=/etc/hostname +``` diff --git a/internal/docsite/testdata/violations/snippet-absolute-path/want.txt b/internal/docsite/testdata/violations/snippet-absolute-path/want.txt new file mode 100644 index 0000000..98c05ea --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-absolute-path/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: path must be relative to the repository root diff --git a/internal/docsite/testdata/violations/snippet-backslash/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-backslash/docs/extras/faq.md new file mode 100644 index 0000000..3c2ad7b --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-backslash/docs/extras/faq.md @@ -0,0 +1,18 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```text src=config\app.yaml +``` diff --git a/internal/docsite/testdata/violations/snippet-backslash/want.txt b/internal/docsite/testdata/violations/snippet-backslash/want.txt new file mode 100644 index 0000000..2cfd1e3 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-backslash/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: path must use forward slashes diff --git a/internal/docsite/testdata/violations/snippet-directory/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-directory/docs/extras/faq.md new file mode 100644 index 0000000..8db6f7f --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-directory/docs/extras/faq.md @@ -0,0 +1,18 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```text src=config +``` diff --git a/internal/docsite/testdata/violations/snippet-directory/want.txt b/internal/docsite/testdata/violations/snippet-directory/want.txt new file mode 100644 index 0000000..fdcdb42 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-directory/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: path is a directory diff --git a/internal/docsite/testdata/violations/snippet-dotdot/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-dotdot/docs/extras/faq.md new file mode 100644 index 0000000..44581e5 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-dotdot/docs/extras/faq.md @@ -0,0 +1,18 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```text src=../outside.txt +``` diff --git a/internal/docsite/testdata/violations/snippet-dotdot/want.txt b/internal/docsite/testdata/violations/snippet-dotdot/want.txt new file mode 100644 index 0000000..35c8b80 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-dotdot/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: path must not leave the repository root diff --git a/internal/docsite/testdata/violations/snippet-dotfile/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-dotfile/docs/extras/faq.md new file mode 100644 index 0000000..07a7b2c --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-dotfile/docs/extras/faq.md @@ -0,0 +1,18 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```text src=.env +``` diff --git a/internal/docsite/testdata/violations/snippet-dotfile/want.txt b/internal/docsite/testdata/violations/snippet-dotfile/want.txt new file mode 100644 index 0000000..17c47a3 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-dotfile/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: path names a dotfile or .env file diff --git a/internal/docsite/testdata/violations/snippet-drift/docs/guide/start.md b/internal/docsite/testdata/violations/snippet-drift/docs/guide/start.md new file mode 100644 index 0000000..71f095b --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-drift/docs/guide/start.md @@ -0,0 +1,32 @@ +--- +title: Start +description: "Install the demo and greet someone." +section: guide +order: 10 +--- +# Start + +The demo module greets people. + +## Install + +Build the docs with `summer docs:build`, or run the application: + +```sh +$ summer docs:build --out site +./bin/demo serve --addr 127.0.0.1:8080 +``` + +## Greet someone + +Call `demo.Hello`: + +```go src=modules/demo/example_test.go#ExampleHello +fmt.Println(demo.Hello("docs")) +// Output: Hello, blog +``` + +> [!TIP] +> The [demo reference](../../modules/demo/README.md#usage) lists the whole API. + +Continue with [configuration](config.md) or jump back to [install](#install). diff --git a/internal/docsite/testdata/violations/snippet-drift/want.txt b/internal/docsite/testdata/violations/snippet-drift/want.txt new file mode 100644 index 0000000..dc0b9c2 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-drift/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/guide/start.md +message: body differs from modules/demo/example_test.go#ExampleHello (run: summer docs:sync) diff --git a/internal/docsite/testdata/violations/snippet-env-file/config/prod.env b/internal/docsite/testdata/violations/snippet-env-file/config/prod.env new file mode 100644 index 0000000..65ec267 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-env-file/config/prod.env @@ -0,0 +1 @@ +SECRET=1 diff --git a/internal/docsite/testdata/violations/snippet-env-file/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-env-file/docs/extras/faq.md new file mode 100644 index 0000000..dc355c9 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-env-file/docs/extras/faq.md @@ -0,0 +1,18 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```text src=config/prod.env +``` diff --git a/internal/docsite/testdata/violations/snippet-env-file/want.txt b/internal/docsite/testdata/violations/snippet-env-file/want.txt new file mode 100644 index 0000000..17c47a3 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-env-file/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: path names a dotfile or .env file diff --git a/internal/docsite/testdata/violations/snippet-example-no-output/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-example-no-output/docs/extras/faq.md new file mode 100644 index 0000000..3b2c669 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-example-no-output/docs/extras/faq.md @@ -0,0 +1,19 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```go src=modules/demo/example_test.go#ExampleGreeter +_ = demo.Greeter{} +``` diff --git a/internal/docsite/testdata/violations/snippet-example-no-output/modules/demo/example_test.go b/internal/docsite/testdata/violations/snippet-example-no-output/modules/demo/example_test.go new file mode 100644 index 0000000..f1f43f4 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-example-no-output/modules/demo/example_test.go @@ -0,0 +1,16 @@ +package demo_test + +import ( + "fmt" + + "example.com/docfixture/modules/demo" +) + +func ExampleHello() { + fmt.Println(demo.Hello("blog")) + // Output: Hello, blog +} + +func ExampleGreeter() { + _ = demo.Greeter{} +} diff --git a/internal/docsite/testdata/violations/snippet-example-no-output/want.txt b/internal/docsite/testdata/violations/snippet-example-no-output/want.txt new file mode 100644 index 0000000..9ae50ed --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-example-no-output/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: ExampleGreeter has no // Output: comment diff --git a/internal/docsite/testdata/violations/snippet-missing-file/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-missing-file/docs/extras/faq.md new file mode 100644 index 0000000..5a4f6b8 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-missing-file/docs/extras/faq.md @@ -0,0 +1,18 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```go src=modules/demo/nope_test.go +``` diff --git a/internal/docsite/testdata/violations/snippet-missing-file/want.txt b/internal/docsite/testdata/violations/snippet-missing-file/want.txt new file mode 100644 index 0000000..12d31f6 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-missing-file/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: modules/demo/nope_test.go not found diff --git a/internal/docsite/testdata/violations/snippet-missing-ident/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-missing-ident/docs/extras/faq.md new file mode 100644 index 0000000..e23d4d0 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-missing-ident/docs/extras/faq.md @@ -0,0 +1,18 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```go src=modules/demo/demo.go#Nope +``` diff --git a/internal/docsite/testdata/violations/snippet-missing-ident/want.txt b/internal/docsite/testdata/violations/snippet-missing-ident/want.txt new file mode 100644 index 0000000..f2b09f0 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-missing-ident/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: modules/demo/demo.go#Nope not found diff --git a/internal/docsite/testdata/violations/snippet-missing-region/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-missing-region/docs/extras/faq.md new file mode 100644 index 0000000..a033e31 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-missing-region/docs/extras/faq.md @@ -0,0 +1,18 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```yaml src=config/app.yaml#nope +``` diff --git a/internal/docsite/testdata/violations/snippet-missing-region/want.txt b/internal/docsite/testdata/violations/snippet-missing-region/want.txt new file mode 100644 index 0000000..53e53a0 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-missing-region/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: config/app.yaml#nope not found diff --git a/internal/docsite/testdata/violations/snippet-nested-module/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-nested-module/docs/extras/faq.md new file mode 100644 index 0000000..b8d6071 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-nested-module/docs/extras/faq.md @@ -0,0 +1,20 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```go src=nested/x.go#X +// X is x. +const X = 1 +``` diff --git a/internal/docsite/testdata/violations/snippet-nested-module/nested/go.mod b/internal/docsite/testdata/violations/snippet-nested-module/nested/go.mod new file mode 100644 index 0000000..c2e246d --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-nested-module/nested/go.mod @@ -0,0 +1,3 @@ +module example.com/nested + +go 1.27.0 diff --git a/internal/docsite/testdata/violations/snippet-nested-module/nested/x.go b/internal/docsite/testdata/violations/snippet-nested-module/nested/x.go new file mode 100644 index 0000000..85299ce --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-nested-module/nested/x.go @@ -0,0 +1,4 @@ +package nested + +// X is x. +const X = 1 diff --git a/internal/docsite/testdata/violations/snippet-nested-module/nested/x_test.go b/internal/docsite/testdata/violations/snippet-nested-module/nested/x_test.go new file mode 100644 index 0000000..bdb515c --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-nested-module/nested/x_test.go @@ -0,0 +1 @@ +package nested diff --git a/internal/docsite/testdata/violations/snippet-nested-module/want.txt b/internal/docsite/testdata/violations/snippet-nested-module/want.txt new file mode 100644 index 0000000..1e95e3f --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-nested-module/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: path is inside the nested module nested/go.mod diff --git a/internal/docsite/testdata/violations/snippet-no-path/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-no-path/docs/extras/faq.md new file mode 100644 index 0000000..daee178 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-no-path/docs/extras/faq.md @@ -0,0 +1,18 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```text src= +``` diff --git a/internal/docsite/testdata/violations/snippet-no-path/want.txt b/internal/docsite/testdata/violations/snippet-no-path/want.txt new file mode 100644 index 0000000..706521d --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-no-path/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: src= has no path diff --git a/internal/docsite/testdata/violations/snippet-no-tests/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-no-tests/docs/extras/faq.md new file mode 100644 index 0000000..7e27ac2 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-no-tests/docs/extras/faq.md @@ -0,0 +1,20 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```go src=lib/lib.go#A +// A is a. +const A = 1 +``` diff --git a/internal/docsite/testdata/violations/snippet-no-tests/lib/lib.go b/internal/docsite/testdata/violations/snippet-no-tests/lib/lib.go new file mode 100644 index 0000000..d357607 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-no-tests/lib/lib.go @@ -0,0 +1,4 @@ +package lib + +// A is a. +const A = 1 diff --git a/internal/docsite/testdata/violations/snippet-no-tests/want.txt b/internal/docsite/testdata/violations/snippet-no-tests/want.txt new file mode 100644 index 0000000..b6ab228 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-no-tests/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: package has no _test.go files diff --git a/internal/docsite/testdata/violations/snippet-unclean-path/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-unclean-path/docs/extras/faq.md new file mode 100644 index 0000000..d69a1a2 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unclean-path/docs/extras/faq.md @@ -0,0 +1,18 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```text src=config//app.yaml +``` diff --git a/internal/docsite/testdata/violations/snippet-unclean-path/want.txt b/internal/docsite/testdata/violations/snippet-unclean-path/want.txt new file mode 100644 index 0000000..672df00 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unclean-path/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: path must be clean diff --git a/internal/docsite/testdata/violations/snippet-unclosed-fence/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-unclosed-fence/docs/extras/faq.md new file mode 100644 index 0000000..7122784 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unclosed-fence/docs/extras/faq.md @@ -0,0 +1,18 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```go src=modules/demo/demo.go#Hello +func Hello() {} diff --git a/internal/docsite/testdata/violations/snippet-unclosed-fence/want.txt b/internal/docsite/testdata/violations/snippet-unclosed-fence/want.txt new file mode 100644 index 0000000..6a1538f --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unclosed-fence/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: code block has no closing fence diff --git a/internal/docsite/testdata/violations/snippet-unparsable-go/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-unparsable-go/docs/extras/faq.md new file mode 100644 index 0000000..516f716 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unparsable-go/docs/extras/faq.md @@ -0,0 +1,19 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```go src=lib/lib.go#A +x +``` diff --git a/internal/docsite/testdata/violations/snippet-unparsable-go/lib/lib.go b/internal/docsite/testdata/violations/snippet-unparsable-go/lib/lib.go new file mode 100644 index 0000000..e465b07 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unparsable-go/lib/lib.go @@ -0,0 +1,3 @@ +package lib + +func { diff --git a/internal/docsite/testdata/violations/snippet-unparsable-go/lib/lib_test.go b/internal/docsite/testdata/violations/snippet-unparsable-go/lib/lib_test.go new file mode 100644 index 0000000..55c21f8 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unparsable-go/lib/lib_test.go @@ -0,0 +1 @@ +package lib diff --git a/internal/docsite/testdata/violations/snippet-unparsable-go/want.txt b/internal/docsite/testdata/violations/snippet-unparsable-go/want.txt new file mode 100644 index 0000000..21275a2 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unparsable-go/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: cannot parse Go source diff --git a/internal/docsite/testdata/violations/snippet-unrun-func/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-unrun-func/docs/extras/faq.md new file mode 100644 index 0000000..de073a1 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unrun-func/docs/extras/faq.md @@ -0,0 +1,21 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```go src=modules/demo/demo_test.go#helper +func helper() string { + return "x" +} +``` diff --git a/internal/docsite/testdata/violations/snippet-unrun-func/modules/demo/demo_test.go b/internal/docsite/testdata/violations/snippet-unrun-func/modules/demo/demo_test.go new file mode 100644 index 0000000..ad681de --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unrun-func/modules/demo/demo_test.go @@ -0,0 +1,17 @@ +package demo + +import "testing" + +func TestGreet(t *testing.T) { + // docs:start greet + g := Greeter{Name: "blog"} + got := g.Greet() + // docs:end greet + if got != "Hello, blog" { + t.Fatalf("Greet() = %q", got) + } +} + +func helper() string { + return "x" +} diff --git a/internal/docsite/testdata/violations/snippet-unrun-func/want.txt b/internal/docsite/testdata/violations/snippet-unrun-func/want.txt new file mode 100644 index 0000000..d8f2eaa --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unrun-func/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: fragment is not inside a Test or Example function diff --git a/internal/docsite/testdata/violations/snippet-unrun-region/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-unrun-region/docs/extras/faq.md new file mode 100644 index 0000000..f743b8f --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unrun-region/docs/extras/faq.md @@ -0,0 +1,19 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```go src=modules/demo/demo_test.go#orphan +return "x" +``` diff --git a/internal/docsite/testdata/violations/snippet-unrun-region/modules/demo/demo_test.go b/internal/docsite/testdata/violations/snippet-unrun-region/modules/demo/demo_test.go new file mode 100644 index 0000000..376840e --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unrun-region/modules/demo/demo_test.go @@ -0,0 +1,19 @@ +package demo + +import "testing" + +func TestGreet(t *testing.T) { + // docs:start greet + g := Greeter{Name: "blog"} + got := g.Greet() + // docs:end greet + if got != "Hello, blog" { + t.Fatalf("Greet() = %q", got) + } +} + +func helper() string { + // docs:start orphan + return "x" + // docs:end orphan +} diff --git a/internal/docsite/testdata/violations/snippet-unrun-region/want.txt b/internal/docsite/testdata/violations/snippet-unrun-region/want.txt new file mode 100644 index 0000000..d8f2eaa --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unrun-region/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: fragment is not inside a Test or Example function diff --git a/internal/docsite/testdata/violations/snippet-unrun-type/docs/extras/faq.md b/internal/docsite/testdata/violations/snippet-unrun-type/docs/extras/faq.md new file mode 100644 index 0000000..5451ef3 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unrun-type/docs/extras/faq.md @@ -0,0 +1,20 @@ +--- +title: FAQ +description: "Questions about the demo." +section: extras +order: 20 +--- +# FAQ + +## Does it migrate + +Yes, run `./bin/demo migrate`, then `summer docs:sync` after a source change. + +```text +summer not:checked in a text fence +``` + +```go src=modules/demo/demo_test.go#unused +// unused is never referenced. +type unused struct{} +``` diff --git a/internal/docsite/testdata/violations/snippet-unrun-type/modules/demo/demo_test.go b/internal/docsite/testdata/violations/snippet-unrun-type/modules/demo/demo_test.go new file mode 100644 index 0000000..5ca54f6 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unrun-type/modules/demo/demo_test.go @@ -0,0 +1,16 @@ +package demo + +import "testing" + +func TestGreet(t *testing.T) { + // docs:start greet + g := Greeter{Name: "blog"} + got := g.Greet() + // docs:end greet + if got != "Hello, blog" { + t.Fatalf("Greet() = %q", got) + } +} + +// unused is never referenced. +type unused struct{} diff --git a/internal/docsite/testdata/violations/snippet-unrun-type/want.txt b/internal/docsite/testdata/violations/snippet-unrun-type/want.txt new file mode 100644 index 0000000..d8f2eaa --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unrun-type/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/extras/faq.md +message: fragment is not inside a Test or Example function diff --git a/internal/docsite/testdata/violations/snippet-unterminated-region/config/app.yaml b/internal/docsite/testdata/violations/snippet-unterminated-region/config/app.yaml new file mode 100644 index 0000000..5e0b7fe --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unterminated-region/config/app.yaml @@ -0,0 +1,5 @@ +app: + # docs:start db + db: + host: localhost + port: 5432 diff --git a/internal/docsite/testdata/violations/snippet-unterminated-region/want.txt b/internal/docsite/testdata/violations/snippet-unterminated-region/want.txt new file mode 100644 index 0000000..f0df709 --- /dev/null +++ b/internal/docsite/testdata/violations/snippet-unterminated-region/want.txt @@ -0,0 +1,3 @@ +rule: snippet +file: docs/guide/config.md +message: region "db" has no docs:end marker diff --git a/internal/docsite/violations_test.go b/internal/docsite/violations_test.go new file mode 100644 index 0000000..cc63a48 --- /dev/null +++ b/internal/docsite/violations_test.go @@ -0,0 +1,421 @@ +package docsite + +import ( + "errors" + "io/fs" + "os" + "path/filepath" + "slices" + "strings" + "testing" +) + +// The planted-violation corpus. testdata/clean is a small repository root +// that passes every check. Each testdata/violations/ directory holds +// the files that differ from the clean fixture (copied over it, same +// relative paths), an optional remove.txt listing clean files to delete, +// and a want.txt naming the one problem the plant must produce: +// +// rule: frontmatter +// file: docs/guide/config.md +// message: unknown field "colour" +// +// The file line is optional; message is a substring of the problem +// message. A case passes only when Check reports exactly that one problem, +// so a checker that stops reporting a rule, or reports it under another +// rule, turns its case red. + +const ( + cleanFixture = "testdata/clean" + violationsFixture = "testdata/violations" +) + +// violationCommands is the command set the corpus is checked with. +var violationCommands = &Commands{Tool: []string{"docs:build", "docs:sync"}, App: []string{"serve", "migrate"}} + +// copyTree copies the regular files under src into dst. +func copyTree(t *testing.T, src, dst string) { + t.Helper() + err := filepath.WalkDir(src, func(p string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + rel, err := filepath.Rel(src, p) + if err != nil { + return err + } + target := filepath.Join(dst, 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.Fatalf("copy %s: %v", src, err) + } +} + +// cleanRoot copies the clean fixture into a fresh temp dir. +func cleanRoot(t *testing.T) string { + t.Helper() + root := t.TempDir() + copyTree(t, cleanFixture, root) + return root +} + +type wantProblem struct { + rule, file, message string +} + +func (w wantProblem) matches(p Problem) bool { + return p.Rule == w.rule && (w.file == "" || p.File == w.file) && strings.Contains(p.Message, w.message) +} + +func parseWant(t *testing.T, path string) wantProblem { + t.Helper() + raw, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + var w wantProblem + for _, line := range strings.Split(string(raw), "\n") { + key, value, ok := strings.Cut(line, ": ") + if !ok { + continue + } + switch key { + case "rule": + w.rule = value + case "file": + w.file = value + case "message": + w.message = value + default: + t.Fatalf("%s: unknown key %q", path, key) + } + } + if w.rule == "" || w.message == "" { + t.Fatalf("%s: want.txt needs a rule and a message", path) + } + return w +} + +// plantCase builds the scratch root of one violation case. +func plantCase(t *testing.T, dir string) string { + t.Helper() + root := cleanRoot(t) + err := filepath.WalkDir(dir, func(p string, d fs.DirEntry, err error) error { + if err != nil || d.IsDir() { + return err + } + rel, err := filepath.Rel(dir, p) + if err != nil { + return err + } + if rel == "want.txt" || rel == "remove.txt" { + return nil + } + data, err := os.ReadFile(p) + if err != nil { + return err + } + target := filepath.Join(root, rel) + if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil { + return err + } + return os.WriteFile(target, data, 0o644) + }) + if err != nil { + t.Fatal(err) + } + if raw, err := os.ReadFile(filepath.Join(dir, "remove.txt")); err == nil { + for _, name := range strings.Fields(string(raw)) { + if err := os.Remove(filepath.Join(root, filepath.FromSlash(name))); err != nil { + t.Fatalf("remove.txt: %v", err) + } + } + } else if !errors.Is(err, fs.ErrNotExist) { + t.Fatal(err) + } + return root +} + +// assertOneProblem requires exactly one problem, matching want. +func assertOneProblem(t *testing.T, problems []Problem, want wantProblem) { + t.Helper() + if len(problems) != 1 || !want.matches(problems[0]) { + t.Fatalf("problems =\n%s\nwant exactly one %s problem in %q containing %q", + strings.Join(problemLines(problems), "\n"), want.rule, want.file, want.message) + } +} + +func TestCleanFixture(t *testing.T) { + root := cleanRoot(t) + problems, err := Check(Options{Root: root, Commands: violationCommands}) + if err != nil { + t.Fatal(err) + } + if len(problems) > 0 { + t.Fatalf("clean fixture has problems:\n%s", strings.Join(problemLines(problems), "\n")) + } + out := filepath.Join(t.TempDir(), "site") + res, problems, err := Build(Options{Root: root, Out: out, Commands: violationCommands}) + if err != nil || len(problems) > 0 { + t.Fatalf("Build: %v %q", err, problemLines(problems)) + } + // index, two guide pages, two extras pages and the demo API page. + if res.Pages != 6 { + t.Fatalf("pages = %d, want 6", res.Pages) + } + res2, problems, err := Sync(Options{Root: root}) + if err != nil || len(problems) > 0 || res2 != (SyncResult{}) { + t.Fatalf("Sync on the clean fixture = %+v %q %v, want up to date", res2, problemLines(problems), err) + } +} + +func TestPlantedViolations(t *testing.T) { + entries, err := os.ReadDir(violationsFixture) + if err != nil { + t.Fatal(err) + } + var cases []string + for _, e := range entries { + if e.IsDir() { + cases = append(cases, e.Name()) + } + } + if len(cases) < 30 { + t.Fatalf("%d violation cases, want at least 30", len(cases)) + } + for _, name := range cases { + t.Run(name, func(t *testing.T) { + dir := filepath.Join(violationsFixture, name) + want := parseWant(t, filepath.Join(dir, "want.txt")) + root := plantCase(t, dir) + problems, err := Check(Options{Root: root, Commands: violationCommands}) + if err != nil { + t.Fatal(err) + } + assertOneProblem(t, problems, want) + + // Build must refuse the same tree and write nothing. + out := filepath.Join(t.TempDir(), "site") + if _, problems, err := Build(Options{Root: root, Out: out, Commands: violationCommands}); err != nil || len(problems) == 0 { + t.Fatalf("Build accepted the planted tree: %v", err) + } + if _, err := os.Stat(out); !errors.Is(err, fs.ErrNotExist) { + t.Fatalf("Build wrote %s despite a problem", out) + } + }) + } + + // Plants that cannot be committed: the forbidden word (assembled from + // split literals, so no committed file names a consuming application) + // and a symlink that leaves the root. + word := "fono" + "teka" + t.Run("forbidden-source", func(t *testing.T) { + root := cleanRoot(t) + appendFile(t, root, "docs/extras/faq.md", "\nThe "+word+" application.\n") + problems, err := Check(Options{Root: root, Commands: violationCommands}) + if err != nil { + t.Fatal(err) + } + assertOneProblem(t, problems, wantProblem{rule: "forbidden", file: "docs/extras/faq.md", message: forbiddenMessage}) + if strings.Contains(strings.ToLower(problems[0].String()), word) { + t.Fatalf("problem line repeats the match: %s", problems[0]) + } + }) + t.Run("forbidden-accented", func(t *testing.T) { + root := cleanRoot(t) + appendFile(t, root, "docs/extras/faq.md", "\nSee "+"P"+"ł"+"ý"+"tarium.\n") + problems, err := Check(Options{Root: root, Commands: violationCommands}) + if err != nil { + t.Fatal(err) + } + assertOneProblem(t, problems, wantProblem{rule: "forbidden", file: "docs/extras/faq.md", message: forbiddenMessage}) + }) + t.Run("forbidden-output", func(t *testing.T) { + root := cleanRoot(t) + site := filepath.Join(root, "docs/site.yaml") + raw, err := os.ReadFile(site) + if err != nil { + t.Fatal(err) + } + raw = []byte(strings.Replace(string(raw), "the clean docs checker fixture", "the "+word+" fixture", 1)) + if err := os.WriteFile(site, raw, 0o644); err != nil { + t.Fatal(err) + } + problems, err := Check(Options{Root: root, Commands: violationCommands}) + if err != nil { + t.Fatal(err) + } + // site.yaml is not a page, so only the rendered outputs carry it: + // every page's text outputs, llms.txt and llms-full.txt. + if len(problems) == 0 || !slices.ContainsFunc(problems, func(p Problem) bool { return p.File == "llms.txt" }) { + t.Fatalf("output scan missed llms.txt: %q", problemLines(problems)) + } + for _, p := range problems { + if p.Rule != "forbidden" || p.Message != forbiddenMessage { + t.Fatalf("unexpected problem %s", p) + } + } + }) + t.Run("snippet-symlink-escape", func(t *testing.T) { + root := cleanRoot(t) + outside := filepath.Join(t.TempDir(), "secret.txt") + if err := os.WriteFile(outside, []byte("secret\n"), 0o644); err != nil { + t.Fatal(err) + } + if err := os.Symlink(outside, filepath.Join(root, "config", "escape.txt")); err != nil { + t.Fatal(err) + } + appendFile(t, root, "docs/extras/faq.md", "\n```text src=config/escape.txt\nsecret\n```\n") + problems, err := Check(Options{Root: root, Commands: violationCommands}) + if err != nil { + t.Fatal(err) + } + assertOneProblem(t, problems, wantProblem{rule: "snippet", file: "docs/extras/faq.md", message: "path resolves outside the repository root"}) + }) + t.Run("snippet-symlink-dotfile", func(t *testing.T) { + root := cleanRoot(t) + if err := os.WriteFile(filepath.Join(root, ".secret"), []byte("secret\n"), 0o644); err != nil { + t.Fatal(err) + } + if err := os.Symlink(filepath.Join(root, ".secret"), filepath.Join(root, "config", "alias.txt")); err != nil { + t.Fatal(err) + } + appendFile(t, root, "docs/extras/faq.md", "\n```text src=config/alias.txt\nsecret\n```\n") + problems, err := Check(Options{Root: root, Commands: violationCommands}) + if err != nil { + t.Fatal(err) + } + assertOneProblem(t, problems, wantProblem{rule: "snippet", file: "docs/extras/faq.md", message: "path resolves to a dotfile or .env file"}) + }) +} + +func appendFile(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) + } +} + +// TestBuildOutputGuard asserts that Build refuses an output directory that +// would overwrite the sources or an unrelated directory, and leaves a +// sentinel file in it untouched. +func TestBuildOutputGuard(t *testing.T) { + for _, tc := range []struct { + name string + // opts returns the Src and Out for a fixture root and the directory + // the sentinel goes into. + opts func(root string) (src, out, sentinelDir string) + want string + }{ + {"out-equals-root", func(root string) (string, string, string) { + return "", root, root + }, "--out must not be inside --src or equal to the repository root"}, + {"out-inside-src", func(root string) (string, string, string) { + return "", filepath.Join(root, "docs", "site"), filepath.Join(root, "docs") + }, "--out must not be inside --src or equal to the repository root"}, + {"out-equals-src", func(root string) (string, string, string) { + return "", filepath.Join(root, "docs"), filepath.Join(root, "docs") + }, "--out must not be inside --src or equal to the repository root"}, + {"out-contains-src", func(root string) (string, string, string) { + return filepath.Join(root, "content", "docs"), filepath.Join(root, "content"), filepath.Join(root, "content") + }, "--out must not be inside --src or equal to the repository root"}, + {"out-contains-root", func(root string) (string, string, string) { + return "", filepath.Dir(root), filepath.Dir(root) + }, "--out must not be inside --src or equal to the repository root"}, + {"out-unmarked", func(root string) (string, string, string) { + out := filepath.Join(t.TempDir(), "unrelated") + return "", out, out + }, "has no .summer-docs marker"}, + } { + t.Run(tc.name, func(t *testing.T) { + root := cleanRoot(t) + src, out, sentinelDir := tc.opts(root) + if src != "" { + if err := os.MkdirAll(filepath.Dir(src), 0o755); err != nil { + t.Fatal(err) + } + if err := os.Rename(filepath.Join(root, "docs"), src); err != nil { + t.Fatal(err) + } + } + if err := os.MkdirAll(sentinelDir, 0o755); err != nil { + t.Fatal(err) + } + sentinel := filepath.Join(sentinelDir, "sentinel.txt") + if err := os.WriteFile(sentinel, []byte("keep me\n"), 0o644); err != nil { + t.Fatal(err) + } + _, problems, err := Build(Options{Root: root, Src: src, Out: out, Commands: violationCommands}) + if err == nil || !strings.Contains(err.Error(), tc.want) { + t.Fatalf("Build = %v (problems %q), want an error containing %q", err, problemLines(problems), tc.want) + } + if got, err := os.ReadFile(sentinel); err != nil || string(got) != "keep me\n" { + t.Fatalf("sentinel changed: %q %v", got, err) + } + if _, err := os.Stat(filepath.Join(out, MarkerFile)); !errors.Is(err, fs.ErrNotExist) { + t.Fatalf("Build wrote the marker into a refused --out") + } + }) + } + + t.Run("out-symlink-into-src", func(t *testing.T) { + root := cleanRoot(t) + link := filepath.Join(t.TempDir(), "site") + if err := os.Symlink(filepath.Join(root, "docs"), link); err != nil { + t.Fatal(err) + } + _, _, err := Build(Options{Root: root, Out: link, Commands: violationCommands}) + if !errors.Is(err, errOutInside) { + t.Fatalf("Build through a symlink into docs/ = %v, want errOutInside", err) + } + if _, err := os.Stat(filepath.Join(root, "docs", "index.md")); err != nil { + t.Fatalf("docs/index.md removed: %v", err) + } + }) + + t.Run("out-marked-is-cleaned", func(t *testing.T) { + root := cleanRoot(t) + out := filepath.Join(t.TempDir(), "site") + if err := os.MkdirAll(filepath.Join(out, "stale"), 0o755); err != nil { + t.Fatal(err) + } + for _, name := range []string{MarkerFile, "stale/old.html"} { + if err := os.WriteFile(filepath.Join(out, name), []byte("x"), 0o644); err != nil { + t.Fatal(err) + } + } + if _, problems, err := Build(Options{Root: root, Out: out, Commands: violationCommands}); err != nil || len(problems) > 0 { + t.Fatalf("Build into a marked directory: %v %q", err, problemLines(problems)) + } + if _, err := os.Stat(filepath.Join(out, "stale")); !errors.Is(err, fs.ErrNotExist) { + t.Fatal("a stale file survived the clean") + } + if _, err := os.Stat(filepath.Join(out, "index.html")); err != nil { + t.Fatal(err) + } + }) + + t.Run("out-empty-dir", func(t *testing.T) { + root := cleanRoot(t) + out := t.TempDir() + if _, problems, err := Build(Options{Root: root, Out: out, Commands: violationCommands}); err != nil || len(problems) > 0 { + t.Fatalf("Build into an empty directory: %v %q", err, problemLines(problems)) + } + if _, err := os.Stat(filepath.Join(out, MarkerFile)); err != nil { + t.Fatal(err) + } + }) +} diff --git a/scripts/check-phase11.1.sh b/scripts/check-phase11.1.sh index 60a8699..2645a59 100755 --- a/scripts/check-phase11.1.sh +++ b/scripts/check-phase11.1.sh @@ -152,6 +152,71 @@ run_go() { echo "phase11.1 go passed" } +# detect_json reads a go test -json log. It refuses a build failure, any +# failed test or package, any skipped test, "no tests to run", a run with +# zero passing tests, and any required " " pair (from +# PHASE11_1_REQUIRE, newline separated) that did not PASS. +detect_json() { + python3 - "$1" <<'PY' +import json, os, sys +path = sys.argv[1] +require = [r.strip() for r in os.environ.get("PHASE11_1_REQUIRE", "").splitlines() if r.strip()] +passed = set() +with open(path, encoding="utf-8", errors="replace") as fh: + for raw in fh: + line = raw.strip() + if not line.startswith("{"): + continue + try: + ev = json.loads(line) + except json.JSONDecodeError: + print("refuse: non-json test output", file=sys.stderr) + sys.exit(4) + action, test, pkg = ev.get("Action"), ev.get("Test") or "", ev.get("Package") or "" + if action == "build-fail" or (action == "fail" and ev.get("FailedBuild")): + print(f"refuse: build failed in {pkg}", file=sys.stderr) + sys.exit(1) + if action == "output" and "no tests to run" in (ev.get("Output") or ""): + print(f"refuse: no tests to run in {pkg}", file=sys.stderr) + sys.exit(3) + if action == "skip" and test: + print(f"refuse: skipped {pkg} {test}", file=sys.stderr) + sys.exit(2) + if action == "fail": + print(f"refuse: failed {pkg} {test}".rstrip(), file=sys.stderr) + sys.exit(1) + if action == "pass" and test: + passed.add(f"{pkg} {test}") +if not passed: + print("refuse: zero tests", file=sys.stderr) + sys.exit(3) +missing = [r for r in require if r not in passed] +if missing: + print("refuse: named tests did not pass (missing, renamed or filtered out): " + ", ".join(missing), file=sys.stderr) + sys.exit(5) +PY +} + +# go_json_named runs `go test -json -count=1 -run '^(names)$'` for one +# package and requires every named test to PASS. +go_json_named() { + local pkg="$1" log import names name require="" code=0 + shift + import="$(cd "$ROOT" && go list -f '{{.ImportPath}}' "$pkg")" || refuse "go list $pkg failed" || return 1 + names="$(IFS='|'; echo "$*")" + for name in "$@"; do + require+="$import $name"$'\n' + done + log="$(mktemp)" + (cd "$ROOT" && go test -json -count=1 "$pkg" -run "^($names)\$" >"$log" 2>&1) || code=$? + if ! PHASE11_1_REQUIRE="$require" detect_json "$log"; then + rm -f "$log" + return 1 + fi + rm -f "$log" + [ "$code" -eq 0 ] || refuse "go test $pkg exited $code" +} + # expect_refusal runs docs:build --check on the scratch root and requires a # non-zero exit whose output names the rule. expect_refusal() { @@ -181,7 +246,10 @@ run_self_test() { summer="$tmp/summer" build_summer "$summer" mkdir -p "$scratch" "$pristine" - cp -R "$ROOT/docs" "$ROOT/modules" "$ROOT/go.mod" "$ROOT/go.sum" "$scratch/" + cp -R "$ROOT/docs" "$ROOT/modules" "$ROOT/go.mod" "$ROOT/go.sum" "$ROOT/README.md" "$scratch/" + # The command checker reads bonfire.Command literals under examples/ + # (the tracked files only: examples/hello/bin holds built binaries). + git -C "$ROOT" ls-files -z examples | tar -C "$ROOT" --null -T - -cf - | tar -C "$scratch" -xf - cp -R "$ROOT/docs" "$pristine/" (cd "$scratch" && GOWORK=off "$summer" docs:build --check --root "$scratch" >/dev/null) || @@ -225,6 +293,12 @@ run_self_test() { expect_refusal 'unknown callout' 'callout: unknown type DANGER (use NOTE, TIP or WARNING)' "$summer" "$scratch" restore "$pristine" "$scratch" docs/index.md + # The planted-violation corpus: one fixture per checker rule, each of + # which must fail for its own rule, and the clean fixture. Skips, zero + # matches and "no tests to run" refuse. + go_json_named ./internal/docsite TestPlantedViolations TestCleanFixture || + refuse "self-test: the planted-violation corpus did not pass" + echo "phase11.1 self-test passed" }