mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
139 lines
4.6 KiB
JavaScript
139 lines
4.6 KiB
JavaScript
import { describe, it, mock } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
describe("dryRunScanner", async () => {
|
|
const mockWriteError = mock.fn();
|
|
const mockDryRunNpmCommandAndOutput = mock.fn();
|
|
|
|
// Mock ui module
|
|
mock.module("../../../environment/userInteraction.js", {
|
|
namedExports: {
|
|
ui: {
|
|
writeError: mockWriteError,
|
|
},
|
|
},
|
|
});
|
|
|
|
// Mock dryRunNpmCommandAndOutput function
|
|
mock.module("../runNpmCommand.js", {
|
|
namedExports: {
|
|
dryRunNpmCommandAndOutput: mockDryRunNpmCommandAndOutput,
|
|
},
|
|
});
|
|
|
|
const { dryRunScanner } = await import("./dryRunScanner.js");
|
|
|
|
describe("doesCommandReturnNonZero", () => {
|
|
// We need to access the internal function for testing
|
|
// Since it's not exported, we'll test it indirectly through the main functionality
|
|
|
|
it("should handle npm audit fix commands that return non-zero", async () => {
|
|
mockDryRunNpmCommandAndOutput.mock.resetCalls();
|
|
mockWriteError.mock.resetCalls();
|
|
mockDryRunNpmCommandAndOutput.mock.mockImplementationOnce(() => ({
|
|
status: 1,
|
|
output: "found 5 vulnerabilities that can be fixed",
|
|
}));
|
|
|
|
const scanner = dryRunScanner();
|
|
const result = await scanner.scan(["audit", "fix"]);
|
|
|
|
// Should not throw an error for audit fix commands
|
|
assert.ok(Array.isArray(result));
|
|
assert.equal(mockWriteError.mock.callCount(), 0);
|
|
});
|
|
|
|
it("should throw error for unexpected non-zero exit codes", async () => {
|
|
mockDryRunNpmCommandAndOutput.mock.resetCalls();
|
|
mockWriteError.mock.resetCalls();
|
|
mockDryRunNpmCommandAndOutput.mock.mockImplementationOnce(() => ({
|
|
status: 1,
|
|
output: "some error output",
|
|
}));
|
|
|
|
const scanner = dryRunScanner();
|
|
|
|
await assert.rejects(async () => {
|
|
await scanner.scan(["install", "lodash"]);
|
|
}, /Dry-run command failed with exit code 1/);
|
|
});
|
|
|
|
it("should handle zero exit codes normally", async () => {
|
|
mockDryRunNpmCommandAndOutput.mock.resetCalls();
|
|
mockWriteError.mock.resetCalls();
|
|
mockDryRunNpmCommandAndOutput.mock.mockImplementationOnce(() => ({
|
|
status: 0,
|
|
output: "added 1 package",
|
|
}));
|
|
|
|
const scanner = dryRunScanner();
|
|
const result = await scanner.scan(["install", "lodash"]);
|
|
|
|
assert.ok(Array.isArray(result));
|
|
assert.equal(mockWriteError.mock.callCount(), 0);
|
|
});
|
|
|
|
it("should throw error for non-zero exit with no output for audit fix", async () => {
|
|
mockDryRunNpmCommandAndOutput.mock.resetCalls();
|
|
mockWriteError.mock.resetCalls();
|
|
mockDryRunNpmCommandAndOutput.mock.mockImplementationOnce(() => ({
|
|
status: 1,
|
|
output: "",
|
|
}));
|
|
|
|
const scanner = dryRunScanner();
|
|
|
|
await assert.rejects(async () => {
|
|
await scanner.scan(["audit", "fix"]);
|
|
}, /Dry-run command failed with exit code 1/);
|
|
});
|
|
});
|
|
|
|
describe("scanner functionality", () => {
|
|
it("should use dryRunCommand option when provided", async () => {
|
|
mockDryRunNpmCommandAndOutput.mock.resetCalls();
|
|
mockWriteError.mock.resetCalls();
|
|
mockDryRunNpmCommandAndOutput.mock.mockImplementationOnce(() => ({
|
|
status: 0,
|
|
output: "no changes",
|
|
}));
|
|
|
|
const scanner = dryRunScanner({ dryRunCommand: "install" });
|
|
await scanner.scan(["install-test", "lodash"]);
|
|
|
|
// Should call with "install" instead of "install-test"
|
|
assert.equal(mockDryRunNpmCommandAndOutput.mock.callCount(), 1);
|
|
const calledArgs =
|
|
mockDryRunNpmCommandAndOutput.mock.calls[0].arguments[0];
|
|
assert.deepEqual(calledArgs, ["install", "lodash"]);
|
|
});
|
|
|
|
it("should skip scanning when hasDryRunArg returns true", async () => {
|
|
mockDryRunNpmCommandAndOutput.mock.resetCalls();
|
|
mockWriteError.mock.resetCalls();
|
|
|
|
const scanner = dryRunScanner();
|
|
const shouldScan = scanner.shouldScan(["install", "--dry-run"]);
|
|
|
|
assert.equal(shouldScan, false);
|
|
// Should not call dryRunNpmCommandAndOutput since scanning is skipped
|
|
assert.equal(mockDryRunNpmCommandAndOutput.mock.callCount(), 0);
|
|
});
|
|
|
|
it("should skip scanning when skipScanWhen returns true", async () => {
|
|
const scanner = dryRunScanner({
|
|
skipScanWhen: (args) => args.includes("--skip"),
|
|
});
|
|
const shouldScan = scanner.shouldScan(["install", "--skip"]);
|
|
|
|
assert.equal(shouldScan, false);
|
|
});
|
|
|
|
it("should scan when conditions are met", async () => {
|
|
const scanner = dryRunScanner();
|
|
const shouldScan = scanner.shouldScan(["install", "lodash"]);
|
|
|
|
assert.equal(shouldScan, true);
|
|
});
|
|
});
|
|
});
|