takt/src/__tests__/autoCommit.test.ts
nrslib 8c83cf60f9 stageAndCommit から git add -f .takt/reports/ を削除
エージェントが c89ac4c で追加した force-add により、worktree 実行時に
.takt/reports/ がコミットに含まれてしまう問題を修正。
.takt/ は .gitignore で除外済みのため force-add は不要。
2026-02-06 16:11:18 +09:00

157 lines
4.5 KiB
TypeScript

/**
* Tests for autoCommitAndPush
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { autoCommitAndPush } from '../infra/task/autoCommit.js';
// Mock child_process.execFileSync
vi.mock('node:child_process', () => ({
execFileSync: vi.fn(),
}));
import { execFileSync } from 'node:child_process';
const mockExecFileSync = vi.mocked(execFileSync);
beforeEach(() => {
vi.clearAllMocks();
});
describe('autoCommitAndPush', () => {
it('should create a commit and push when there are changes', () => {
mockExecFileSync.mockImplementation((cmd, args) => {
const argsArr = args as string[];
if (argsArr[0] === 'status') {
return 'M src/index.ts\n';
}
if (argsArr[0] === 'rev-parse') {
return 'abc1234\n';
}
return Buffer.from('');
});
const result = autoCommitAndPush('/tmp/clone', 'my-task', '/project');
expect(result.success).toBe(true);
expect(result.commitHash).toBe('abc1234');
expect(result.message).toContain('abc1234');
// Verify git add -A was called
expect(mockExecFileSync).toHaveBeenCalledWith(
'git',
['add', '-A'],
expect.objectContaining({ cwd: '/tmp/clone' })
);
// Verify commit was called with correct message (no co-author)
expect(mockExecFileSync).toHaveBeenCalledWith(
'git',
['commit', '-m', 'takt: my-task'],
expect.objectContaining({ cwd: '/tmp/clone' })
);
// Verify push was called with projectDir directly (no origin remote)
expect(mockExecFileSync).toHaveBeenCalledWith(
'git',
['push', '/project', 'HEAD'],
expect.objectContaining({ cwd: '/tmp/clone' })
);
});
it('should return success with no commit when there are no changes', () => {
mockExecFileSync.mockImplementation((cmd, args) => {
const argsArr = args as string[];
if (argsArr[0] === 'status') {
return ''; // No changes
}
return Buffer.from('');
});
const result = autoCommitAndPush('/tmp/clone', 'my-task', '/project');
expect(result.success).toBe(true);
expect(result.commitHash).toBeUndefined();
expect(result.message).toBe('No changes to commit');
// Verify git add -A was called
expect(mockExecFileSync).toHaveBeenCalledWith(
'git',
['add', '-A'],
expect.objectContaining({ cwd: '/tmp/clone' })
);
// Verify commit was NOT called
expect(mockExecFileSync).not.toHaveBeenCalledWith(
'git',
['commit', '-m', expect.any(String)],
expect.anything()
);
// Verify push was NOT called
expect(mockExecFileSync).not.toHaveBeenCalledWith(
'git',
['push', '/project', 'HEAD'],
expect.anything()
);
});
it('should return failure when git command fails', () => {
mockExecFileSync.mockImplementation(() => {
throw new Error('git error: not a git repository');
});
const result = autoCommitAndPush('/tmp/clone', 'my-task', '/project');
expect(result.success).toBe(false);
expect(result.commitHash).toBeUndefined();
expect(result.message).toContain('Auto-commit failed');
expect(result.message).toContain('not a git repository');
});
it('should not include co-author in commit message', () => {
mockExecFileSync.mockImplementation((cmd, args) => {
const argsArr = args as string[];
if (argsArr[0] === 'status') {
return 'M file.ts\n';
}
if (argsArr[0] === 'rev-parse') {
return 'def5678\n';
}
return Buffer.from('');
});
autoCommitAndPush('/tmp/clone', 'test-task', '/project');
// Find the commit call
const commitCall = mockExecFileSync.mock.calls.find(
call => (call[1] as string[])[0] === 'commit'
);
expect(commitCall).toBeDefined();
const commitMessage = (commitCall![1] as string[])[2];
expect(commitMessage).toBe('takt: test-task');
expect(commitMessage).not.toContain('Co-Authored-By');
});
it('should use the correct commit message format', () => {
mockExecFileSync.mockImplementation((cmd, args) => {
const argsArr = args as string[];
if (argsArr[0] === 'status') {
return 'A new-file.ts\n';
}
if (argsArr[0] === 'rev-parse') {
return 'aaa1111\n';
}
return Buffer.from('');
});
autoCommitAndPush('/tmp/clone', '認証機能を追加する', '/project');
const commitCall = mockExecFileSync.mock.calls.find(
call => (call[1] as string[])[0] === 'commit'
);
expect((commitCall![1] as string[])[2]).toBe('takt: 認証機能を追加する');
});
});