Files
claude-task-master/tests/integration/migration-verification.test.js

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'));
});
});
});
});
});