refactor(core): Update GitDiffResult imports and restructure git handling modules

This commit is contained in:
Kazuki Yamada
2025-05-24 14:14:26 +09:00
parent b13a21aebd
commit fecebc2ca6
19 changed files with 328 additions and 278 deletions

View File

@@ -3,8 +3,9 @@ import os from 'node:os';
import path from 'node:path';
import pc from 'picocolors';
import { execGitShallowClone } from '../../core/git/gitCommand.js';
import { getRemoteRefs, isGitInstalled } from '../../core/git/gitHandle.js';
import { getRemoteRefs } from '../../core/git/gitRemoteHandle.js';
import { parseRemoteValue } from '../../core/git/gitRemoteParse.js';
import { isGitInstalled } from '../../core/git/gitRepositoryHandle.js';
import { RepomixError } from '../../shared/errorHandle.js';
import { logger } from '../../shared/logger.js';
import { Spinner } from '../cliSpinner.js';

View File

@@ -1,43 +1,14 @@
import type { RepomixConfigMerged } from '../../config/configSchema.js';
import { RepomixError } from '../../shared/errorHandle.js';
import { logger } from '../../shared/logger.js';
import {
execGitDiff,
execGitLogFilenames,
execGitRevParse,
execGitVersion,
execLsRemote,
validateGitUrl,
} from './gitCommand.js';
import { execGitDiff } from './gitCommand.js';
import { isGitRepository } from './gitRepositoryHandle.js';
export interface GitDiffResult {
workTreeDiffContent: string;
stagedDiffContent: string;
}
export const getFileChangeCount = async (
directory: string,
maxCommits = 100,
deps = {
execGitLogFilenames,
},
): Promise<Record<string, number>> => {
try {
const filenames = await deps.execGitLogFilenames(directory, maxCommits);
const fileChangeCounts: Record<string, number> = {};
for (const filename of filenames) {
fileChangeCounts[filename] = (fileChangeCounts[filename] || 0) + 1;
}
return fileChangeCounts;
} catch (error) {
logger.trace('Failed to get file change counts:', (error as Error).message);
return {};
}
};
export const getWorkTreeDiff = async (
directory: string,
deps = {
@@ -87,68 +58,6 @@ const getDiff = async (
}
};
export const isGitRepository = async (
directory: string,
deps = {
execGitRevParse,
},
): Promise<boolean> => {
try {
await deps.execGitRevParse(directory);
return true;
} catch (error) {
return false;
}
};
export const isGitInstalled = async (
deps = {
execGitVersion,
},
): Promise<boolean> => {
try {
const result = await deps.execGitVersion();
return !result.includes('error') && result.includes('git version');
} catch (error) {
logger.trace('Git is not installed:', (error as Error).message);
return false;
}
};
export const getRemoteRefs = async (
url: string,
deps = {
execLsRemote,
},
): Promise<string[]> => {
validateGitUrl(url);
try {
const stdout = await deps.execLsRemote(url);
// Extract ref names from the output
// Format is: hash\tref_name
const refs = stdout
.split('\n')
.filter(Boolean)
.map((line) => {
// Skip the hash part and extract only the ref name
const parts = line.split('\t');
if (parts.length < 2) return '';
// Remove 'refs/heads/' or 'refs/tags/' prefix
return parts[1].replace(/^refs\/(heads|tags)\//, '');
})
.filter(Boolean);
logger.trace(`Found ${refs.length} refs in repository: ${url}`);
return refs;
} catch (error) {
logger.trace('Failed to get remote refs:', (error as Error).message);
throw new RepomixError(`Failed to get remote refs: ${(error as Error).message}`);
}
};
export const getGitDiffs = async (
rootDirs: string[],
config: RepomixConfigMerged,

View File

@@ -0,0 +1,37 @@
import { RepomixError } from '../../shared/errorHandle.js';
import { logger } from '../../shared/logger.js';
import { execLsRemote, validateGitUrl } from './gitCommand.js';
export const getRemoteRefs = async (
url: string,
deps = {
execLsRemote,
},
): Promise<string[]> => {
validateGitUrl(url);
try {
const stdout = await deps.execLsRemote(url);
// Extract ref names from the output
// Format is: hash\tref_name
const refs = stdout
.split('\n')
.filter(Boolean)
.map((line) => {
// Skip the hash part and extract only the ref name
const parts = line.split('\t');
if (parts.length < 2) return '';
// Remove 'refs/heads/' or 'refs/tags/' prefix
return parts[1].replace(/^refs\/(heads|tags)\//, '');
})
.filter(Boolean);
logger.trace(`Found ${refs.length} refs in repository: ${url}`);
return refs;
} catch (error) {
logger.trace('Failed to get remote refs:', (error as Error).message);
throw new RepomixError(`Failed to get remote refs: ${(error as Error).message}`);
}
};

View File

@@ -0,0 +1,53 @@
import { logger } from '../../shared/logger.js';
import { execGitLogFilenames, execGitRevParse, execGitVersion } from './gitCommand.js';
export const getFileChangeCount = async (
directory: string,
maxCommits = 100,
deps = {
execGitLogFilenames,
},
): Promise<Record<string, number>> => {
try {
const filenames = await deps.execGitLogFilenames(directory, maxCommits);
const fileChangeCounts: Record<string, number> = {};
for (const filename of filenames) {
fileChangeCounts[filename] = (fileChangeCounts[filename] || 0) + 1;
}
return fileChangeCounts;
} catch (error) {
logger.trace('Failed to get file change counts:', (error as Error).message);
return {};
}
};
export const isGitRepository = async (
directory: string,
deps = {
execGitRevParse,
},
): Promise<boolean> => {
try {
await deps.execGitRevParse(directory);
return true;
} catch (error) {
return false;
}
};
export const isGitInstalled = async (
deps = {
execGitVersion,
},
): Promise<boolean> => {
try {
const result = await deps.execGitVersion();
return !result.includes('error') && result.includes('git version');
} catch (error) {
logger.trace('Git is not installed:', (error as Error).message);
return false;
}
};

View File

@@ -1,7 +1,7 @@
import type { RepomixConfigMerged } from '../../config/configSchema.js';
import type { RepomixProgressCallback } from '../../shared/types.js';
import type { ProcessedFile } from '../file/fileTypes.js';
import type { GitDiffResult } from '../git/gitHandle.js';
import type { GitDiffResult } from '../git/gitDiffHandle.js';
import { calculateAllFileMetrics } from './calculateAllFileMetrics.js';
import { calculateOutputMetrics } from './calculateOutputMetrics.js';

View File

@@ -7,7 +7,7 @@ import { RepomixError } from '../../shared/errorHandle.js';
import { type FileSearchResult, searchFiles } from '../file/fileSearch.js';
import { generateTreeString } from '../file/fileTreeGenerate.js';
import type { ProcessedFile } from '../file/fileTypes.js';
import type { GitDiffResult } from '../git/gitHandle.js';
import type { GitDiffResult } from '../git/gitDiffHandle.js';
import type { OutputGeneratorContext, RenderContext } from './outputGeneratorTypes.js';
import { sortOutputFiles } from './outputSort.js';
import {

View File

@@ -1,6 +1,6 @@
import type { RepomixConfigMerged } from '../../config/configSchema.js';
import type { ProcessedFile } from '../file/fileTypes.js';
import type { GitDiffResult } from '../git/gitHandle.js';
import type { GitDiffResult } from '../git/gitDiffHandle.js';
export interface OutputGeneratorContext {
generationDate: string;

View File

@@ -3,7 +3,7 @@ import path from 'node:path';
import type { RepomixConfigMerged } from '../../config/configSchema.js';
import { logger } from '../../shared/logger.js';
import type { ProcessedFile } from '../file/fileTypes.js';
import { getFileChangeCount, isGitInstalled } from '../git/gitHandle.js';
import { getFileChangeCount, isGitInstalled } from '../git/gitRepositoryHandle.js';
// Sort files by git change count for output
export const sortOutputFiles = async (

View File

@@ -5,7 +5,7 @@ import { sortPaths } from './file/filePathSort.js';
import { processFiles } from './file/fileProcess.js';
import { searchFiles } from './file/fileSearch.js';
import type { RawFile } from './file/fileTypes.js';
import { GitDiffResult, getGitDiffs } from './git/gitHandle.js';
import { GitDiffResult, getGitDiffs } from './git/gitDiffHandle.js';
import { calculateMetrics } from './metrics/calculateMetrics.js';
import { generateOutput } from './output/outputGenerate.js';
import { copyToClipboardIfEnabled } from './packager/copyToClipboardIfEnabled.js';

View File

@@ -3,7 +3,7 @@ import { logger } from '../../shared/logger.js';
import { initPiscina } from '../../shared/processConcurrency.js';
import type { RepomixProgressCallback } from '../../shared/types.js';
import type { RawFile } from '../file/fileTypes.js';
import type { GitDiffResult } from '../git/gitHandle.js';
import type { GitDiffResult } from '../git/gitDiffHandle.js';
import type { SecurityCheckTask, SecurityCheckType } from './workers/securityCheckWorker.js';
export interface SuspiciousFileResult {

View File

@@ -2,7 +2,7 @@ import type { RepomixConfigMerged } from '../../config/configSchema.js';
import { logger } from '../../shared/logger.js';
import type { RepomixProgressCallback } from '../../shared/types.js';
import type { ProcessedFile, RawFile } from '../file/fileTypes.js';
import type { GitDiffResult } from '../git/gitHandle.js';
import type { GitDiffResult } from '../git/gitDiffHandle.js';
import { filterOutUntrustedFiles } from './filterOutUntrustedFiles.js';
import { type SuspiciousFileResult, runSecurityCheck } from './securityCheck.js';

View File

@@ -1,64 +1,15 @@
import { beforeEach, describe, expect, test, vi } from 'vitest';
import {
getFileChangeCount,
getGitDiffs,
getRemoteRefs,
getStagedDiff,
getWorkTreeDiff,
isGitInstalled,
isGitRepository,
} from '../../../src/core/git/gitHandle.js';
import { getGitDiffs, getStagedDiff, getWorkTreeDiff } from '../../../src/core/git/gitDiffHandle.js';
import { logger } from '../../../src/shared/logger.js';
import { createMockConfig } from '../../testing/testUtils.js';
vi.mock('../../../src/shared/logger');
describe('gitHandle', () => {
describe('gitDiffHandle', () => {
beforeEach(() => {
vi.resetAllMocks();
});
describe('getFileChangeCount', () => {
test('should count file changes correctly', async () => {
const mockFilenames = ['file1.ts', 'file2.ts', 'file1.ts', 'file3.ts', 'file2.ts'];
const mockExecGitLogFilenames = vi.fn().mockResolvedValue(mockFilenames);
const result = await getFileChangeCount('/test/dir', 5, {
execGitLogFilenames: mockExecGitLogFilenames,
});
expect(result).toEqual({
'file1.ts': 2,
'file2.ts': 2,
'file3.ts': 1,
});
expect(mockExecGitLogFilenames).toHaveBeenCalledWith('/test/dir', 5);
});
test('should return empty object when git command fails', async () => {
const mockExecGitLogFilenames = vi.fn().mockRejectedValue(new Error('git command failed'));
const result = await getFileChangeCount('/test/dir', 5, {
execGitLogFilenames: mockExecGitLogFilenames,
});
expect(result).toEqual({});
expect(logger.trace).toHaveBeenCalledWith('Failed to get file change counts:', 'git command failed');
});
test('should handle empty git log output', async () => {
const mockExecGitLogFilenames = vi.fn().mockResolvedValue([]);
const result = await getFileChangeCount('/test/dir', 5, {
execGitLogFilenames: mockExecGitLogFilenames,
});
expect(result).toEqual({});
expect(mockExecGitLogFilenames).toHaveBeenCalledWith('/test/dir', 5);
});
});
describe('getWorkTreeDiff', () => {
test('should return diffs when directory is a git repository', async () => {
const mockDiff = 'diff --git a/file.txt b/file.txt\n+new line';
@@ -122,116 +73,6 @@ describe('gitHandle', () => {
});
});
describe('isGitRepository', () => {
test('should return true when directory is a git repository', async () => {
const mockExecGitRevParse = vi.fn().mockResolvedValue('true');
const result = await isGitRepository('/test/dir', {
execGitRevParse: mockExecGitRevParse,
});
expect(result).toBe(true);
expect(mockExecGitRevParse).toHaveBeenCalledWith('/test/dir');
});
test('should return false when directory is not a git repository', async () => {
const mockExecGitRevParse = vi.fn().mockRejectedValue(new Error('Not a git repository'));
const result = await isGitRepository('/test/dir', {
execGitRevParse: mockExecGitRevParse,
});
expect(result).toBe(false);
expect(mockExecGitRevParse).toHaveBeenCalledWith('/test/dir');
});
});
describe('isGitInstalled', () => {
test('should return true when git is installed', async () => {
const mockExecGitVersion = vi.fn().mockResolvedValue('git version 2.34.1');
const result = await isGitInstalled({
execGitVersion: mockExecGitVersion,
});
expect(result).toBe(true);
expect(mockExecGitVersion).toHaveBeenCalled();
});
test('should return false when git command fails', async () => {
const mockExecGitVersion = vi.fn().mockRejectedValue(new Error('Command not found: git'));
const result = await isGitInstalled({
execGitVersion: mockExecGitVersion,
});
expect(result).toBe(false);
expect(mockExecGitVersion).toHaveBeenCalled();
expect(logger.trace).toHaveBeenCalledWith('Git is not installed:', 'Command not found: git');
});
test('should return false when git version output contains error', async () => {
const mockExecGitVersion = vi.fn().mockResolvedValue('error: git not found');
const result = await isGitInstalled({
execGitVersion: mockExecGitVersion,
});
expect(result).toBe(false);
expect(mockExecGitVersion).toHaveBeenCalled();
});
});
describe('getRemoteRefs', () => {
test('should return refs when URL is valid', async () => {
const mockOutput = `
a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6\trefs/heads/main
b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7\trefs/heads/develop
c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8\trefs/tags/v1.0.0
`.trim();
const mockExecLsRemote = vi.fn().mockResolvedValue(mockOutput);
const result = await getRemoteRefs('https://github.com/user/repo.git', {
execLsRemote: mockExecLsRemote,
});
expect(result).toEqual(['main', 'develop', 'v1.0.0']);
expect(mockExecLsRemote).toHaveBeenCalledWith('https://github.com/user/repo.git');
});
test('should throw error when URL does not start with git@ or https://', async () => {
const mockExecLsRemote = vi.fn();
await expect(getRemoteRefs('invalid-url', { execLsRemote: mockExecLsRemote })).rejects.toThrow(
"Invalid URL protocol for 'invalid-url'. URL must start with 'git@' or 'https://'",
);
expect(mockExecLsRemote).not.toHaveBeenCalled();
});
test('should throw error when URL contains dangerous parameters', async () => {
const mockExecLsRemote = vi.fn();
await expect(
getRemoteRefs('https://github.com/user/repo.git --upload-pack=evil-command', {
execLsRemote: mockExecLsRemote,
}),
).rejects.toThrow('Invalid repository URL. URL contains potentially dangerous parameters');
expect(mockExecLsRemote).not.toHaveBeenCalled();
});
test('should throw error when git command fails', async () => {
const mockExecLsRemote = vi.fn().mockRejectedValue(new Error('git command failed'));
await expect(
getRemoteRefs('https://github.com/user/repo.git', { execLsRemote: mockExecLsRemote }),
).rejects.toThrow('Failed to get remote refs: git command failed');
expect(mockExecLsRemote).toHaveBeenCalledWith('https://github.com/user/repo.git');
});
});
describe('getGitDiffs', () => {
test('should return git diffs when includeDiffs is enabled', async () => {
const mockWorkTreeDiff = 'diff --git a/file.txt b/file.txt\n+new line';
@@ -308,6 +149,7 @@ c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8\trefs/tags/v1.0.0
workTreeDiffContent: mockWorkTreeDiff,
stagedDiffContent: mockStagedDiff,
});
// createMockConfig sets cwd to the actual working directory, so we check the actual config value
expect(mockGetWorkTreeDiff).toHaveBeenCalledWith(mockConfig.cwd);
expect(mockGetStagedDiff).toHaveBeenCalledWith(mockConfig.cwd);
});

View File

@@ -0,0 +1,88 @@
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { getRemoteRefs } from '../../../src/core/git/gitRemoteHandle.js';
import { logger } from '../../../src/shared/logger.js';
vi.mock('../../../src/shared/logger');
describe('gitRemoteHandle', () => {
beforeEach(() => {
vi.resetAllMocks();
});
describe('getRemoteRefs', () => {
test('should return refs when URL is valid', async () => {
const mockOutput = `
a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 refs/heads/main
b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7 refs/heads/develop
c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8 refs/tags/v1.0.0
`.trim();
const mockExecLsRemote = vi.fn().mockResolvedValue(mockOutput);
const result = await getRemoteRefs('https://github.com/user/repo.git', {
execLsRemote: mockExecLsRemote,
});
expect(result).toEqual(['main', 'develop', 'v1.0.0']);
expect(mockExecLsRemote).toHaveBeenCalledWith('https://github.com/user/repo.git');
expect(logger.trace).toHaveBeenCalledWith('Found 3 refs in repository: https://github.com/user/repo.git');
});
test('should return empty array when no refs found', async () => {
const mockExecLsRemote = vi.fn().mockResolvedValue('');
const result = await getRemoteRefs('https://github.com/user/repo.git', {
execLsRemote: mockExecLsRemote,
});
expect(result).toEqual([]);
expect(mockExecLsRemote).toHaveBeenCalledWith('https://github.com/user/repo.git');
expect(logger.trace).toHaveBeenCalledWith('Found 0 refs in repository: https://github.com/user/repo.git');
});
test('should throw error when ls-remote fails', async () => {
const mockExecLsRemote = vi.fn().mockRejectedValue(new Error('Repository not found'));
await expect(
getRemoteRefs('https://github.com/user/nonexistent.git', {
execLsRemote: mockExecLsRemote,
}),
).rejects.toThrow('Failed to get remote refs: Repository not found');
expect(mockExecLsRemote).toHaveBeenCalledWith('https://github.com/user/nonexistent.git');
expect(logger.trace).toHaveBeenCalledWith('Failed to get remote refs:', 'Repository not found');
});
test('should handle malformed output lines', async () => {
const mockOutput = `
a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 refs/heads/main
invalid-line-without-tab
b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7 refs/heads/develop
`.trim();
const mockExecLsRemote = vi.fn().mockResolvedValue(mockOutput);
const result = await getRemoteRefs('https://github.com/user/repo.git', {
execLsRemote: mockExecLsRemote,
});
expect(result).toEqual(['main', 'develop']);
expect(mockExecLsRemote).toHaveBeenCalledWith('https://github.com/user/repo.git');
expect(logger.trace).toHaveBeenCalledWith('Found 2 refs in repository: https://github.com/user/repo.git');
});
test('should throw error for invalid URL', async () => {
await expect(
getRemoteRefs('invalid-url', {
execLsRemote: vi.fn(),
}),
).rejects.toThrow('Invalid URL protocol');
});
test('should throw error for dangerous URL parameters', async () => {
await expect(
getRemoteRefs('https://github.com/user/repo.git --upload-pack=evil', {
execLsRemote: vi.fn(),
}),
).rejects.toThrow('Invalid repository URL. URL contains potentially dangerous parameters');
});
});
});

View File

@@ -0,0 +1,112 @@
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { getFileChangeCount, isGitInstalled, isGitRepository } from '../../../src/core/git/gitRepositoryHandle.js';
import { logger } from '../../../src/shared/logger.js';
vi.mock('../../../src/shared/logger');
describe('gitRepositoryHandle', () => {
beforeEach(() => {
vi.resetAllMocks();
});
describe('getFileChangeCount', () => {
test('should count file changes correctly', async () => {
const mockFilenames = ['file1.ts', 'file2.ts', 'file1.ts', 'file3.ts', 'file2.ts'];
const mockExecGitLogFilenames = vi.fn().mockResolvedValue(mockFilenames);
const result = await getFileChangeCount('/test/dir', 5, {
execGitLogFilenames: mockExecGitLogFilenames,
});
expect(result).toEqual({
'file1.ts': 2,
'file2.ts': 2,
'file3.ts': 1,
});
expect(mockExecGitLogFilenames).toHaveBeenCalledWith('/test/dir', 5);
});
test('should return empty object when git command fails', async () => {
const mockExecGitLogFilenames = vi.fn().mockRejectedValue(new Error('git command failed'));
const result = await getFileChangeCount('/test/dir', 5, {
execGitLogFilenames: mockExecGitLogFilenames,
});
expect(result).toEqual({});
expect(logger.trace).toHaveBeenCalledWith('Failed to get file change counts:', 'git command failed');
});
test('should handle empty git log output', async () => {
const mockExecGitLogFilenames = vi.fn().mockResolvedValue([]);
const result = await getFileChangeCount('/test/dir', 5, {
execGitLogFilenames: mockExecGitLogFilenames,
});
expect(result).toEqual({});
expect(mockExecGitLogFilenames).toHaveBeenCalledWith('/test/dir', 5);
});
});
describe('isGitRepository', () => {
test('should return true when directory is a git repository', async () => {
const mockExecGitRevParse = vi.fn().mockResolvedValue('true');
const result = await isGitRepository('/test/dir', {
execGitRevParse: mockExecGitRevParse,
});
expect(result).toBe(true);
expect(mockExecGitRevParse).toHaveBeenCalledWith('/test/dir');
});
test('should return false when directory is not a git repository', async () => {
const mockExecGitRevParse = vi.fn().mockRejectedValue(new Error('Not a git repository'));
const result = await isGitRepository('/test/dir', {
execGitRevParse: mockExecGitRevParse,
});
expect(result).toBe(false);
expect(mockExecGitRevParse).toHaveBeenCalledWith('/test/dir');
});
});
describe('isGitInstalled', () => {
test('should return true when git is installed', async () => {
const mockExecGitVersion = vi.fn().mockResolvedValue('git version 2.34.1');
const result = await isGitInstalled({
execGitVersion: mockExecGitVersion,
});
expect(result).toBe(true);
expect(mockExecGitVersion).toHaveBeenCalled();
});
test('should return false when git command fails', async () => {
const mockExecGitVersion = vi.fn().mockRejectedValue(new Error('Command not found: git'));
const result = await isGitInstalled({
execGitVersion: mockExecGitVersion,
});
expect(result).toBe(false);
expect(mockExecGitVersion).toHaveBeenCalled();
expect(logger.trace).toHaveBeenCalledWith('Git is not installed:', 'Command not found: git');
});
test('should return false when git version output contains error', async () => {
const mockExecGitVersion = vi.fn().mockResolvedValue('error: git not found');
const result = await isGitInstalled({
execGitVersion: mockExecGitVersion,
});
expect(result).toBe(false);
expect(mockExecGitVersion).toHaveBeenCalled();
});
});
});

View File

@@ -1,6 +1,6 @@
import { type Mock, describe, expect, it, vi } from 'vitest';
import type { ProcessedFile } from '../../../src/core/file/fileTypes.js';
import type { GitDiffResult } from '../../../src/core/git/gitHandle.js';
import type { GitDiffResult } from '../../../src/core/git/gitDiffHandle.js';
import { TokenCounter } from '../../../src/core/metrics/TokenCounter.js';
import { calculateAllFileMetrics } from '../../../src/core/metrics/calculateAllFileMetrics.js';
import { calculateMetrics } from '../../../src/core/metrics/calculateMetrics.js';

View File

@@ -1,15 +1,19 @@
import { beforeEach, describe, expect, test, vi } from 'vitest';
import type { RepomixConfigMerged } from '../../../src/config/configSchema.js';
import type { ProcessedFile } from '../../../src/core/file/fileTypes.js';
import type { GitDiffResult } from '../../../src/core/git/gitHandle.js';
import * as gitHandleModule from '../../../src/core/git/gitHandle.js';
import type { GitDiffResult } from '../../../src/core/git/gitDiffHandle.js';
import * as gitDiffModule from '../../../src/core/git/gitDiffHandle.js';
import * as gitRepositoryModule from '../../../src/core/git/gitRepositoryHandle.js';
import { buildOutputGeneratorContext, generateOutput } from '../../../src/core/output/outputGenerate.js';
import type { RenderContext } from '../../../src/core/output/outputGeneratorTypes.js';
import { createMockConfig } from '../../testing/testUtils.js';
// Mock the gitHandle module
vi.mock('../../../src/core/git/gitHandle.js', () => ({
// Mock the git modules
vi.mock('../../../src/core/git/gitDiffHandle.js', () => ({
getWorkTreeDiff: vi.fn(),
}));
vi.mock('../../../src/core/git/gitRepositoryHandle.js', () => ({
isGitRepository: vi.fn(),
}));
@@ -28,8 +32,8 @@ index 123..456 100644
vi.resetAllMocks();
// Mock the git command
vi.mocked(gitHandleModule.getWorkTreeDiff).mockResolvedValue(sampleDiff);
vi.mocked(gitHandleModule.isGitRepository).mockResolvedValue(true);
vi.mocked(gitDiffModule.getWorkTreeDiff).mockResolvedValue(sampleDiff);
vi.mocked(gitRepositoryModule.isGitRepository).mockResolvedValue(true);
// Sample minimal config using createMockConfig utility
mockConfig = createMockConfig({

View File

@@ -1,6 +1,6 @@
import { describe, expect, test, vi } from 'vitest';
import type { RepomixConfigMerged } from '../../../src/config/configSchema.js';
import type { GitDiffResult } from '../../../src/core/git/gitHandle.js';
import type { GitDiffResult } from '../../../src/core/git/gitDiffHandle.js';
import { generateOutput } from '../../../src/core/output/outputGenerate.js';
import type { RenderContext } from '../../../src/core/output/outputGeneratorTypes.js';
import { createMockConfig } from '../../testing/testUtils.js';

View File

@@ -1,18 +1,22 @@
import { beforeEach, describe, expect, test, vi } from 'vitest';
import type { RepomixConfigMerged } from '../../../src/config/configSchema.js';
import type { ProcessedFile } from '../../../src/core/file/fileTypes.js';
import * as gitHandleModule from '../../../src/core/git/gitHandle.js';
import * as gitDiffModule from '../../../src/core/git/gitDiffHandle.js';
import * as gitRepositoryModule from '../../../src/core/git/gitRepositoryHandle.js';
import { pack } from '../../../src/core/packager.js';
import { createMockConfig } from '../../testing/testUtils.js';
// Mock the dependencies
vi.mock('../../../src/core/git/gitHandle.js', () => ({
vi.mock('../../../src/core/git/gitDiffHandle.js', () => ({
getWorkTreeDiff: vi.fn(),
getStagedDiff: vi.fn(),
isGitRepository: vi.fn(),
getGitDiffs: vi.fn(),
}));
vi.mock('../../../src/core/git/gitRepositoryHandle.js', () => ({
isGitRepository: vi.fn(),
}));
describe('Git Diffs Functionality', () => {
let mockConfig: RepomixConfigMerged;
const mockRootDir = '/test/repo';
@@ -41,9 +45,9 @@ index 123..456 100644
});
// Set up our mocks
vi.mocked(gitHandleModule.isGitRepository).mockResolvedValue(true);
vi.mocked(gitHandleModule.getWorkTreeDiff).mockResolvedValue(sampleDiff);
vi.mocked(gitHandleModule.getStagedDiff).mockResolvedValue('');
vi.mocked(gitRepositoryModule.isGitRepository).mockResolvedValue(true);
vi.mocked(gitDiffModule.getWorkTreeDiff).mockResolvedValue(sampleDiff);
vi.mocked(gitDiffModule.getStagedDiff).mockResolvedValue('');
});
test('should not fetch diffs when includeDiffs is disabled', async () => {
@@ -86,7 +90,7 @@ index 123..456 100644
});
// Should not call getWorkTreeDiff
expect(gitHandleModule.getWorkTreeDiff).not.toHaveBeenCalled();
expect(gitDiffModule.getWorkTreeDiff).not.toHaveBeenCalled();
});
test('should calculate diff token count correctly', async () => {

View File

@@ -11,7 +11,7 @@ import type { ProcessedFile } from '../../src/core/file/fileTypes.js';
import type { FileCollectTask } from '../../src/core/file/workers/fileCollectWorker.js';
import fileCollectWorker from '../../src/core/file/workers/fileCollectWorker.js';
import fileProcessWorker from '../../src/core/file/workers/fileProcessWorker.js';
import type { GitDiffResult } from '../../src/core/git/gitHandle.js';
import type { GitDiffResult } from '../../src/core/git/gitDiffHandle.js';
import { generateOutput } from '../../src/core/output/outputGenerate.js';
import { pack } from '../../src/core/packager.js';
import { copyToClipboardIfEnabled } from '../../src/core/packager/copyToClipboardIfEnabled.js';