mirror of
https://github.com/yamadashy/repomix.git
synced 2026-02-07 20:26:47 +01:00
Extract logSuspiciousContentWarning helper function to eliminate code duplication between Git diffs and Git logs security warning logic in validateFileSafety.ts. This addresses PR feedback about duplicate code patterns and improves maintainability by following DRY principles.
295 lines
9.1 KiB
TypeScript
295 lines
9.1 KiB
TypeScript
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
import type { RepomixConfigMerged } from '../../../src/config/configSchema.js';
|
|
import { GIT_LOG_FORMAT_SEPARATOR, GIT_LOG_RECORD_SEPARATOR } from '../../../src/core/git/gitLogHandle.js';
|
|
import { getGitLog, getGitLogs } from '../../../src/core/git/gitLogHandle.js';
|
|
import { RepomixError } from '../../../src/shared/errorHandle.js';
|
|
import { logger } from '../../../src/shared/logger.js';
|
|
|
|
vi.mock('../../../src/shared/logger');
|
|
|
|
describe('gitLogHandle', () => {
|
|
beforeEach(() => {
|
|
vi.resetAllMocks();
|
|
});
|
|
|
|
describe('getGitLog', () => {
|
|
test('should return git log content when directory is a git repository', async () => {
|
|
const mockExecGitLog = vi.fn().mockResolvedValue('mock log content');
|
|
const mockIsGitRepository = vi.fn().mockResolvedValue(true);
|
|
|
|
const result = await getGitLog('/test/dir', 10, {
|
|
execGitLog: mockExecGitLog,
|
|
isGitRepository: mockIsGitRepository,
|
|
});
|
|
|
|
expect(result).toBe('mock log content');
|
|
expect(mockIsGitRepository).toHaveBeenCalledWith('/test/dir');
|
|
expect(mockExecGitLog).toHaveBeenCalledWith('/test/dir', 10, GIT_LOG_FORMAT_SEPARATOR);
|
|
});
|
|
|
|
test('should return empty string when directory is not a git repository', async () => {
|
|
const mockExecGitLog = vi.fn();
|
|
const mockIsGitRepository = vi.fn().mockResolvedValue(false);
|
|
|
|
const result = await getGitLog('/test/dir', 10, {
|
|
execGitLog: mockExecGitLog,
|
|
isGitRepository: mockIsGitRepository,
|
|
});
|
|
|
|
expect(result).toBe('');
|
|
expect(mockIsGitRepository).toHaveBeenCalledWith('/test/dir');
|
|
expect(mockExecGitLog).not.toHaveBeenCalled();
|
|
expect(logger.trace).toHaveBeenCalledWith('Directory /test/dir is not a git repository');
|
|
});
|
|
|
|
test('should throw error when git log command fails', async () => {
|
|
const mockError = new Error('git command failed');
|
|
const mockExecGitLog = vi.fn().mockRejectedValue(mockError);
|
|
const mockIsGitRepository = vi.fn().mockResolvedValue(true);
|
|
|
|
await expect(
|
|
getGitLog('/test/dir', 10, {
|
|
execGitLog: mockExecGitLog,
|
|
isGitRepository: mockIsGitRepository,
|
|
}),
|
|
).rejects.toThrow('git command failed');
|
|
|
|
expect(logger.trace).toHaveBeenCalledWith('Failed to get git log:', 'git command failed');
|
|
});
|
|
});
|
|
|
|
describe('getGitLogs', () => {
|
|
test('should return git logs when includeLogs is enabled', async () => {
|
|
const mockLogContent = `${GIT_LOG_RECORD_SEPARATOR}2024-01-01 10:00:00 +0900|Initial commit
|
|
file1.txt
|
|
file2.txt
|
|
${GIT_LOG_RECORD_SEPARATOR}2024-01-02 11:00:00 +0900|Add feature
|
|
src/feature.ts`;
|
|
|
|
const mockGetGitLog = vi.fn().mockResolvedValue(mockLogContent);
|
|
const config: RepomixConfigMerged = {
|
|
cwd: '/project',
|
|
output: {
|
|
git: {
|
|
includeLogs: true,
|
|
includeLogsCount: 25,
|
|
},
|
|
},
|
|
} as RepomixConfigMerged;
|
|
|
|
const result = await getGitLogs(['/project/src'], config, {
|
|
getGitLog: mockGetGitLog,
|
|
});
|
|
|
|
expect(result).toEqual({
|
|
logContent: mockLogContent,
|
|
commits: [
|
|
{
|
|
date: '2024-01-01 10:00:00 +0900',
|
|
message: 'Initial commit',
|
|
files: ['file1.txt', 'file2.txt'],
|
|
},
|
|
{
|
|
date: '2024-01-02 11:00:00 +0900',
|
|
message: 'Add feature',
|
|
files: ['src/feature.ts'],
|
|
},
|
|
],
|
|
});
|
|
expect(mockGetGitLog).toHaveBeenCalledWith('/project/src', 25);
|
|
});
|
|
|
|
test('should return undefined when includeLogs is disabled', async () => {
|
|
const config: RepomixConfigMerged = {
|
|
cwd: '/project',
|
|
output: {
|
|
git: {
|
|
includeLogs: false,
|
|
},
|
|
},
|
|
} as RepomixConfigMerged;
|
|
|
|
const result = await getGitLogs(['/project/src'], config);
|
|
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
test('should use default commit count when includeLogsCount is not specified', async () => {
|
|
const mockGetGitLog = vi.fn().mockResolvedValue(`${GIT_LOG_RECORD_SEPARATOR}2024-01-01 10:00:00 +0900|Test commit
|
|
test.txt`);
|
|
const config: RepomixConfigMerged = {
|
|
cwd: '/project',
|
|
output: {
|
|
git: {
|
|
includeLogs: true,
|
|
},
|
|
},
|
|
} as RepomixConfigMerged;
|
|
|
|
await getGitLogs(['/project/src'], config, {
|
|
getGitLog: mockGetGitLog,
|
|
});
|
|
|
|
expect(mockGetGitLog).toHaveBeenCalledWith('/project/src', 50);
|
|
});
|
|
|
|
test('should use first directory as git root', async () => {
|
|
const mockGetGitLog = vi.fn().mockResolvedValue('');
|
|
const config: RepomixConfigMerged = {
|
|
cwd: '/fallback',
|
|
output: {
|
|
git: {
|
|
includeLogs: true,
|
|
},
|
|
},
|
|
} as RepomixConfigMerged;
|
|
|
|
await getGitLogs(['/first/dir', '/second/dir'], config, {
|
|
getGitLog: mockGetGitLog,
|
|
});
|
|
|
|
expect(mockGetGitLog).toHaveBeenCalledWith('/first/dir', 50);
|
|
});
|
|
|
|
test('should fallback to config.cwd when no directories provided', async () => {
|
|
const mockGetGitLog = vi.fn().mockResolvedValue('');
|
|
const config: RepomixConfigMerged = {
|
|
cwd: '/fallback',
|
|
output: {
|
|
git: {
|
|
includeLogs: true,
|
|
},
|
|
},
|
|
} as RepomixConfigMerged;
|
|
|
|
await getGitLogs([], config, {
|
|
getGitLog: mockGetGitLog,
|
|
});
|
|
|
|
expect(mockGetGitLog).toHaveBeenCalledWith('/fallback', 50);
|
|
});
|
|
|
|
test('should throw RepomixError when getGitLog fails', async () => {
|
|
const mockError = new Error('git failed');
|
|
const mockGetGitLog = vi.fn().mockRejectedValue(mockError);
|
|
const config: RepomixConfigMerged = {
|
|
cwd: '/project',
|
|
output: {
|
|
git: {
|
|
includeLogs: true,
|
|
},
|
|
},
|
|
} as RepomixConfigMerged;
|
|
|
|
await expect(
|
|
getGitLogs(['/project'], config, {
|
|
getGitLog: mockGetGitLog,
|
|
}),
|
|
).rejects.toThrow(RepomixError);
|
|
await expect(
|
|
getGitLogs(['/project'], config, {
|
|
getGitLog: mockGetGitLog,
|
|
}),
|
|
).rejects.toThrow('Failed to get git logs: git failed');
|
|
});
|
|
|
|
test('should handle empty git log output', async () => {
|
|
const mockGetGitLog = vi.fn().mockResolvedValue('');
|
|
const config: RepomixConfigMerged = {
|
|
cwd: '/project',
|
|
output: {
|
|
git: {
|
|
includeLogs: true,
|
|
},
|
|
},
|
|
} as RepomixConfigMerged;
|
|
|
|
const result = await getGitLogs(['/project'], config, {
|
|
getGitLog: mockGetGitLog,
|
|
});
|
|
|
|
expect(result).toEqual({
|
|
logContent: '',
|
|
commits: [],
|
|
});
|
|
});
|
|
|
|
test('should parse git log correctly with malformed separator content', async () => {
|
|
// Test behavior when log content doesn't match expected separator
|
|
const malformedLogContent = 'random content without separator';
|
|
|
|
const mockGetGitLog = vi.fn().mockResolvedValue(malformedLogContent);
|
|
const config: RepomixConfigMerged = {
|
|
cwd: '/project',
|
|
output: {
|
|
git: {
|
|
includeLogs: true,
|
|
},
|
|
},
|
|
} as RepomixConfigMerged;
|
|
|
|
const result = await getGitLogs(['/project'], config, {
|
|
getGitLog: mockGetGitLog,
|
|
});
|
|
|
|
// Should return empty commits array when content cannot be parsed properly
|
|
expect(result?.commits).toEqual([]);
|
|
expect(result?.logContent).toBe(malformedLogContent);
|
|
});
|
|
|
|
test('should handle Windows line endings (CRLF) correctly', async () => {
|
|
// Test with Windows-style line endings (\r\n)
|
|
const mockLogContent = `${GIT_LOG_RECORD_SEPARATOR}2024-01-01 10:00:00 +0900|Windows commit\r\nfile1.txt\r\nfile2.txt`;
|
|
|
|
const mockGetGitLog = vi.fn().mockResolvedValue(mockLogContent);
|
|
const config: RepomixConfigMerged = {
|
|
cwd: '/project',
|
|
output: {
|
|
git: {
|
|
includeLogs: true,
|
|
},
|
|
},
|
|
} as RepomixConfigMerged;
|
|
|
|
const result = await getGitLogs(['/project'], config, {
|
|
getGitLog: mockGetGitLog,
|
|
});
|
|
|
|
expect(result?.commits).toEqual([
|
|
{
|
|
date: '2024-01-01 10:00:00 +0900',
|
|
message: 'Windows commit',
|
|
files: ['file1.txt', 'file2.txt'],
|
|
},
|
|
]);
|
|
});
|
|
|
|
test('should handle mixed line endings correctly', async () => {
|
|
// Test with mixed Unix (\n) and Windows (\r\n) line endings
|
|
const mockLogContent = `${GIT_LOG_RECORD_SEPARATOR}2024-01-01 10:00:00 +0900|Mixed line endings\nfile1.txt\r\nfile2.txt`;
|
|
|
|
const mockGetGitLog = vi.fn().mockResolvedValue(mockLogContent);
|
|
const config: RepomixConfigMerged = {
|
|
cwd: '/project',
|
|
output: {
|
|
git: {
|
|
includeLogs: true,
|
|
},
|
|
},
|
|
} as RepomixConfigMerged;
|
|
|
|
const result = await getGitLogs(['/project'], config, {
|
|
getGitLog: mockGetGitLog,
|
|
});
|
|
|
|
expect(result?.commits).toEqual([
|
|
{
|
|
date: '2024-01-01 10:00:00 +0900',
|
|
message: 'Mixed line endings',
|
|
files: ['file1.txt', 'file2.txt'],
|
|
},
|
|
]);
|
|
});
|
|
});
|
|
});
|