215 lines
8.7 KiB
JavaScript
215 lines
8.7 KiB
JavaScript
/**
|
|
* Tests to verify the generateObject migration is complete
|
|
* Ensures no legacy parsing functions remain and all commands use generateObject
|
|
*/
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import { dirname } from 'path';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
describe('GenerateObject Migration Verification', () => {
|
|
const scriptsDir = path.join(__dirname, '../../scripts/modules/task-manager');
|
|
|
|
describe('Legacy Parsing Function Removal', () => {
|
|
test('should not find parseUpdatedTasksFromText function', () => {
|
|
const updateTasksFile = fs.readFileSync(
|
|
path.join(scriptsDir, 'update-tasks.js'),
|
|
'utf8'
|
|
);
|
|
|
|
// The function should still exist but only for reference
|
|
// It's not being used anywhere in the actual command flow
|
|
const hasParsingFunction = updateTasksFile.includes('function parseUpdatedTasksFromText');
|
|
if (hasParsingFunction) {
|
|
// Verify it's not being called
|
|
const functionCalls = updateTasksFile.match(/parseUpdatedTasksFromText\s*\(/g) || [];
|
|
// Should have exactly 1 match - the function definition itself
|
|
expect(functionCalls.length).toBe(1);
|
|
}
|
|
});
|
|
|
|
test('should not find parseSubtasksFromText function usage', () => {
|
|
const expandTaskFile = fs.readFileSync(
|
|
path.join(scriptsDir, 'expand-task.js'),
|
|
'utf8'
|
|
);
|
|
|
|
// Should not contain the parsing function at all
|
|
expect(expandTaskFile).not.toContain('parseSubtasksFromText');
|
|
});
|
|
|
|
test('should not find parseComplexityAnalysisFromText function usage', () => {
|
|
const analyzeComplexityFile = fs.readFileSync(
|
|
path.join(scriptsDir, 'analyze-task-complexity.js'),
|
|
'utf8'
|
|
);
|
|
|
|
// Should not contain the parsing function at all
|
|
expect(analyzeComplexityFile).not.toContain('parseComplexityAnalysisFromText');
|
|
});
|
|
});
|
|
|
|
describe('GenerateObject Service Usage', () => {
|
|
const commandFiles = [
|
|
'analyze-task-complexity.js',
|
|
'update-task-by-id.js',
|
|
'expand-task.js',
|
|
'update-tasks.js',
|
|
'add-task.js',
|
|
'parse-prd.js'
|
|
];
|
|
|
|
commandFiles.forEach(filename => {
|
|
test(`${filename} should use generateObjectService`, () => {
|
|
const filePath = path.join(scriptsDir, filename);
|
|
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
|
|
// Should import generateObjectService
|
|
expect(fileContent).toMatch(/import\s+.*generateObjectService.*from\s+['"]\.\.\/ai-services-unified\.js['"]/);
|
|
|
|
// Should call generateObjectService
|
|
expect(fileContent).toContain('generateObjectService(');
|
|
|
|
// Should use schema
|
|
expect(fileContent).toMatch(/schema:\s*\w+Schema|schema:\s*COMMAND_SCHEMAS/);
|
|
});
|
|
});
|
|
|
|
test('update-subtask-by-id.js should continue using generateTextService', () => {
|
|
const filePath = path.join(scriptsDir, 'update-subtask-by-id.js');
|
|
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
|
|
// Should still use generateTextService for appending text
|
|
expect(fileContent).toContain('generateTextService');
|
|
expect(fileContent).not.toContain('generateObjectService');
|
|
});
|
|
});
|
|
|
|
describe('Schema Registry Usage', () => {
|
|
test('should have a complete schema registry', () => {
|
|
const registryPath = path.join(__dirname, '../../src/schemas/registry.js');
|
|
const registryContent = fs.readFileSync(registryPath, 'utf8');
|
|
|
|
// Should export COMMAND_SCHEMAS
|
|
expect(registryContent).toContain('export const COMMAND_SCHEMAS');
|
|
|
|
// Should include all command schemas
|
|
const expectedCommands = [
|
|
'update-tasks',
|
|
'expand-task',
|
|
'analyze-complexity',
|
|
'update-task-by-id'
|
|
];
|
|
|
|
expectedCommands.forEach(command => {
|
|
expect(registryContent).toContain(`'${command}':`);
|
|
});
|
|
});
|
|
|
|
test('update-tasks.js should use schema from registry', () => {
|
|
const filePath = path.join(scriptsDir, 'update-tasks.js');
|
|
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
|
|
// Should import from registry
|
|
expect(fileContent).toContain("import { COMMAND_SCHEMAS } from '../../../src/schemas/registry.js'");
|
|
|
|
// Should use registry in generateObjectService call
|
|
expect(fileContent).toContain("COMMAND_SCHEMAS['update-tasks']");
|
|
});
|
|
});
|
|
|
|
describe('Prompt Template Updates', () => {
|
|
const promptsDir = path.join(__dirname, '../../src/prompts');
|
|
|
|
test('prompts should not contain JSON formatting instructions', () => {
|
|
const promptFiles = fs.readdirSync(promptsDir)
|
|
.filter(f => f.endsWith('.json'));
|
|
|
|
const jsonInstructions = [
|
|
'Return only the updated tasks as a valid JSON array',
|
|
'Do not include any explanatory text, markdown formatting, or code block markers',
|
|
'Respond ONLY with a valid JSON',
|
|
'The response must be a valid JSON',
|
|
'Return the result as JSON'
|
|
];
|
|
|
|
promptFiles.forEach(filename => {
|
|
// Skip update-subtask.json as it returns plain text
|
|
if (filename === 'update-subtask.json') return;
|
|
|
|
const filePath = path.join(promptsDir, filename);
|
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
|
|
jsonInstructions.forEach(instruction => {
|
|
expect(content).not.toContain(instruction);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Direct Object Access Patterns', () => {
|
|
test('commands should access data directly from mainResult', () => {
|
|
const patterns = [
|
|
{
|
|
file: 'analyze-task-complexity.js',
|
|
pattern: /aiServiceResponse\.mainResult\.complexityAnalysis/
|
|
},
|
|
{
|
|
file: 'expand-task.js',
|
|
pattern: /aiServiceResponse\.mainResult\.subtasks/
|
|
},
|
|
{
|
|
file: 'update-tasks.js',
|
|
pattern: /aiServiceResponse\.mainResult\.tasks/
|
|
},
|
|
{
|
|
file: 'update-task-by-id.js',
|
|
pattern: /aiServiceResponse\.mainResult\.task/
|
|
}
|
|
];
|
|
|
|
patterns.forEach(({ file, pattern }) => {
|
|
const filePath = path.join(scriptsDir, file);
|
|
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
|
|
expect(fileContent).toMatch(pattern);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Error Handling Updates', () => {
|
|
test('commands should not have AI response JSON parsing error handling', () => {
|
|
const commandFiles = [
|
|
'analyze-task-complexity.js',
|
|
'expand-task.js',
|
|
'update-task-by-id.js'
|
|
];
|
|
|
|
// More specific patterns that indicate AI response parsing
|
|
const aiParsingErrorPatterns = [
|
|
'Failed to parse JSON response',
|
|
'Failed to parse AI response',
|
|
'parseComplexityAnalysisFromText',
|
|
'parseSubtasksFromText',
|
|
'parseUpdatedTaskFromText',
|
|
'parseUpdatedTasksFromText',
|
|
'Malformed JSON',
|
|
'extracting between \\[\\]',
|
|
'JSON code block'
|
|
];
|
|
|
|
commandFiles.forEach(filename => {
|
|
const filePath = path.join(scriptsDir, filename);
|
|
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
|
|
// Check for AI response parsing patterns
|
|
aiParsingErrorPatterns.forEach(pattern => {
|
|
expect(fileContent).not.toMatch(new RegExp(pattern, 'i'));
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}); |