takt/src/__tests__/getFilesChanged.test.ts

104 lines
3.0 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
vi.mock('node:child_process', () => ({
execFileSync: vi.fn(),
}));
import { execFileSync } from 'node:child_process';
const mockExecFileSync = vi.mocked(execFileSync);
import { getFilesChanged } from '../infra/task/branchList.js';
beforeEach(() => {
vi.clearAllMocks();
});
describe('getFilesChanged', () => {
it('should count changed files from branch entry base commit via reflog', () => {
mockExecFileSync
.mockReturnValueOnce('f00dbabe\nfeedface\nabc123\n')
.mockReturnValueOnce('1\t0\tfile1.ts\n2\t1\tfile2.ts\n');
const result = getFilesChanged('/project', 'main', 'takt/20260128-fix-auth');
expect(result).toBe(2);
expect(mockExecFileSync).toHaveBeenNthCalledWith(
2,
'git',
['diff', '--numstat', 'abc123..takt/20260128-fix-auth'],
expect.objectContaining({ cwd: '/project', encoding: 'utf-8' }),
);
});
it('should infer base from refs when reflog is unavailable', () => {
let developMergeBaseCalls = 0;
mockExecFileSync.mockImplementation((cmd, args) => {
if (cmd !== 'git') {
throw new Error('unexpected command');
}
if (args[0] === 'reflog') {
throw new Error('reflog unavailable');
}
if (args[0] === 'merge-base' && args[1] === 'develop') {
developMergeBaseCalls += 1;
if (developMergeBaseCalls === 1) {
throw new Error('priority develop failed');
}
return 'base999\n';
}
if (args[0] === 'merge-base' && args[1] === 'origin/develop') {
throw new Error('priority origin/develop failed');
}
if (args[0] === 'rev-parse' && args[1] === '--git-common-dir') {
return '.git\n';
}
if (args[0] === 'for-each-ref') {
return 'develop\n';
}
if (args[0] === 'rev-list') {
return '1\n';
}
if (args[0] === 'log' && args[1] === '--format=%s') {
return 'takt: initial\n';
}
if (args[0] === 'diff' && args[1] === '--numstat') {
return '1\t0\tfile1.ts\n';
}
throw new Error(`Unexpected git args: ${args.join(' ')}`);
});
const result = getFilesChanged('/project', 'develop', 'takt/20260128-fix-auth');
expect(result).toBe(1);
expect(mockExecFileSync).toHaveBeenCalledWith(
'git',
['for-each-ref', '--format=%(refname:short)', 'refs/heads', 'refs/remotes'],
expect.objectContaining({ cwd: '/project', encoding: 'utf-8' }),
);
expect(mockExecFileSync).toHaveBeenCalledWith(
'git',
['merge-base', 'develop', 'takt/20260128-fix-auth'],
expect.objectContaining({ cwd: '/project', encoding: 'utf-8' }),
);
});
it('should return 0 when base commit resolution fails', () => {
mockExecFileSync.mockImplementation(() => {
throw new Error('base resolution failed');
});
const result = getFilesChanged('/project', 'main', 'takt/20260128-fix-auth');
expect(result).toBe(0);
});
});