package docs import ( "os" "path/filepath" "regexp" "strings" "testing" ) // TestDocsRelativeLinks keeps the developer docs tree internally navigable: // every relative markdown link must point at a file that exists. The // api-reference overview once shipped 20+ directory-style links // (authentication/ etc.) that 404'd — this gate makes that class of rot // fail make test. (docs/user is excluded: end-user prose has its own pass.) // // Rules: // - http(s):, mailto:, and pure #anchors are skipped // - every other target must end in .md and resolve (relative to the // linking file) to an existing file inside the tree // - directory-style targets ("area/") are the exact bug this test exists // for and always fail var docsLinkRe = regexp.MustCompile(`\[[^\]]*\]\(([^)\s]+)\)`) func TestDocsRelativeLinks(t *testing.T) { // Walk developer docs; escape-check against the whole docs tree — // cross-area links into docs/user are legitimate (same site). devRoot, err := filepath.Abs("../../docs/developer") if err != nil { t.Fatalf("abs developer root: %v", err) } treeRoot, err := filepath.Abs("../../docs") if err != nil { t.Fatalf("abs docs root: %v", err) } err = filepath.WalkDir(devRoot, func(path string, d os.DirEntry, err error) error { if err != nil || d.IsDir() || !strings.HasSuffix(path, ".md") { return err } raw, err := os.ReadFile(path) if err != nil { return err } rel, _ := filepath.Rel(docsRoot, path) absPath, err := filepath.Abs(path) if err != nil { return err } for _, m := range docsLinkRe.FindAllStringSubmatch(string(raw), -1) { target := m[1] if strings.HasPrefix(target, "http://") || strings.HasPrefix(target, "https://") || strings.HasPrefix(target, "mailto:") { continue } if strings.HasPrefix(target, "#") { continue // in-page anchor } // strip a trailing fragment for file resolution fileTarget := target if i := strings.Index(fileTarget, "#"); i >= 0 { fileTarget = fileTarget[:i] } if fileTarget == "" { continue // pure-anchor form already handled; empty = odd, skip } if !strings.HasSuffix(fileTarget, ".md") { t.Errorf("%s: link target %q is not a .md file — directory-style links 404 on the docs site; link the file (e.g. %q)", rel, target, strings.TrimSuffix(fileTarget, "/")+".md") continue } resolved := filepath.Clean(filepath.Join(filepath.Dir(absPath), fileTarget)) if !strings.HasPrefix(resolved, treeRoot+string(filepath.Separator)) { t.Errorf("%s: link target %q escapes the docs tree", rel, target) continue } if _, err := os.Stat(resolved); err != nil { t.Errorf("%s: link target %q does not resolve to an existing file (%s)", rel, target, resolved) } } return nil }) if err != nil { t.Fatalf("walk docs: %v", err) } }