carlos / platform Public

docsite: gate internal links, their fragments, and anchor uniqueness

The fragment half is the one that earns its keep: a link to a renamed
heading still looks right in review and still lands the reader at
the top of the wrong page.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 50e227e3c7d4bfb2106fafa5cf1acc8d2d9aa964 parent 4442fb6
1 file changed, +93 −0
  • internal/docsite/links_test.go +93 −0
diff --git a/internal/docsite/links_test.go b/internal/docsite/links_test.go
new file mode 100644
index 000000000..b1a5dc96f
--- /dev/null
+++ b/internal/docsite/links_test.go
@@ -0,0 +1,93 @@
+package docsite
+
+import (
+ "os"
+ "testing"
+)
+
+// TestInternalLinksResolve stops a docs link 404ing.
+//
+// It checks the fragment as well as the page, because a link to a
+// renamed heading is the failure that actually happens: the page still
+// exists, the link still looks right in review, and the reader lands at
+// the top of a long page wondering what they missed.
+func TestInternalLinksResolve(t *testing.T) {
+ if _, err := os.Stat(root + "/nav.json"); os.IsNotExist(err) {
+ t.Skip("no corpus yet — Task 8 creates docs/site/nav.json")
+ }
+ site, err := Load(root)
+ if err != nil {
+ t.Fatalf("load: %v", err)
+ }
+ for _, p := range site.Pages() {
+ for _, l := range p.Links {
+ target, ok := site.BySlug[l.Target]
+ if !ok {
+ t.Errorf("%s.md:%d: links to /docs/%s, which is not a page",
+ p.Slug, l.Line, l.Target)
+ continue
+ }
+ if l.Fragment == "" {
+ continue
+ }
+ if !hasAnchor(target, l.Fragment) {
+ t.Errorf("%s.md:%d: links to /docs/%s#%s, but %s has no heading anchoring %q",
+ p.Slug, l.Line, l.Target, l.Fragment, l.Target, l.Fragment)
+ }
+ }
+ }
+}
+
+func hasAnchor(p *Page, anchor string) bool {
+ for _, h := range p.Headings {
+ if h.Anchor == anchor {
+ return true
+ }
+ }
+ return false
+}
+
+// TestLinkCheckCatchesADeadFragment proves the fragment half fires.
+func TestLinkCheckCatchesADeadFragment(t *testing.T) {
+ site, err := Load(testCorpus)
+ if err != nil {
+ t.Fatalf("load: %v", err)
+ }
+ cli := site.BySlug["reference/cli"]
+ if cli == nil {
+ t.Fatal("no reference/cli page")
+ }
+ if !hasAnchor(cli, "carlos-ship") {
+ t.Error("expected reference/cli to anchor carlos-ship")
+ }
+ if hasAnchor(cli, "carlos-teleport") {
+ t.Error("hasAnchor invented an anchor that is not there")
+ }
+}
+
+// TestAnchorsAreUnique matters because a duplicate anchor makes a link
+// to it ambiguous, and the renderer resolves that by silently suffixing
+// the second one. The link then goes to the wrong section rather than
+// nowhere, which is harder to notice.
+func TestAnchorsAreUnique(t *testing.T) {
+ if _, err := os.Stat(root + "/nav.json"); os.IsNotExist(err) {
+ t.Skip("no corpus yet — Task 8 creates docs/site/nav.json")
+ }
+ site, err := Load(root)
+ if err != nil {
+ t.Fatalf("load: %v", err)
+ }
+ for _, p := range site.Pages() {
+ seen := map[string]string{}
+ for _, h := range p.Headings {
+ if h.Level == 1 {
+ continue
+ }
+ if prev, dup := seen[h.Anchor]; dup {
+ t.Errorf("%s.md: headings %q and %q both anchor to #%s",
+ p.Slug, prev, h.Text, h.Anchor)
+ }
+ seen[h.Anchor] = h.Text
+ }
+ }
+}