fix: address docs checker review findings
Check every link reference definition, decode destinations per CommonMark, report correct lines for empty headings, autolinks and nested definitions, reject symlinked record directories and wrong-case path components, and refuse titles that cannot pass. Only top-level headings count (spec section 6). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
789931d90f
commit
d579e38ba5
8 files changed
+146
-27
No files matched your search
@@ -235,6 +235,8 @@ func TestCheck(t *testing.T) {
|
||||
{name: "null empty", files: map[string]string{aPath: sub(adrBase, "summary: Run jobs on a queue.", "summary:")}, want: []string{aPath + ":6: must not be null"}},
|
||||
{name: "null word", files: map[string]string{aPath: sub(adrBase, "summary: Run jobs on a queue.", "summary: Null")}, want: []string{aPath + ":6: must not be null"}},
|
||||
{name: "scalars are literal text", files: map[string]string{aPath: sub(adrBase, "title: Use Queue", "title: 2026", "# Use Queue", "# 2026", "date: 2026-10-23", `date: "2026-10-23"`, "summary: Run jobs on a queue.", `summary: "null"`)}},
|
||||
{name: "null lowercase", files: map[string]string{aPath: sub(adrBase, "summary: Run jobs on a queue.", "summary: null")}, want: []string{aPath + ":6: must not be null"}},
|
||||
{name: "null uppercase", files: map[string]string{aPath: sub(adrBase, "summary: Run jobs on a queue.", "summary: NULL")}, want: []string{aPath + ":6: must not be null"}},
|
||||
{name: "list item is null", files: map[string]string{aPath: sub(adrBase, "related: []", "related:\n - ~")}, want: []string{aPath + ":12: must not be null"}},
|
||||
|
||||
// 3.1 ADR keys
|
||||
@@ -272,6 +274,11 @@ func TestCheck(t *testing.T) {
|
||||
{name: "this PR placeholder", files: map[string]string{aPath: sub(adrBase, "implemented_by: []", "implemented_by:\n - This PR")}, want: []string{aPath + ":14: not durable"}},
|
||||
{name: "fragment in frontmatter", files: map[string]string{aPath: sub(adrBase, "implemented_by: []", "implemented_by:\n - /docs/spec.txt#x")}, want: []string{aPath + ":14: fragments"}},
|
||||
{name: "credentials in frontmatter URL", files: map[string]string{aPath: sub(adrBase, "implemented_by: []", "implemented_by:\n - https://u:p@example.com/x")}, want: []string{aPath + ":14: credentials"}},
|
||||
{name: "mailto in frontmatter", files: map[string]string{aPath: sub(adrBase, "implemented_by: []", "implemented_by:\n - mailto:a@example.com")}, want: []string{aPath + ":14: scheme"}},
|
||||
{name: "anchor in frontmatter", files: map[string]string{aPath: sub(adrBase, "implemented_by: []", "implemented_by:\n - \"#anchor\"")}, want: []string{aPath + ":14: relative"}},
|
||||
{name: "symlink escape in frontmatter", files: map[string]string{aPath: sub(adrBase, "implemented_by: []", "implemented_by:\n - /docs/out/f.md")}, links: map[string]string{"docs/out": "@outside"}, want: []string{aPath + ":14: outside the repository"}},
|
||||
{name: "percent-encoded self reference", files: map[string]string{aPath: sub(adrBase, "implemented_by: []", "implemented_by:\n - /docs/adr/20261023T204512345Z-use%2Dqueue.md")}, want: []string{aPath + ":14: itself"}},
|
||||
{name: "supersedes rejects plan path", files: map[string]string{aPath: sub(adrBase, "status: proposed", "status: accepted", "supersedes: []", "supersedes:\n - /"+pPath)}, want: []string{aPath + ":10: supersedes"}},
|
||||
{name: "relative path in frontmatter", files: map[string]string{aPath: sub(adrBase, "implemented_by: []", "implemented_by:\n - ../docs/spec.txt")}, want: []string{aPath + ":14: relative"}},
|
||||
|
||||
// 4. Status
|
||||
@@ -298,6 +305,9 @@ func TestCheck(t *testing.T) {
|
||||
{name: "superseder is proposed", files: map[string]string{
|
||||
aPath: sub(adrBase, "supersedes: []", "supersedes:\n - /"+bPath),
|
||||
bPath: sub(bBase, "status: accepted", "status: superseded", "superseded_by: []", "superseded_by:\n - /"+aPath)}, want: []string{aPath + ":9: must be accepted"}},
|
||||
{name: "superseder is deprecated", files: map[string]string{
|
||||
aPath: sub(adrBase, "status: proposed", "status: superseded", "superseded_by: []", "superseded_by:\n - /"+bPath),
|
||||
bPath: sub(bBase, "status: accepted", "status: deprecated", "supersedes: []", "supersedes:\n - /"+aPath)}},
|
||||
{name: "superseder is rejected", files: map[string]string{
|
||||
aPath: sub(adrBase, "status: proposed", "status: rejected", "implementation: not-started", "implementation: not-applicable", "supersedes: []", "supersedes:\n - /"+bPath),
|
||||
bPath: sub(bBase, "status: accepted", "status: superseded", "superseded_by: []", "superseded_by:\n - /"+aPath)}, want: []string{aPath + ":9: must be accepted"}},
|
||||
@@ -325,7 +335,13 @@ func TestCheck(t *testing.T) {
|
||||
{name: "Consequences without prose", files: map[string]string{aPath: sub(adrBase, "* One more service.", "<!-- later -->")}, want: []string{aPath + ":31: has no prose"}},
|
||||
{name: "Decision Outcome without own prose", files: map[string]string{aPath: sub(adrBase, "Chosen option: Queue.\n\n", "")}, want: []string{aPath + ":27: needs prose before"}},
|
||||
{name: "plan missing section", files: map[string]string{pPath: sub(planBase, "## Steps\n\n1. Do it.\n\n", "")}, want: []string{pPath + ":1: missing required section \"Steps\""}},
|
||||
{name: "plan empty H1", files: map[string]string{pPath: sub(planBase, "# Adopt Queue", "#")}, want: []string{pPath + ":7: H1"}},
|
||||
{name: "plan empty H1", files: map[string]string{pPath: sub(planBase, "# Adopt Queue", "#")}, want: []string{pPath + ":8: H1"}},
|
||||
{name: "empty ADR H1 is on its own line", files: map[string]string{aPath: sub(adrBase, "# Use Queue", "#")}, want: []string{aPath + ":16: must equal title"}},
|
||||
{name: "headings inside blockquotes and lists do not count", files: map[string]string{aPath: adrBase + "\n> ## Consequences\n\n* ## Decision Outcome\n"}},
|
||||
{name: "H4 does not end the Decision Outcome prose", files: map[string]string{aPath: sub(adrBase, "Chosen option: Queue.", "#### Detail\n\nChosen.")}},
|
||||
{name: "plan second H1", files: map[string]string{pPath: planBase + "\n# Again\n"}, want: []string{pPath + ":22: more than one H1"}},
|
||||
{name: "plan repeated section", files: map[string]string{pPath: planBase + "\n## Scope\n\nMore.\n"}, want: []string{pPath + ":22: appears more than once"}},
|
||||
{name: "plan section without prose", files: map[string]string{pPath: sub(planBase, "1. Do it.", "<!-- later -->")}, want: []string{pPath + ":14: has no prose"}},
|
||||
{name: "plan Outcome is optional", files: map[string]string{pPath: planBase + "\n## Outcome\n\nDone.\n"}},
|
||||
|
||||
// 6.3 Placeholders
|
||||
@@ -345,6 +361,12 @@ func TestCheck(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckSymlinkedDocsDir(t *testing.T) {
|
||||
res, err := Check(tree(t, nil, map[string]string{"docs/plans": "@outside"}))
|
||||
must(t, err)
|
||||
expect(t, res.Problems, []string{"docs/plans:1: real directory"})
|
||||
}
|
||||
|
||||
func TestCheckEmptyRoot(t *testing.T) {
|
||||
res, err := Check(t.TempDir())
|
||||
must(t, err)
|
||||
|
||||
Reference in new issue
Block a user