File size: 3,687 Bytes
a9fbc84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { execFileSync } from "node:child_process";
import fs from "node:fs";
import { createRequire } from "node:module";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { afterEach, beforeEach, describe, expect, it } from "vitest";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const SCRIPT_PATH = path.resolve(__dirname, "documentation-validator.cjs");
const REPO_ROOT = path.resolve(__dirname, "..");
const { documentationFiles } = createRequire(import.meta.url)(SCRIPT_PATH);

function runValidator(rootDir, cwd = REPO_ROOT) {
  try {
    const output = execFileSync(process.execPath, [SCRIPT_PATH, rootDir], {
      cwd,
      encoding: "utf8",
      stdio: "pipe",
    });
    return { exitCode: 0, stdout: output, stderr: "" };
  } catch (error) {
    if (error.signal) throw new Error(`Killed by ${error.signal}`);
    return {
      exitCode: error.status ?? 1,
      stdout: error.stdout ?? "",
      stderr: error.stderr ?? "",
    };
  }
}

function writeDoc(dir, relPath, content) {
  const fullPath = path.join(dir, relPath);
  fs.mkdirSync(path.dirname(fullPath), { recursive: true });
  fs.writeFileSync(fullPath, content);
}

describe("documentation-validator", () => {
  let tmpDir;

  beforeEach(() => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "doc-validator-test-"));
    for (const docFile of documentationFiles) {
      writeDoc(tmpDir, docFile, "# Doc\n");
    }
  });

  afterEach(() => {
    fs.rmSync(tmpDir, { recursive: true, force: true });
  });

  it("lists no file this repository is missing", () => {
    expect(runValidator(REPO_ROOT).stdout).not.toContain("File not found");
  });

  it("fails and counts the issue when a listed file is missing", () => {
    const missing = ".github/ISSUE_TEMPLATE/bug_report.yml";
    fs.rmSync(path.join(tmpDir, missing));

    const { exitCode, stdout } = runValidator(tmpDir);

    expect(exitCode).toBe(1);
    expect(stdout).toContain(`File not found: ${missing}`);
    expect(stdout).toMatch(/Issues found: 1$/m);
  });

  it("fails and counts the issue when a listed file cannot be read", () => {
    const unreadable = path.join(tmpDir, ".github/SECURITY.md");
    fs.rmSync(unreadable);
    fs.mkdirSync(unreadable);

    const { exitCode, stdout } = runValidator(tmpDir);

    expect(exitCode).toBe(1);
    expect(stdout).toContain("Error reading .github/SECURITY.md");
    expect(stdout).toMatch(/Issues found: 1$/m);
  });

  it("counts a broken link the same way it reports it", () => {
    writeDoc(tmpDir, "README.md", "# Doc\n\n[Gone](./nonexistent.md)\n");

    const { exitCode, stdout } = runValidator(tmpDir);

    expect(exitCode).toBe(1);
    expect(stdout).toContain('Missing target: "./nonexistent.md"');
    expect(stdout).toMatch(/Issues found: 1$/m);
  });

  it("rejects a root directory that does not exist", () => {
    const { exitCode, stderr } = runValidator(path.join(tmpDir, "missing"));

    expect(exitCode).toBe(1);
    expect(stderr).toContain("Directory not found");
  });

  it("rejects a file given where a root directory belongs", () => {
    const { exitCode, stderr } = runValidator(path.join(tmpDir, "README.md"));

    expect(exitCode).toBe(1);
    expect(stderr).toContain("Directory not found");
  });

  it("falls back to the repository root when the argument is empty", () => {
    const elsewhere = fs.mkdtempSync(
      path.join(os.tmpdir(), "doc-validator-cwd-"),
    );

    try {
      expect(runValidator("", elsewhere).stdout).not.toContain(
        "File not found",
      );
    } finally {
      fs.rmSync(elsewhere, { recursive: true, force: true });
    }
  });
});