mirror of
https://github.com/yamadashy/repomix.git
synced 2025-06-11 00:25:54 +03:00
test(core): improve test coverage
This commit is contained in:
@@ -51,5 +51,7 @@ function setupErrorHandlers() {
|
||||
} else {
|
||||
console.error('Fatal Error:', error);
|
||||
}
|
||||
|
||||
process.exit(EXIT_CODES.ERROR);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -18,21 +18,28 @@ export const runRemoteAction = async (repoUrl: string, options: CliOptions): Pro
|
||||
throw new RepomixError('Git is not installed or not in the system PATH.');
|
||||
}
|
||||
|
||||
const formattedUrl = formatGitUrl(repoUrl);
|
||||
const tempDir = await createTempDirectory();
|
||||
const spinner = new Spinner('Cloning repository...');
|
||||
|
||||
const tempDirPath = await createTempDirectory();
|
||||
|
||||
try {
|
||||
spinner.start();
|
||||
await cloneRepository(formattedUrl, tempDir);
|
||||
|
||||
// Clone the repository
|
||||
await cloneRepository(formatGitUrl(repoUrl), tempDirPath);
|
||||
|
||||
spinner.succeed('Repository cloned successfully!');
|
||||
logger.log('');
|
||||
|
||||
const result = await runDefaultAction(tempDir, tempDir, options);
|
||||
await copyOutputToCurrentDirectory(tempDir, process.cwd(), result.config.output.filePath);
|
||||
// Run the default action on the cloned repository
|
||||
const result = await runDefaultAction(tempDirPath, tempDirPath, options);
|
||||
await copyOutputToCurrentDirectory(tempDirPath, process.cwd(), result.config.output.filePath);
|
||||
} catch (error) {
|
||||
spinner.fail('Error during repository cloning. cleanup...');
|
||||
throw error;
|
||||
} finally {
|
||||
// Clean up the temporary directory
|
||||
await cleanupTempDirectory(tempDir);
|
||||
// Cleanup the temporary directory
|
||||
await cleanupTempDirectory(tempDirPath);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
import * as fs from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import * as os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
import {
|
||||
checkGitInstallation,
|
||||
cleanupTempDirectory,
|
||||
copyOutputToCurrentDirectory,
|
||||
createTempDirectory,
|
||||
formatGitUrl,
|
||||
runRemoteAction,
|
||||
} from '../../../src/cli/actions/remoteAction.js';
|
||||
|
||||
vi.mock('node:child_process');
|
||||
vi.mock('node:fs/promises');
|
||||
vi.mock('node:os');
|
||||
vi.mock('node:fs/promises', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('node:fs/promises')>();
|
||||
return {
|
||||
...actual,
|
||||
copyFile: vi.fn(),
|
||||
};
|
||||
});
|
||||
vi.mock('../../../src/shared/logger');
|
||||
|
||||
describe('remoteAction functions', () => {
|
||||
@@ -19,6 +25,20 @@ describe('remoteAction functions', () => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('runRemoteAction', () => {
|
||||
test('should clone the repository', async () => {
|
||||
vi.mocked(fs.copyFile).mockResolvedValue(undefined);
|
||||
await runRemoteAction('yamadashy/repomix', {});
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkGitInstallation Integration', () => {
|
||||
test('should detect git installation in real environment', async () => {
|
||||
const result = await checkGitInstallation();
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatGitUrl', () => {
|
||||
test('should convert GitHub shorthand to full URL', () => {
|
||||
expect(formatGitUrl('user/repo')).toBe('https://github.com/user/repo.git');
|
||||
@@ -37,29 +57,6 @@ describe('remoteAction functions', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('createTempDirectory', () => {
|
||||
test('should create temporary directory', async () => {
|
||||
const mockTempDir = '/mock/temp/dir';
|
||||
vi.mocked(os.tmpdir).mockReturnValue('/mock/temp');
|
||||
vi.mocked(fs.mkdtemp).mockResolvedValue(mockTempDir);
|
||||
|
||||
const result = await createTempDirectory();
|
||||
expect(result).toBe(mockTempDir);
|
||||
expect(fs.mkdtemp).toHaveBeenCalledWith(path.join('/mock/temp', 'repomix-'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('cleanupTempDirectory', () => {
|
||||
test('should cleanup directory', async () => {
|
||||
const mockDir = '/mock/temp/dir';
|
||||
vi.mocked(fs.rm).mockResolvedValue();
|
||||
|
||||
await cleanupTempDirectory(mockDir);
|
||||
|
||||
expect(fs.rm).toHaveBeenCalledWith(mockDir, { recursive: true, force: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('copyOutputToCurrentDirectory', () => {
|
||||
test('should copy output file', async () => {
|
||||
const sourceDir = '/source/dir';
|
||||
|
||||
132
tests/cli/cliPrint.test.ts
Normal file
132
tests/cli/cliPrint.test.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import path from 'node:path';
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
import { printCompletion, printSecurityCheck, printSummary, printTopFiles } from '../../src/cli/cliPrint.js';
|
||||
import type { SuspiciousFileResult } from '../../src/core/security/securityCheck.js';
|
||||
import { logger } from '../../src/shared/logger.js';
|
||||
import { createMockConfig, isWindows } from '../testing/testUtils.js';
|
||||
|
||||
vi.mock('../../src/shared/logger');
|
||||
vi.mock('picocolors', () => ({
|
||||
default: {
|
||||
white: (str: string) => `WHITE:${str}`,
|
||||
dim: (str: string) => `DIM:${str}`,
|
||||
green: (str: string) => `GREEN:${str}`,
|
||||
yellow: (str: string) => `YELLOW:${str}`,
|
||||
red: (str: string) => `RED:${str}`,
|
||||
cyan: (str: string) => `CYAN:${str}`,
|
||||
},
|
||||
}));
|
||||
|
||||
describe('cliPrint', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('printSummary', () => {
|
||||
test('should print summary with suspicious files and security check enabled', () => {
|
||||
const config = createMockConfig({
|
||||
security: { enableSecurityCheck: true },
|
||||
});
|
||||
const suspiciousFiles: SuspiciousFileResult[] = [
|
||||
{ filePath: 'suspicious.txt', messages: ['Contains sensitive data'] },
|
||||
];
|
||||
|
||||
printSummary(10, 1000, 200, 'output.txt', suspiciousFiles, config);
|
||||
|
||||
expect(logger.log).toHaveBeenCalledWith(expect.stringContaining('1 suspicious file(s) detected and excluded'));
|
||||
});
|
||||
|
||||
test('should print summary with security check disabled', () => {
|
||||
const config = createMockConfig({
|
||||
security: { enableSecurityCheck: false },
|
||||
});
|
||||
|
||||
printSummary(10, 1000, 200, 'output.txt', [], config);
|
||||
|
||||
expect(logger.log).toHaveBeenCalledWith(expect.stringContaining('Security check disabled'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('printSecurityCheck', () => {
|
||||
test('should skip printing when security check is disabled', () => {
|
||||
const config = createMockConfig({
|
||||
security: { enableSecurityCheck: false },
|
||||
});
|
||||
|
||||
printSecurityCheck('/root', [], config);
|
||||
expect(logger.log).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should print message when no suspicious files found', () => {
|
||||
const config = createMockConfig({
|
||||
security: { enableSecurityCheck: true },
|
||||
});
|
||||
|
||||
printSecurityCheck('/root', [], config);
|
||||
|
||||
expect(logger.log).toHaveBeenCalledWith('WHITE:🔎 Security Check:');
|
||||
expect(logger.log).toHaveBeenCalledWith('DIM:──────────────────');
|
||||
expect(logger.log).toHaveBeenCalledWith('GREEN:✔ WHITE:No suspicious files detected.');
|
||||
});
|
||||
|
||||
test('should print details of suspicious files when found', () => {
|
||||
const config = createMockConfig({
|
||||
security: { enableSecurityCheck: true },
|
||||
});
|
||||
const configRelativePath = path.join('config', 'secrets.txt');
|
||||
const suspiciousFiles: SuspiciousFileResult[] = [
|
||||
{
|
||||
filePath: path.join('/root', configRelativePath),
|
||||
messages: ['Contains API key', 'Contains password'],
|
||||
},
|
||||
];
|
||||
|
||||
printSecurityCheck('/root', suspiciousFiles, config);
|
||||
|
||||
expect(logger.log).toHaveBeenCalledWith('YELLOW:1 suspicious file(s) detected and excluded from the output:');
|
||||
expect(logger.log).toHaveBeenCalledWith(`WHITE:1. WHITE:${configRelativePath}`);
|
||||
expect(logger.log).toHaveBeenCalledWith(expect.stringContaining('Contains API key'));
|
||||
expect(logger.log).toHaveBeenCalledWith(expect.stringContaining('Contains password'));
|
||||
expect(logger.log).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Please review these files for potential sensitive information.'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('printTopFiles', () => {
|
||||
test('should print top files sorted by character count', () => {
|
||||
const fileCharCounts = {
|
||||
'src/index.ts': 1000,
|
||||
'src/utils.ts': 500,
|
||||
'README.md': 2000,
|
||||
};
|
||||
const fileTokenCounts = {
|
||||
'src/index.ts': 200,
|
||||
'src/utils.ts': 100,
|
||||
'README.md': 400,
|
||||
};
|
||||
|
||||
printTopFiles(fileCharCounts, fileTokenCounts, 2);
|
||||
|
||||
expect(logger.log).toHaveBeenCalledWith(expect.stringContaining('Top 2 Files'));
|
||||
expect(logger.log).toHaveBeenCalledWith(expect.stringContaining('README.md'));
|
||||
expect(logger.log).toHaveBeenCalledWith(expect.stringContaining('src/index.ts'));
|
||||
expect(logger.log).not.toHaveBeenCalledWith(expect.stringContaining('src/utils.ts'));
|
||||
});
|
||||
|
||||
test('should handle empty file list', () => {
|
||||
printTopFiles({}, {}, 5);
|
||||
|
||||
expect(logger.log).toHaveBeenCalledWith(expect.stringContaining('Top 5 Files'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('printCompletion', () => {
|
||||
test('should print completion message', () => {
|
||||
printCompletion();
|
||||
|
||||
expect(logger.log).toHaveBeenCalledWith('GREEN:🎉 All Done!');
|
||||
expect(logger.log).toHaveBeenCalledWith('WHITE:Your repository has been successfully packed.');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,49 +1,87 @@
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
import { getGlobalDirectory } from '../../src/config/globalDirectory.js';
|
||||
import { isLinux, isMac, isWindows } from '../testing/testUtils.js';
|
||||
|
||||
vi.mock('node:os');
|
||||
|
||||
describe('globalDirectory', () => {
|
||||
describe('getGlobalDirectory', () => {
|
||||
const originalPlatform = process.platform;
|
||||
const originalEnv = process.env;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
process.env = {};
|
||||
process.env = { ...originalEnv };
|
||||
});
|
||||
|
||||
test.runIf(isWindows)('should return correct path for Windows', () => {
|
||||
vi.mocked(os.platform).mockReturnValue('win32');
|
||||
vi.mocked(os.homedir).mockReturnValue('C:\\Users\\TestUser');
|
||||
process.env.LOCALAPPDATA = 'C:\\Users\\TestUser\\AppData\\Local';
|
||||
|
||||
const result = getGlobalDirectory();
|
||||
expect(result).toBe(path.join('C:\\Users\\TestUser\\AppData\\Local', 'Repomix'));
|
||||
afterEach(() => {
|
||||
Object.defineProperty(process, 'platform', { value: originalPlatform });
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
test.runIf(isWindows)('should use homedir if LOCALAPPDATA is not set on Windows', () => {
|
||||
vi.mocked(os.platform).mockReturnValue('win32');
|
||||
vi.mocked(os.homedir).mockReturnValue('C:\\Users\\TestUser');
|
||||
process.env.LOCALAPPDATA = undefined;
|
||||
describe('Windows platform', () => {
|
||||
test('should use LOCALAPPDATA when available', () => {
|
||||
Object.defineProperty(process, 'platform', { value: 'win32' });
|
||||
process.env.LOCALAPPDATA = 'C:\\Users\\Test\\AppData\\Local';
|
||||
|
||||
const result = getGlobalDirectory();
|
||||
expect(result).toBe(path.join('C:\\Users\\TestUser', 'AppData', 'Local', 'Repomix'));
|
||||
const result = getGlobalDirectory();
|
||||
expect(result).toBe(path.join('C:\\Users\\Test\\AppData\\Local', 'Repomix'));
|
||||
});
|
||||
|
||||
test('should fall back to homedir when LOCALAPPDATA is not available', () => {
|
||||
Object.defineProperty(process, 'platform', { value: 'win32' });
|
||||
process.env.LOCALAPPDATA = undefined;
|
||||
vi.mocked(os.homedir).mockReturnValue('C:\\Users\\Test');
|
||||
|
||||
const result = getGlobalDirectory();
|
||||
expect(result).toBe(path.join('C:\\Users\\Test', 'AppData', 'Local', 'Repomix'));
|
||||
});
|
||||
});
|
||||
|
||||
test.runIf(isLinux)('should use XDG_CONFIG_HOME on Unix systems if set', () => {
|
||||
vi.mocked(os.platform).mockReturnValue('linux');
|
||||
process.env.XDG_CONFIG_HOME = '/custom/config';
|
||||
describe('Unix platforms', () => {
|
||||
test('should use XDG_CONFIG_HOME when available', () => {
|
||||
Object.defineProperty(process, 'platform', { value: 'linux' });
|
||||
process.env.XDG_CONFIG_HOME = '/custom/config';
|
||||
|
||||
const result = getGlobalDirectory();
|
||||
expect(result).toBe(path.join('/custom/config', 'repomix'));
|
||||
const result = getGlobalDirectory();
|
||||
expect(result).toBe(path.join('/custom/config', 'repomix'));
|
||||
});
|
||||
|
||||
test('should fall back to ~/.config on Linux', () => {
|
||||
Object.defineProperty(process, 'platform', { value: 'linux' });
|
||||
process.env.XDG_CONFIG_HOME = undefined;
|
||||
vi.mocked(os.homedir).mockReturnValue('/home/test');
|
||||
|
||||
const result = getGlobalDirectory();
|
||||
expect(result).toBe(path.join('/home/test', '.config', 'repomix'));
|
||||
});
|
||||
|
||||
test('should fall back to ~/.config on macOS', () => {
|
||||
Object.defineProperty(process, 'platform', { value: 'darwin' });
|
||||
process.env.XDG_CONFIG_HOME = undefined;
|
||||
vi.mocked(os.homedir).mockReturnValue('/Users/test');
|
||||
|
||||
const result = getGlobalDirectory();
|
||||
expect(result).toBe(path.join('/Users/test', '.config', 'repomix'));
|
||||
});
|
||||
});
|
||||
|
||||
test.runIf(isMac)('should use ~/.config on Unix systems if XDG_CONFIG_HOME is not set', () => {
|
||||
vi.mocked(os.platform).mockReturnValue('darwin');
|
||||
vi.mocked(os.homedir).mockReturnValue('/Users/TestUser');
|
||||
process.env.XDG_CONFIG_HOME = undefined;
|
||||
describe('Edge cases', () => {
|
||||
test('should handle empty homedir', () => {
|
||||
Object.defineProperty(process, 'platform', { value: 'linux' });
|
||||
process.env.XDG_CONFIG_HOME = undefined;
|
||||
vi.mocked(os.homedir).mockReturnValue('');
|
||||
|
||||
const result = getGlobalDirectory();
|
||||
expect(result).toBe(path.join('/Users/TestUser', '.config', 'repomix'));
|
||||
const result = getGlobalDirectory();
|
||||
expect(result).toBe(path.join('', '.config', 'repomix'));
|
||||
});
|
||||
|
||||
test('should handle unusual XDG_CONFIG_HOME paths', () => {
|
||||
Object.defineProperty(process, 'platform', { value: 'linux' });
|
||||
process.env.XDG_CONFIG_HOME = '////multiple///slashes///';
|
||||
|
||||
const result = getGlobalDirectory();
|
||||
expect(result).toBe(path.join('////multiple///slashes///', 'repomix'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user