bruno: regenerate collection from canonical docs + permanent docs↔Bruno parity gate

WO4. The old collection was the drifted artifact AGENTS.md warned about:
bodies pre-truncated at their first {{var}}, routes renamed server-side
but not here (/api/notes/:media_id, /api/book-matching/search,
/api/collections/:id/rules/test), an orphan universal-progress suite for
routes that no longer exist.

scripts/gen_bruno_from_docs.py emits one OpenCollection request per
frontmatter'd endpoint doc (191 requests): gate-verified example-request
JSON as body, {{env}} params per the environment file, device-token
routes get an explicit Bearer {{device_token}} header, login/register/
refresh keep the token-capture after-response scripts that chain the
collection. Old 1:1 mirrors deleted (128); hand-authored NewDevDBSetup,
environments, and edge-case suites kept. 30 scenario files with
truncated bodies (the documented corruption) deleted — they could not
run.

internal/docs/drift_test.go gains TestBrunoMirrorsDocs: every
frontmatter'd doc must have a Bruno request with matching method+URL
shape — the collection can never silently drift from the docs again.
opencollection.yml README block replaced with the real run instructions.

make test green (9 pkgs, parity test included in internal/docs).
This commit is contained in:
John O'Keefe committed 2026-10-24 18:35:51 -04:00
1 parent 6fc501b35a
commit acd82fff0a
353 files changed
+5165 -5293

No files matched your search

+57
View File
@@ -186,6 +186,63 @@ func parseHandlerMiddleware(rest string) []string {
// ---------- the gate ----------
// TestBrunoMirrorsDocs keeps the executable-verification layer honest:
// every frontmatter'd endpoint doc must have a generated OpenCollection
// (Bruno) request whose method and URL shape match. Bruno is regenerated
// from the docs (scripts/gen_bruno_from_docs.py); if this test fails, the
// collection drifted from the canonical docs or a new doc was added
// without regenerating.
func TestBrunoMirrorsDocs(t *testing.T) {
docsDir := "../../docs/developer/api"
docs, err := LoadAPIDocs(docsDir)
if err != nil {
t.Fatalf("walk %s: %v", docsDir, err)
}
// Collect bruno requests: "METHOD normalized-path" -> relpath.
bruno := map[string]string{}
files, _ := filepath.Glob("../../bruno/**/*.yml")
for _, file := range files {
rel, _ := filepath.Rel("../../bruno", file)
parts := strings.Split(rel, string(filepath.Separator))
if len(parts) > 0 && (parts[0] == "NewDevDBSetup" || parts[0] == "environments") {
continue
}
if strings.Contains(file, "/scenarios/") || strings.Contains(file, "/filters/") || strings.Contains(file, "/search/") {
continue // hand-authored edge-case suites, not 1:1 mirrors
}
raw, err := os.ReadFile(file)
if err != nil {
t.Fatalf("read %s: %v", file, err)
}
var method, url string
for _, line := range strings.Split(string(raw), "\n") {
s := strings.TrimSpace(line)
if m := regexp.MustCompile(`^method:\s*(\w+)$`).FindStringSubmatch(s); m != nil && method == "" {
method = strings.ToUpper(m[1])
}
if m := regexp.MustCompile(`^url:\s*'?([^']+)`).FindStringSubmatch(s); m != nil && url == "" {
url = m[1]
}
}
if method == "" || url == "" {
continue // folder.yml and other metadata
}
// {{base_url}}/api/x/{{var}} -> /api/x/{} (params collapse to {})
path := regexp.MustCompile(`^\{\{(?:base|opds_base)_url\}\}`).ReplaceAllString(url, "")
path = regexp.MustCompile(`\{\{[^}]+\}\}`).ReplaceAllString(path, "{}")
bruno[method+" "+path] = rel
}
for _, doc := range docs {
path := regexp.MustCompile(`:[^/{}]+`).ReplaceAllString(doc.Frontmatter.Path, "{}")
key := doc.Frontmatter.Method + " " + path
if _, ok := bruno[key]; !ok {
t.Errorf("%s: no Bruno request mirrors %q — run scripts/gen_bruno_from_docs.py --apply", doc.RelPath, key)
}
}
}
func TestDocsMatchRoutes(t *testing.T) {
routes := extractRoutesFromRouterSource(t)