takt/src/__tests__/getOriginalInstruction.test.ts
2026-02-02 17:11:42 +09:00

98 lines
3.1 KiB
TypeScript

/**
* Tests for getOriginalInstruction
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
// Mock child_process.execFileSync
vi.mock('node:child_process', () => ({
execFileSync: vi.fn(),
}));
import { execFileSync } from 'node:child_process';
const mockExecFileSync = vi.mocked(execFileSync);
import { getOriginalInstruction } from '../infra/task/branchList.js';
beforeEach(() => {
vi.clearAllMocks();
});
describe('getOriginalInstruction', () => {
it('should extract instruction from takt-prefixed commit message', () => {
mockExecFileSync.mockReturnValue('takt: 認証機能を追加する\ntakt: fix-auth\n');
const result = getOriginalInstruction('/project', 'main', 'takt/20260128-fix-auth');
expect(result).toBe('認証機能を追加する');
expect(mockExecFileSync).toHaveBeenCalledWith(
'git',
['log', '--format=%s', '--reverse', 'main..takt/20260128-fix-auth'],
expect.objectContaining({ cwd: '/project', encoding: 'utf-8' }),
);
});
it('should return first commit message without takt prefix if not present', () => {
mockExecFileSync.mockReturnValue('Initial implementation\n');
const result = getOriginalInstruction('/project', 'main', 'takt/20260128-fix-auth');
expect(result).toBe('Initial implementation');
});
it('should return empty string when no commits on branch', () => {
mockExecFileSync.mockReturnValue('');
const result = getOriginalInstruction('/project', 'main', 'takt/20260128-fix-auth');
expect(result).toBe('');
});
it('should return empty string when git command fails', () => {
mockExecFileSync.mockImplementation(() => {
throw new Error('not a git repository');
});
const result = getOriginalInstruction('/non-existent', 'main', 'takt/20260128-fix-auth');
expect(result).toBe('');
});
it('should handle multi-line commit messages (use only first line)', () => {
mockExecFileSync.mockReturnValue('takt: Fix the login bug\ntakt: follow-up fix\n');
const result = getOriginalInstruction('/project', 'main', 'takt/20260128-fix-login');
expect(result).toBe('Fix the login bug');
});
it('should return empty string when takt prefix has no content', () => {
// "takt: \n" trimmed → "takt:", starts with "takt:" → slice + trim → ""
mockExecFileSync.mockReturnValue('takt: \n');
const result = getOriginalInstruction('/project', 'main', 'takt/20260128-task');
expect(result).toBe('');
});
it('should return instruction text when takt prefix has content', () => {
mockExecFileSync.mockReturnValue('takt: add search feature\n');
const result = getOriginalInstruction('/project', 'main', 'takt/20260128-task');
expect(result).toBe('add search feature');
});
it('should use correct git range with custom default branch', () => {
mockExecFileSync.mockReturnValue('takt: Add search feature\n');
getOriginalInstruction('/project', 'master', 'takt/20260128-add-search');
expect(mockExecFileSync).toHaveBeenCalledWith(
'git',
['log', '--format=%s', '--reverse', 'master..takt/20260128-add-search'],
expect.objectContaining({ cwd: '/project' }),
);
});
});