docsite: pin the anchor rule in a fixture both languages read
The website's slugify and docsite.Anchor have to agree exactly or a fragment link passes the Go gate and 404s in a browser. They now assert against docs/site/anchors.json rather than against each other, and the cases are shapes CARLOS headings actually take: backticked flags, a slashed channel name, an HTTP header.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2 files changed,
+35
−0
docs/site/anchors.json+11 −0internal/docsite/links_test.go+24 −0
diff --git a/docs/site/anchors.json b/docs/site/anchors.json| new file mode 100644 |
| index 000000000..4f680cecf |
| --- /dev/null |
| +++ b/docs/site/anchors.json |
| @@ -0,0 +1,11 @@ |
| +{ |
| + "Getting started": "getting-started", |
| + "The `carlos ship` contract": "the-carlos-ship-contract", |
| + "404, never 403": "404-never-403", |
| + "Routes & instances": "routes-instances", |
| + " Leading and trailing ": "leading-and-trailing", |
| + "Hyphen-joined words": "hyphen-joined-words", |
| + "`-app` and `--app`": "app-and-app", |
| + "canary/rehearsal": "canary-rehearsal", |
| + "X-Carlos-Version": "x-carlos-version" |
| +} |
diff --git a/internal/docsite/links_test.go b/internal/docsite/links_test.go| index b1a5dc96f..fa7bf99eb 100644 |
| --- a/internal/docsite/links_test.go |
| +++ b/internal/docsite/links_test.go |
| @@ -1,7 +1,9 @@ |
| package docsite |
| import ( |
| + "encoding/json" |
| "os" |
| + "path/filepath" |
| "testing" |
| ) |
| @@ -91,3 +93,25 @@ func TestAnchorsAreUnique(t *testing.T) { |
| } |
| } |
| } |
| + |
| +// TestAnchorMatchesFixture pins the anchor rule itself. The website's |
| +// slugify reads the same file, so the two implementations assert |
| +// against a shared fixture rather than against each other. |
| +func TestAnchorMatchesFixture(t *testing.T) { |
| + raw, err := os.ReadFile(filepath.Join(root, "anchors.json")) |
| + if err != nil { |
| + t.Fatalf("read fixture: %v", err) |
| + } |
| + var cases map[string]string |
| + if err := json.Unmarshal(raw, &cases); err != nil { |
| + t.Fatalf("parse fixture: %v", err) |
| + } |
| + if len(cases) < 5 { |
| + t.Fatalf("fixture has %d cases — too few to pin anything", len(cases)) |
| + } |
| + for in, want := range cases { |
| + if got := Anchor(in); got != want { |
| + t.Errorf("Anchor(%q) = %q, want %q", in, got, want) |
| + } |
| + } |
| +} |