From 4f7cad3ca676e1c2c6b51632fdcee1627af4b5ce Mon Sep 17 00:00:00 2001 From: ColumbusLabs <287001685+ColumbusLabs@users.noreply.github.com> Date: Fri, 5 Jun 2026 14:58:30 -0400 Subject: [PATCH] test: expand reporter and scan coverage --- .github/workflows/ci.yml | 4 +- tests/core/scan.test.ts | 36 ++++++++++++++ tests/reporters/jsonReporter.test.ts | 73 ++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 tests/core/scan.test.ts create mode 100644 tests/reporters/jsonReporter.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cae3e62..f38b0b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,4 +23,6 @@ jobs: - run: npm run typecheck:tests - run: npm test - run: npm run build - - run: node dist/cli/index.js scan examples/react --min-severity info --format markdown --output debtlens-example-report.md + - run: node dist/cli/index.js scan examples/react --min-severity info --format markdown --output debtlens-react-report.md + - run: node dist/cli/index.js scan examples/react-native --min-severity info --format markdown --output debtlens-react-native-report.md + - run: node dist/cli/index.js scan examples/next --min-severity info --format markdown --output debtlens-next-report.md diff --git a/tests/core/scan.test.ts b/tests/core/scan.test.ts new file mode 100644 index 0000000..6b55208 --- /dev/null +++ b/tests/core/scan.test.ts @@ -0,0 +1,36 @@ +import assert from "node:assert/strict"; +import { resolve } from "node:path"; +import { describe, it } from "node:test"; +import { defaultConfig } from "../../src/config/defaults.js"; +import { scan } from "../../src/core/scan.js"; + +describe("scan integration", () => { + it("runs the full pipeline against examples/react", async () => { + const cwd = process.cwd(); + const result = await scan({ + cwd, + target: resolve("examples/react"), + include: defaultConfig.include, + exclude: [], + minSeverity: "medium", + rules: ["duplicate-logic", "prop-drilling"], + thresholds: {}, + maxFiles: defaultConfig.maxFiles, + }); + + assert.equal(result.summary.filesScanned, 3); + assert.equal(result.summary.rulesRun, 2); + assert.equal(result.summary.totalIssues, 2); + assert.equal(result.summary.bySeverity.high, 2); + assert.deepEqual(result.summary.byRule, { + "prop-drilling": 1, + "duplicate-logic": 1, + }); + + assert.deepEqual(result.issues.map((issue) => issue.ruleId), ["prop-drilling", "duplicate-logic"]); + assert.equal(result.issues[0]?.severity, "high"); + assert.equal(result.issues[0]?.file, "src/Dashboard.tsx"); + assert.equal(result.issues[1]?.severity, "high"); + assert.equal(result.issues[1]?.file, "src/duplicateOne.ts"); + }); +}); diff --git a/tests/reporters/jsonReporter.test.ts b/tests/reporters/jsonReporter.test.ts new file mode 100644 index 0000000..b2b55d5 --- /dev/null +++ b/tests/reporters/jsonReporter.test.ts @@ -0,0 +1,73 @@ +import assert from "node:assert/strict"; +import { describe, it } from "node:test"; +import type { DebtIssue, ScanResult } from "../../src/core/types.js"; +import { renderJson } from "../../src/reporters/jsonReporter.js"; + +const issue: DebtIssue = { + id: "dl_json_contract", + ruleId: "prop-drilling", + ruleName: "Prop drilling", + severity: "high", + confidence: 0.73, + message: "Parent forwards 5 props across 1 child component.", + file: "src/Parent.tsx", + location: { startLine: 13, startColumn: 3, endLine: 20, endColumn: 5 }, + evidence: ["Child: a, b, c, d, e"], + suggestion: "Consider colocating the data owner closer to consumers.", + tags: ["react", "props", "component-design"], +}; + +const result: ScanResult = { + issues: [issue], + summary: { + totalIssues: 1, + bySeverity: { info: 0, low: 0, medium: 0, high: 1 }, + byRule: { "prop-drilling": 1 }, + filesScanned: 1, + rulesRun: 8, + elapsedMs: 12, + }, + options: { + target: ".", + include: ["src/**/*.{ts,tsx}"], + exclude: ["**/*.test.ts"], + minSeverity: "medium", + rules: ["prop-drilling"], + }, +}; + +describe("json reporter", () => { + it("preserves the public ScanResult contract", () => { + const parsed = JSON.parse(renderJson(result)); + + assert.deepEqual(Object.keys(parsed), ["issues", "summary", "options"]); + + const [parsedIssue] = parsed.issues; + assert.equal(parsedIssue.id, issue.id); + assert.equal(parsedIssue.ruleId, issue.ruleId); + assert.equal(parsedIssue.ruleName, issue.ruleName); + assert.equal(parsedIssue.severity, "high"); + assert.equal(parsedIssue.confidence, issue.confidence); + assert.equal(parsedIssue.message, issue.message); + assert.equal(parsedIssue.file, issue.file); + assert.deepEqual(parsedIssue.location, issue.location); + assert.deepEqual(parsedIssue.evidence, issue.evidence); + assert.equal(parsedIssue.suggestion, issue.suggestion); + assert.deepEqual(parsedIssue.tags, issue.tags); + + assert.deepEqual(Object.keys(parsed.summary), [ + "totalIssues", + "bySeverity", + "byRule", + "filesScanned", + "rulesRun", + "elapsedMs", + ]); + assert.equal(parsed.summary.totalIssues, 1); + assert.deepEqual(parsed.summary.bySeverity, { info: 0, low: 0, medium: 0, high: 1 }); + assert.deepEqual(parsed.summary.byRule, { "prop-drilling": 1 }); + assert.equal(parsed.summary.filesScanned, 1); + assert.equal(parsed.summary.rulesRun, 8); + assert.equal(parsed.summary.elapsedMs, 12); + }); +});