Files

130 lines
3.7 KiB
JavaScript
Raw Permalink Normal View History

import { test } from "node:test";
import assert from "node:assert/strict";
import { buildRunJson, buildRunMarkdown } from "../src/report.js";
function sampleRun(overrides = {}) {
return {
scannedAt: "2026-06-01T12:00:00.000Z",
scannedAtHuman: "Mon Jun 01 2026 12:00:00",
account: { kind: "org", owner: "RMSoft-ltd" },
dryRun: false,
autoMerge: true,
mergeMethod: "squash",
totals: {
total: 4,
clean: 1,
infected: 3,
remediated: 2,
merged: 1,
prOpened: 2,
manualOnly: 1,
errors: 1,
},
repos: [
{
repo: "RMSoft-ltd/clean-app",
severity: "clean",
findings: [],
deleted: [],
modified: [],
notes: [],
manualReview: [],
pr: null,
error: null,
},
{
repo: "RMSoft-ltd/web",
severity: "infected",
findings: [
{
file: ".vscode",
action: "remove-dir",
confidence: "high",
contentConfirmed: true,
description: "malicious task",
},
],
deleted: [".vscode", "public/fonts"],
modified: ["vite.config.js"],
notes: ["removed malware-injected entries from .gitignore"],
manualReview: [],
pr: {
url: "https://github.com/RMSoft-ltd/web/pull/7",
merged: true,
mergeError: null,
},
error: null,
},
{
repo: "RMSoft-ltd/api",
severity: "infected",
findings: [],
deleted: [],
modified: ["postcss.config.mjs"],
notes: [],
manualReview: [],
pr: {
url: "https://github.com/RMSoft-ltd/api/pull/3",
merged: false,
mergeError: "required status checks have not passed",
},
error: null,
},
{
repo: "RMSoft-ltd/mob",
severity: "infected",
findings: [],
deleted: [],
modified: [],
notes: [],
manualReview: ["package.json: impostor dependency"],
pr: null,
error: null,
},
],
...overrides,
};
}
test("buildRunMarkdown renders clickable PR links and merge status", () => {
const md = buildRunMarkdown(sampleRun());
assert.match(md, /\[#7\]\(https:\/\/github\.com\/RMSoft-ltd\/web\/pull\/7\)/);
assert.match(md, /✅ merged/);
assert.match(md, /⚠️ merge blocked/);
assert.match(md, /\| Repository \| Pull request \| Status \| Changes \|/);
assert.match(md, /removed: \.vscode, public\/fonts/);
});
test("buildRunMarkdown surfaces a Needs attention section", () => {
const md = buildRunMarkdown(sampleRun());
assert.match(md, /## Needs attention/);
assert.match(md, /auto-merge blocked: required status checks/);
assert.match(md, /manual review: package\.json: impostor dependency/);
});
test("buildRunMarkdown reflects account, mode, and totals", () => {
const md = buildRunMarkdown(sampleRun());
assert.match(md, /\*\*Account:\*\* org `RMSoft-ltd`/);
assert.match(md, /auto-merge: on \(squash\)/);
assert.match(md, /\| PRs merged \| 1 \|/);
});
test("dry-run mode is labeled and omits the merged row", () => {
const md = buildRunMarkdown(sampleRun({ dryRun: true, autoMerge: false }));
assert.match(md, /DRY RUN/);
assert.match(md, /auto-merge: off/);
assert.ok(!md.includes("PRs merged"));
});
test("buildRunJson tags a schema and preserves the repos array", () => {
const run = sampleRun();
const json = buildRunJson(run);
assert.equal(json.schema, "polinrider-run/1");
assert.equal(json.repos.length, 4);
assert.equal(json.totals.merged, 1);
// round-trips through JSON
assert.deepEqual(JSON.parse(JSON.stringify(json)).account, {
kind: "org",
owner: "RMSoft-ltd",
});
});