import assert from "node:assert/strict"; import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { test } from "node:test"; import { __test as code } from "./gh_code_search.mjs"; import { __test as failed } from "./gh_failed_run.mjs"; import { __test as pages } from "./gh_pages_deploy.mjs"; function withArgv(args, fn) { const old = process.argv; process.argv = [process.execPath, "script.mjs", ...args]; try { return fn(); } finally { process.argv = old; } } function responseSequence(...responses) { return () => responses.shift() ?? { status: 1, stdout: "", stderr: "unexpected call" }; } function withoutConsole(fn) { const oldLog = console.log; const oldError = console.error; console.log = () => {}; console.error = () => {}; try { return fn(); } finally { console.log = oldLog; console.error = oldError; } } test("code search builds gh args from CLI flags", () => { withArgv([ "TODO", "--limit", "5", "--language", "javascript", "--repo", "o/r", "--owner", "org", ], () => { assert.equal(code.positional(), "TODO"); assert.deepEqual(code.build("TODO"), [ "gh", "search", "code", "TODO", "--limit", "5", "--language", "javascript", "--repo", "o/r", "--owner", "org", "--json", "path,repository,sha,textMatches,url", ]); }); }); test("code search filters, sorts, and formats results", () => { const results = [ { path: "b.js", repository: { nameWithOwner: "z/repo", isFork: true }, textMatches: [{ fragment: "b" }], url: "u2" }, { path: "a.js", repository: { nameWithOwner: "a/repo", isPrivate: true }, textMatches: [{ fragment: "a" }, { fragment: "aa" }], url: "u1" }, ]; assert.equal(withArgv(["q", "--exclude-forks"], () => code.filter(results)).length, 1); assert.equal(withArgv(["q", "--exclude-private"], () => code.filter(results)).length, 1); assert.equal(withArgv(["q", "--min-matches", "2"], () => code.filter(results))[0].path, "a.js"); assert.equal(code.sort(results, "repo")[0].path, "a.js"); assert.equal(code.sort(results, "path")[0].path, "a.js"); assert.equal(code.sort(results, "matches")[0].path, "a.js"); assert.match(code.pretty(results), /Found 2 result/); assert.match(code.summary(results), /Total files found: 2/); assert.equal(code.pretty([]), "No results found."); }); test("failed run helpers clean logs and target jobs by database ID", () => { assert.equal(failed.clean("\u001b[31merror\u001b[0m\r"), "error"); assert.deepEqual(failed.repoArgs("o/r"), ["--repo", "o/r"]); assert.deepEqual(failed.repoArgs(), []); assert.deepEqual(failed.jobLogArgs(42, 9001, "o/r"), [ "run", "view", "42", "--log-failed", "--job", "9001", "--repo", "o/r", ]); const out = failed.excerpts("##[command] npm test\nError: boom\nError: boom\nok\nProcess completed with exit code 1", 5); assert.deepEqual(out, ["Error: boom", "Process completed with exit code 1"]); }); test("pages workflow requires an explicit site directory", () => { assert.throws( () => pages.createWorkflow(".github/workflows/pages.yml", false, false), /site directory/i, ); }); test("pages workflow dry-run never writes or overwrites", () => { const dir = mkdtempSync(join(tmpdir(), "gh-pages-test-")); const output = join(dir, "pages.yml"); const oldLog = console.log; console.log = () => {}; try { const preview = pages.createWorkflow(output, false, true, "dist"); assert.equal(preview.status, "dry_run"); assert.equal(existsSync(output), false); writeFileSync(output, "keep\n"); const replacement = pages.createWorkflow(output, true, true, "dist"); assert.equal(replacement.status, "dry_run"); assert.equal(readFileSync(output, "utf8"), "keep\n"); } finally { console.log = oldLog; rmSync(dir, { recursive: true, force: true }); } }); test("pages helper creates a workflow for only the explicit site directory", () => { assert.equal(pages.msg("done", "success"), "✓ done"); assert.equal(pages.json({ ok: true }), '{\n "ok": true\n}'); assert.throws(() => pages.validateSiteDir("../private"), /repository-relative/); const dir = mkdtempSync(join(tmpdir(), "gh-pages-test-")); const output = join(dir, ".github/workflows/pages.yml"); const oldLog = console.log; const logs = []; console.log = (line = "") => logs.push(String(line)); try { const created = pages.createWorkflow(output, false, false, "dist"); assert.equal(created.status, "created"); const workflow = readFileSync(output, "utf8"); assert.match(workflow, /actions\/deploy-pages@v4/); assert.match(workflow, /path: 'dist'/); assert.doesNotMatch(workflow, /cp -r/); const skipped = pages.createWorkflow(output, false, false, "dist"); assert.equal(skipped.status, "skipped"); assert.match(logs.join("\n"), /file_exists/); } finally { console.log = oldLog; rmSync(dir, { recursive: true, force: true }); } }); test("pages status preserves API failures as errors", () => { const run = responseSequence( { status: 0, stdout: "", stderr: "" }, { status: 1, stdout: "", stderr: "permission denied" }, ); const result = withoutConsole(() => new pages.Pages("o/r", false, true, run).status()); assert.deepEqual(result, { ok: false, status: "error", action: "status", error: "permission denied", }); }); test("pages enable reports failed HTTPS enforcement", () => { const run = responseSequence( { status: 0, stdout: "", stderr: "" }, { status: 0, stdout: "{}", stderr: "" }, { status: 1, stdout: "", stderr: "HTTPS unavailable" }, ); const result = withoutConsole(() => new pages.Pages("o/r", false, true, run).enable()); assert.deepEqual(result, { ok: false, status: "error", action: "enforce_https", pages_enabled: true, error: "HTTPS unavailable", }); });