Files
repomix-mirror/tests/core/security/validateFileSafety.test.ts
Kazuki Yamada fb281d4560 test(core): Wire createSecurityTaskRunner mock into smaller packager tests
Continuation of the perf(core) Pre-warm security worker pool change —
extends `mockDeps` / inline pack-test plumbing in the three smaller test
files so the default-scope path no longer attempts to spawn a real
worker pool from the test environment.

- tests/core/packager/diffsFunctionality.test.ts: adds
  `mockCreateSecurityTaskRunner` to both pack-call sites.
- tests/core/packager/splitOutput.test.ts: same — adds the stub to the
  inline mock deps.
- tests/core/security/validateFileSafety.test.ts: updates the
  `runSecurityCheck` call assertion to include the new
  `{ taskRunner: undefined }` deps argument forwarded by
  `validateFileSafety` when no pre-warmed runner is provided.

(See PR description / parent commit for the full perf change rationale,
benchmark numbers, and correctness notes.)
2026-05-09 02:30:26 +09:00

118 lines
5.2 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from 'vitest';
import type { RepomixConfigMerged } from '../../../src/config/configSchema.js';
import type { RawFile } from '../../../src/core/file/fileTypes.js';
import type { SuspiciousFileResult } from '../../../src/core/security/securityCheck.js';
import { validateFileSafety } from '../../../src/core/security/validateFileSafety.js';
import { logger } from '../../../src/shared/logger.js';
import type { RepomixProgressCallback } from '../../../src/shared/types.js';
describe('validateFileSafety', () => {
it('should validate file safety and return safe files and paths', async () => {
const rawFiles: RawFile[] = [
{ path: 'file1.txt', content: 'content1' },
{ path: 'file2.txt', content: 'content2' },
{ path: 'file3.txt', content: 'content3' },
];
const safeRawFiles = [rawFiles[0], rawFiles[1]];
const config: RepomixConfigMerged = {
security: { enableSecurityCheck: true },
} as RepomixConfigMerged;
const progressCallback: RepomixProgressCallback = vi.fn();
const suspiciousFilesResults: SuspiciousFileResult[] = [
{ filePath: 'file2.txt', messages: ['something suspicious.'], type: 'file' },
];
const deps = {
runSecurityCheck: vi.fn().mockResolvedValue(suspiciousFilesResults),
filterOutUntrustedFiles: vi.fn().mockReturnValue(safeRawFiles),
};
const result = await validateFileSafety(rawFiles, progressCallback, config, undefined, undefined, deps);
expect(deps.runSecurityCheck).toHaveBeenCalledWith(rawFiles, progressCallback, undefined, undefined, {
taskRunner: undefined,
});
expect(deps.filterOutUntrustedFiles).toHaveBeenCalledWith(rawFiles, suspiciousFilesResults);
expect(result).toEqual({
safeRawFiles,
safeFilePaths: ['file1.txt', 'file2.txt'],
suspiciousFilesResults,
suspiciousGitDiffResults: [],
suspiciousGitLogResults: [],
});
});
describe('git content warnings', () => {
afterEach(() => {
vi.restoreAllMocks();
});
it('warns about each suspicious git diff/log entry with singular/plural form', async () => {
const warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {});
const config: RepomixConfigMerged = {
security: { enableSecurityCheck: true },
} as RepomixConfigMerged;
const progressCallback: RepomixProgressCallback = vi.fn();
const allResults: SuspiciousFileResult[] = [
{ filePath: 'work_tree', messages: ['leaked key'], type: 'gitDiff' },
{ filePath: 'staged', messages: ['leaked key', 'token'], type: 'gitDiff' },
{ filePath: 'commit_log', messages: ['secret', 'pw', 'token'], type: 'gitLog' },
];
const deps = {
runSecurityCheck: vi.fn().mockResolvedValue(allResults),
filterOutUntrustedFiles: vi.fn().mockReturnValue([]),
};
const result = await validateFileSafety([], progressCallback, config, undefined, undefined, deps);
expect(result.suspiciousGitDiffResults).toHaveLength(2);
expect(result.suspiciousGitLogResults).toHaveLength(1);
const warnings = warnSpy.mock.calls.map((c) => String(c[0]));
// Header lines for each section
expect(warnings).toContain('Security issues found in Git diffs, but they will still be included in the output');
expect(warnings).toContain('Security issues found in Git logs, but they will still be included in the output');
// Singular form for 1 message, plural for >1
expect(warnings).toContain(' - work_tree: 1 issue detected');
expect(warnings).toContain(' - staged: 2 issues detected');
expect(warnings).toContain(' - commit_log: 3 issues detected');
});
it('does not log a header when no suspicious git content is found', async () => {
const warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {});
const config: RepomixConfigMerged = {
security: { enableSecurityCheck: true },
} as RepomixConfigMerged;
await validateFileSafety([], vi.fn(), config, undefined, undefined, {
runSecurityCheck: vi.fn().mockResolvedValue([]),
filterOutUntrustedFiles: vi.fn().mockReturnValue([]),
});
expect(warnSpy).not.toHaveBeenCalled();
});
});
it('skips runSecurityCheck entirely when enableSecurityCheck is false', async () => {
// Pin the negative path of the `if (config.security.enableSecurityCheck)` guard.
// Dropping the guard would still pass every other test in this file because
// they all enable the check; this one fails if the guard ever regresses.
const config: RepomixConfigMerged = {
security: { enableSecurityCheck: false },
} as RepomixConfigMerged;
const rawFiles: RawFile[] = [{ path: 'file1.txt', content: 'content' }];
const deps = {
runSecurityCheck: vi.fn(),
filterOutUntrustedFiles: vi.fn().mockReturnValue(rawFiles),
};
const result = await validateFileSafety(rawFiles, vi.fn(), config, undefined, undefined, deps);
expect(deps.runSecurityCheck).not.toHaveBeenCalled();
expect(result.suspiciousFilesResults).toEqual([]);
expect(result.suspiciousGitDiffResults).toEqual([]);
expect(result.suspiciousGitLogResults).toEqual([]);
expect(result.safeFilePaths).toEqual(['file1.txt']);
});
});