167 lines
5.0 KiB
TypeScript
167 lines
5.0 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
import { join, resolve, dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { createIsolatedEnv, updateIsolatedConfig, type IsolatedEnv } from '../helpers/isolated-env';
|
|
import { createTestRepo, type TestRepo } from '../helpers/test-repo';
|
|
import { runTakt } from '../helpers/takt-runner';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
function writeAgent(baseDir: string): void {
|
|
const agentsDir = join(baseDir, 'agents');
|
|
mkdirSync(agentsDir, { recursive: true });
|
|
writeFileSync(
|
|
join(agentsDir, 'test-coder.md'),
|
|
'You are a test coder. Complete the task exactly and respond with Done.',
|
|
'utf-8',
|
|
);
|
|
}
|
|
|
|
function writeMinimalPiece(piecePath: string): void {
|
|
const pieceDir = dirname(piecePath);
|
|
mkdirSync(pieceDir, { recursive: true });
|
|
writeFileSync(
|
|
piecePath,
|
|
[
|
|
'name: e2e-branch-piece',
|
|
'description: Piece for branch coverage E2E',
|
|
'max_movements: 3',
|
|
'movements:',
|
|
' - name: execute',
|
|
' edit: true',
|
|
' persona: ../agents/test-coder.md',
|
|
' allowed_tools:',
|
|
' - Read',
|
|
' - Write',
|
|
' - Edit',
|
|
' required_permission_mode: edit',
|
|
' instruction_template: |',
|
|
' {task}',
|
|
' rules:',
|
|
' - condition: Done',
|
|
' next: COMPLETE',
|
|
'',
|
|
].join('\n'),
|
|
'utf-8',
|
|
);
|
|
}
|
|
|
|
function runTaskWithPiece(args: {
|
|
piece?: string;
|
|
cwd: string;
|
|
env: NodeJS.ProcessEnv;
|
|
}): ReturnType<typeof runTakt> {
|
|
const scenarioPath = resolve(__dirname, '../fixtures/scenarios/execute-done.json');
|
|
const baseArgs = ['--task', 'Create a file called noop.txt', '--create-worktree', 'no', '--provider', 'mock'];
|
|
const fullArgs = args.piece ? [...baseArgs, '--piece', args.piece] : baseArgs;
|
|
return runTakt({
|
|
args: fullArgs,
|
|
cwd: args.cwd,
|
|
env: {
|
|
...args.env,
|
|
TAKT_MOCK_SCENARIO: scenarioPath,
|
|
},
|
|
timeout: 240_000,
|
|
});
|
|
}
|
|
|
|
describe('E2E: Piece selection branch coverage', () => {
|
|
let isolatedEnv: IsolatedEnv;
|
|
let testRepo: TestRepo;
|
|
|
|
beforeEach(() => {
|
|
isolatedEnv = createIsolatedEnv();
|
|
testRepo = createTestRepo();
|
|
|
|
updateIsolatedConfig(isolatedEnv.taktDir, {
|
|
provider: 'mock',
|
|
model: 'mock-model',
|
|
enable_builtin_pieces: false,
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
try {
|
|
testRepo.cleanup();
|
|
} catch {
|
|
// best-effort
|
|
}
|
|
try {
|
|
isolatedEnv.cleanup();
|
|
} catch {
|
|
// best-effort
|
|
}
|
|
});
|
|
|
|
it('should execute when --piece is a file path (isPiecePath branch)', () => {
|
|
const customPiecePath = join(testRepo.path, '.takt', 'pieces', 'path-piece.yaml');
|
|
writeAgent(join(testRepo.path, '.takt'));
|
|
writeMinimalPiece(customPiecePath);
|
|
|
|
const result = runTaskWithPiece({
|
|
piece: customPiecePath,
|
|
cwd: testRepo.path,
|
|
env: isolatedEnv.env,
|
|
});
|
|
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.stdout).toContain('Piece completed');
|
|
}, 240_000);
|
|
|
|
it('should execute when --piece is a known local name (resolver hit branch)', () => {
|
|
writeAgent(join(testRepo.path, '.takt'));
|
|
writeMinimalPiece(join(testRepo.path, '.takt', 'pieces', 'local-piece.yaml'));
|
|
|
|
const result = runTaskWithPiece({
|
|
piece: 'local-piece',
|
|
cwd: testRepo.path,
|
|
env: isolatedEnv.env,
|
|
});
|
|
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.stdout).toContain('Piece completed');
|
|
}, 240_000);
|
|
|
|
it('should execute when --piece is a repertoire @scope name (resolver hit branch)', () => {
|
|
const pkgRoot = join(isolatedEnv.taktDir, 'repertoire', '@nrslib', 'takt-ensembles');
|
|
writeAgent(pkgRoot);
|
|
writeMinimalPiece(join(pkgRoot, 'pieces', 'critical-thinking.yaml'));
|
|
|
|
const result = runTaskWithPiece({
|
|
piece: '@nrslib/takt-ensembles/critical-thinking',
|
|
cwd: testRepo.path,
|
|
env: isolatedEnv.env,
|
|
});
|
|
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.stdout).toContain('Piece completed');
|
|
expect(result.stdout).not.toContain('Piece not found');
|
|
}, 240_000);
|
|
|
|
it('should fail fast with message when --piece is unknown (resolver miss branch)', () => {
|
|
const result = runTaskWithPiece({
|
|
piece: '@nrslib/takt-ensembles/not-found',
|
|
cwd: testRepo.path,
|
|
env: isolatedEnv.env,
|
|
});
|
|
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.stdout).toContain('Piece not found: @nrslib/takt-ensembles/not-found');
|
|
expect(result.stdout).toContain('Cancelled');
|
|
}, 240_000);
|
|
|
|
it('should execute when --piece is omitted (selectPiece branch)', () => {
|
|
writeAgent(join(testRepo.path, '.takt'));
|
|
writeMinimalPiece(join(testRepo.path, '.takt', 'pieces', 'default.yaml'));
|
|
|
|
const result = runTaskWithPiece({
|
|
cwd: testRepo.path,
|
|
env: isolatedEnv.env,
|
|
});
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.stdout).toContain('Piece completed');
|
|
}, 240_000);
|
|
});
|