From e37858ad4982b5da47d1e327c7e7303901b258e3 Mon Sep 17 00:00:00 2001 From: miccy <9729864+miccy@users.noreply.github.com> Date: Mon, 4 May 2026 02:35:09 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=AA=20Add=20error=20path=20tests=20for?= =?UTF-8?q?=20package.json=20in=20scanner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented unit tests for the internal `detectInjection` function in `packages/scanner/src/scan.ts`. These tests mock `node:fs` to verify that the scanner handles missing, unreadable, or invalid `package.json` files gracefully by returning no findings, matching the existing implementation's try-catch blocks. Coverage includes: - Missing/unreadable package.json - Invalid JSON content in package.json - Successful detection of undeclared packages in lockfiles (happy path) --- packages/scanner/tests/scan.test.ts | 104 ++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 packages/scanner/tests/scan.test.ts diff --git a/packages/scanner/tests/scan.test.ts b/packages/scanner/tests/scan.test.ts new file mode 100644 index 0000000..2d62c67 --- /dev/null +++ b/packages/scanner/tests/scan.test.ts @@ -0,0 +1,104 @@ +import { describe, expect, it, mock, beforeEach } from "bun:test"; +import { existsSync, readFileSync } from "node:fs"; + +const mockExistsSync = mock(); +const mockReadFileSync = mock(); + +mock.module("node:fs", () => ({ + existsSync: mockExistsSync, + readFileSync: mockReadFileSync, +})); + +import { scan } from "../src/scan"; + +describe("scan - detectInjection", () => { + beforeEach(() => { + mockExistsSync.mockReset(); + mockReadFileSync.mockReset(); + }); + + it("should return empty findings if package.json is missing or unreadable", async () => { + mockExistsSync.mockImplementation((path: string) => { + if (path.endsWith("package-lock.json")) return true; + if (path.endsWith("package.json")) return false; + return false; + }); + + mockReadFileSync.mockImplementation((path: string) => { + if (path.endsWith("package-lock.json")) { + return JSON.stringify({ + lockfileVersion: 2, + packages: { + "node_modules/pkg-a": { version: "1.0.0" } + } + }); + } + if (path.endsWith("package.json")) { + throw new Error("File not found"); + } + return ""; + }); + + const result = await scan("/test-dir"); + expect(result.findings).toEqual([]); + }); + + it("should return empty findings if package.json is invalid JSON", async () => { + mockExistsSync.mockImplementation((path: string) => { + if (path.endsWith("package-lock.json")) return true; + return true; + }); + + mockReadFileSync.mockImplementation((path: string) => { + if (path.endsWith("package-lock.json")) { + return JSON.stringify({ + lockfileVersion: 2, + packages: { + "node_modules/pkg-a": { version: "1.0.0" } + } + }); + } + if (path.endsWith("package.json")) { + return "invalid { json"; + } + return ""; + }); + + const result = await scan("/test-dir"); + expect(result.findings).toEqual([]); + }); + + it("should detect injection when package is in lockfile but not in package.json", async () => { + mockExistsSync.mockImplementation((path: string) => { + if (path.endsWith("package-lock.json")) return true; + if (path.endsWith("package.json")) return true; + return false; + }); + + mockReadFileSync.mockImplementation((path: string) => { + if (path.endsWith("package-lock.json")) { + return JSON.stringify({ + lockfileVersion: 3, + packages: { + "": { version: "1.0.0" }, + "node_modules/pkg-a": { version: "1.0.0" }, + "node_modules/pkg-b": { version: "2.0.0" } + } + }); + } + if (path.endsWith("package.json")) { + return JSON.stringify({ + dependencies: { + "pkg-a": "1.0.0" + } + }); + } + return ""; + }); + + const result = await scan("/test-dir"); + const injections = result.findings.filter(f => f.type === 'injection'); + expect(injections).toHaveLength(1); + expect(injections[0].package).toBe("pkg-b"); + }); +});