148 lines
5.3 KiB
TypeScript
148 lines
5.3 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import * as path from 'path';
|
|
import { IterativeContextBuilder } from '../ts/context/iterative-context-builder.js';
|
|
import type { IIterativeConfig, TaskType } from '../ts/context/types.js';
|
|
import * as qenv from '@push.rocks/qenv';
|
|
|
|
// Test project directory
|
|
const testProjectRoot = path.join(process.cwd());
|
|
|
|
// Helper to check if OPENAI_TOKEN is available
|
|
async function hasOpenAIToken(): Promise<boolean> {
|
|
try {
|
|
const qenvInstance = new qenv.Qenv();
|
|
const token = await qenvInstance.getEnvVarOnDemand('OPENAI_TOKEN');
|
|
return !!token;
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
tap.test('IterativeContextBuilder should create instance with default config', async () => {
|
|
const builder = new IterativeContextBuilder(testProjectRoot);
|
|
expect(builder).toBeInstanceOf(IterativeContextBuilder);
|
|
});
|
|
|
|
tap.test('IterativeContextBuilder should create instance with custom config', async () => {
|
|
const customConfig: Partial<IIterativeConfig> = {
|
|
maxIterations: 3,
|
|
firstPassFileLimit: 5,
|
|
subsequentPassFileLimit: 3,
|
|
temperature: 0.5,
|
|
model: 'gpt-4',
|
|
};
|
|
const builder = new IterativeContextBuilder(testProjectRoot, customConfig);
|
|
expect(builder).toBeInstanceOf(IterativeContextBuilder);
|
|
});
|
|
|
|
tap.test('IterativeContextBuilder should initialize successfully', async () => {
|
|
if (!(await hasOpenAIToken())) {
|
|
console.log('⚠️ Skipping initialization test - OPENAI_TOKEN not available');
|
|
return;
|
|
}
|
|
|
|
const builder = new IterativeContextBuilder(testProjectRoot);
|
|
await builder.initialize();
|
|
// If we get here without error, initialization succeeded
|
|
expect(true).toEqual(true);
|
|
});
|
|
|
|
tap.test('IterativeContextBuilder should build context iteratively for readme task', async () => {
|
|
if (!(await hasOpenAIToken())) {
|
|
console.log('⚠️ Skipping iterative build test - OPENAI_TOKEN not available');
|
|
return;
|
|
}
|
|
|
|
const builder = new IterativeContextBuilder(testProjectRoot, {
|
|
maxIterations: 2, // Limit iterations for testing
|
|
firstPassFileLimit: 3,
|
|
subsequentPassFileLimit: 2,
|
|
});
|
|
|
|
await builder.initialize();
|
|
|
|
const result = await builder.buildContextIteratively('readme');
|
|
|
|
// Verify result structure
|
|
expect(result).toBeTypeOf('object');
|
|
expect(result.context).toBeTypeOf('string');
|
|
expect(result.context.length).toBeGreaterThan(0);
|
|
expect(result.tokenCount).toBeTypeOf('number');
|
|
expect(result.tokenCount).toBeGreaterThan(0);
|
|
expect(result.includedFiles).toBeInstanceOf(Array);
|
|
expect(result.includedFiles.length).toBeGreaterThan(0);
|
|
expect(result.iterationCount).toBeTypeOf('number');
|
|
expect(result.iterationCount).toBeGreaterThan(0);
|
|
expect(result.iterationCount).toBeLessThanOrEqual(2);
|
|
expect(result.iterations).toBeInstanceOf(Array);
|
|
expect(result.iterations.length).toEqual(result.iterationCount);
|
|
expect(result.apiCallCount).toBeTypeOf('number');
|
|
expect(result.apiCallCount).toBeGreaterThan(0);
|
|
expect(result.totalDuration).toBeTypeOf('number');
|
|
expect(result.totalDuration).toBeGreaterThan(0);
|
|
|
|
// Verify iteration structure
|
|
for (const iteration of result.iterations) {
|
|
expect(iteration.iteration).toBeTypeOf('number');
|
|
expect(iteration.filesLoaded).toBeInstanceOf(Array);
|
|
expect(iteration.tokensUsed).toBeTypeOf('number');
|
|
expect(iteration.totalTokensUsed).toBeTypeOf('number');
|
|
expect(iteration.decision).toBeTypeOf('object');
|
|
expect(iteration.duration).toBeTypeOf('number');
|
|
}
|
|
|
|
console.log(`✅ Iterative context build completed:`);
|
|
console.log(` Iterations: ${result.iterationCount}`);
|
|
console.log(` Files: ${result.includedFiles.length}`);
|
|
console.log(` Tokens: ${result.tokenCount}`);
|
|
console.log(` API calls: ${result.apiCallCount}`);
|
|
console.log(` Duration: ${(result.totalDuration / 1000).toFixed(2)}s`);
|
|
});
|
|
|
|
tap.test('IterativeContextBuilder should respect token budget', async () => {
|
|
if (!(await hasOpenAIToken())) {
|
|
console.log('⚠️ Skipping token budget test - OPENAI_TOKEN not available');
|
|
return;
|
|
}
|
|
|
|
const builder = new IterativeContextBuilder(testProjectRoot, {
|
|
maxIterations: 5,
|
|
});
|
|
|
|
await builder.initialize();
|
|
|
|
const result = await builder.buildContextIteratively('description');
|
|
|
|
// Token count should not exceed budget significantly (allow 5% margin for safety)
|
|
const configManager = (await import('../ts/context/config-manager.js')).ConfigManager.getInstance();
|
|
const maxTokens = configManager.getMaxTokens();
|
|
expect(result.tokenCount).toBeLessThanOrEqual(maxTokens * 1.05);
|
|
|
|
console.log(`✅ Token budget respected: ${result.tokenCount}/${maxTokens}`);
|
|
});
|
|
|
|
tap.test('IterativeContextBuilder should work with different task types', async () => {
|
|
if (!(await hasOpenAIToken())) {
|
|
console.log('⚠️ Skipping task types test - OPENAI_TOKEN not available');
|
|
return;
|
|
}
|
|
|
|
const taskTypes: TaskType[] = ['readme', 'description', 'commit'];
|
|
|
|
for (const taskType of taskTypes) {
|
|
const builder = new IterativeContextBuilder(testProjectRoot, {
|
|
maxIterations: 2,
|
|
firstPassFileLimit: 2,
|
|
});
|
|
|
|
await builder.initialize();
|
|
const result = await builder.buildContextIteratively(taskType);
|
|
|
|
expect(result.includedFiles.length).toBeGreaterThan(0);
|
|
|
|
console.log(`✅ ${taskType}: ${result.includedFiles.length} files, ${result.tokenCount} tokens`);
|
|
}
|
|
});
|
|
|
|
export default tap.start();
|