449 lines
13 KiB
JavaScript
449 lines
13 KiB
JavaScript
/**
|
|
* Comprehensive integration test suite for all generateObject-migrated commands
|
|
* Tests end-to-end command execution with real AI service calls
|
|
*/
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { jest } from '@jest/globals';
|
|
|
|
// Import all commands
|
|
import analyzeTaskComplexity from '../../../scripts/modules/task-manager/analyze-task-complexity.js';
|
|
import updateTaskById from '../../../scripts/modules/task-manager/update-task-by-id.js';
|
|
import expandTask from '../../../scripts/modules/task-manager/expand-task.js';
|
|
import updateTasks from '../../../scripts/modules/task-manager/update-tasks.js';
|
|
import addTask from '../../../scripts/modules/task-manager/add-task.js';
|
|
import parsePRD from '../../../scripts/modules/task-manager/parse-prd.js';
|
|
|
|
describe('GenerateObject Migration - Comprehensive Integration Tests', () => {
|
|
const testDir = path.join(process.cwd(), 'test-integration-output');
|
|
const testTasksFile = path.join(testDir, 'test-tasks.json');
|
|
const testPrdFile = path.join(testDir, 'test-prd.md');
|
|
|
|
beforeAll(() => {
|
|
// Create test directory
|
|
if (!fs.existsSync(testDir)) {
|
|
fs.mkdirSync(testDir, { recursive: true });
|
|
}
|
|
});
|
|
|
|
beforeEach(() => {
|
|
// Create initial test data
|
|
const initialTasks = {
|
|
master: {
|
|
tasks: [
|
|
{
|
|
id: 1,
|
|
title: 'Setup project infrastructure',
|
|
description:
|
|
'Initialize the project with proper structure and dependencies',
|
|
status: 'done',
|
|
dependencies: [],
|
|
priority: 'high',
|
|
details:
|
|
'Created project structure with src, tests, and docs folders',
|
|
testStrategy: 'Manual verification of folder structure',
|
|
subtasks: []
|
|
},
|
|
{
|
|
id: 2,
|
|
title: 'Implement authentication system',
|
|
description:
|
|
'Add user authentication with JWT tokens and OAuth2 support',
|
|
status: 'in-progress',
|
|
dependencies: [1],
|
|
priority: 'high',
|
|
details:
|
|
'Need to support both OAuth2 and traditional email/password login',
|
|
testStrategy:
|
|
'Unit tests for auth logic, integration tests for endpoints',
|
|
subtasks: [
|
|
{
|
|
id: 1,
|
|
title: 'Design authentication flow',
|
|
description: 'Create detailed flow diagrams for auth process',
|
|
status: 'done',
|
|
dependencies: []
|
|
},
|
|
{
|
|
id: 2,
|
|
title: 'Implement JWT token generation',
|
|
description:
|
|
'Create secure JWT token generation and validation',
|
|
status: 'pending',
|
|
dependencies: []
|
|
}
|
|
]
|
|
},
|
|
{
|
|
id: 3,
|
|
title: 'Build RESTful API',
|
|
description: 'Create comprehensive REST API endpoints',
|
|
status: 'pending',
|
|
dependencies: [2],
|
|
priority: 'medium',
|
|
details: 'Use Express.js with proper middleware and error handling',
|
|
testStrategy: null,
|
|
subtasks: []
|
|
}
|
|
],
|
|
metadata: {
|
|
created: new Date().toISOString(),
|
|
updated: new Date().toISOString(),
|
|
description: 'Test project tasks'
|
|
}
|
|
}
|
|
};
|
|
|
|
fs.writeFileSync(testTasksFile, JSON.stringify(initialTasks, null, 2));
|
|
|
|
// Create test PRD file
|
|
const testPrd = `# Product Requirements Document
|
|
|
|
## Overview
|
|
We need to build a modern task management system with real-time collaboration features.
|
|
|
|
## Key Features
|
|
1. User authentication and authorization
|
|
2. Task creation and management
|
|
3. Real-time updates via WebSockets
|
|
4. File attachments and comments
|
|
5. Advanced search and filtering
|
|
|
|
## Technical Requirements
|
|
- Node.js backend with Express
|
|
- PostgreSQL database
|
|
- Redis for caching
|
|
- WebSocket support
|
|
- RESTful API design
|
|
|
|
## Success Criteria
|
|
- Support 10,000+ concurrent users
|
|
- Sub-100ms API response times
|
|
- 99.9% uptime SLA`;
|
|
|
|
fs.writeFileSync(testPrdFile, testPrd);
|
|
});
|
|
|
|
afterEach(() => {
|
|
// Clean up test files
|
|
if (fs.existsSync(testTasksFile)) {
|
|
fs.unlinkSync(testTasksFile);
|
|
}
|
|
if (fs.existsSync(testPrdFile)) {
|
|
fs.unlinkSync(testPrdFile);
|
|
}
|
|
});
|
|
|
|
afterAll(() => {
|
|
// Clean up test directory
|
|
if (fs.existsSync(testDir)) {
|
|
fs.rmSync(testDir, { recursive: true });
|
|
}
|
|
});
|
|
|
|
describe('analyze-complexity command', () => {
|
|
test('should analyze task complexity with structured output', async () => {
|
|
const result = await analyzeTaskComplexity(
|
|
testTasksFile,
|
|
2, // Analyze task ID 2
|
|
false, // Don't use research mode
|
|
{
|
|
projectRoot: process.cwd(),
|
|
tag: 'master'
|
|
},
|
|
'json' // JSON output format
|
|
);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result.complexityAnalysis).toBeDefined();
|
|
expect(result.complexityAnalysis.overallComplexity).toMatch(
|
|
/low|medium|high|very high/i
|
|
);
|
|
expect(result.complexityAnalysis.factors).toBeDefined();
|
|
expect(Array.isArray(result.complexityAnalysis.factors)).toBe(true);
|
|
expect(result.complexityAnalysis.timeEstimate).toBeDefined();
|
|
expect(result.complexityAnalysis.riskAssessment).toBeDefined();
|
|
expect(result.telemetryData).toBeDefined();
|
|
}, 30000);
|
|
});
|
|
|
|
describe('add-task command', () => {
|
|
test('should add a new task with structured output', async () => {
|
|
const result = await addTask(
|
|
testTasksFile,
|
|
'Implement caching layer with Redis for improved performance',
|
|
[2], // Depends on task 2
|
|
'medium',
|
|
{
|
|
projectRoot: process.cwd(),
|
|
tag: 'master'
|
|
},
|
|
'json',
|
|
null, // No manual task data
|
|
false // Don't use research mode
|
|
);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result.newTaskId).toBe(4); // Should be the next ID
|
|
expect(result.telemetryData).toBeDefined();
|
|
|
|
// Verify task was added
|
|
const updatedData = JSON.parse(fs.readFileSync(testTasksFile, 'utf8'));
|
|
const newTask = updatedData.master.tasks.find((t) => t.id === 4);
|
|
expect(newTask).toBeDefined();
|
|
expect(newTask.title).toContain('caching');
|
|
expect(newTask.priority).toBe('medium');
|
|
expect(newTask.dependencies).toContain(2);
|
|
}, 30000);
|
|
});
|
|
|
|
describe('expand-task command', () => {
|
|
test('should expand task into subtasks with structured output', async () => {
|
|
const result = await expandTask(
|
|
testTasksFile,
|
|
3, // Expand task ID 3
|
|
5, // Generate 5 subtasks
|
|
false, // Don't use research mode
|
|
{
|
|
projectRoot: process.cwd(),
|
|
tag: 'master'
|
|
},
|
|
'json'
|
|
);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result.expandedTask).toBeDefined();
|
|
expect(result.generatedSubtasks).toBeDefined();
|
|
expect(Array.isArray(result.generatedSubtasks)).toBe(true);
|
|
expect(result.generatedSubtasks.length).toBeGreaterThan(0);
|
|
expect(result.generatedSubtasks.length).toBeLessThanOrEqual(5);
|
|
|
|
// Verify subtasks were added
|
|
const updatedData = JSON.parse(fs.readFileSync(testTasksFile, 'utf8'));
|
|
const task3 = updatedData.master.tasks.find((t) => t.id === 3);
|
|
expect(task3.subtasks).toBeDefined();
|
|
expect(task3.subtasks.length).toBeGreaterThan(0);
|
|
}, 30000);
|
|
});
|
|
|
|
describe('update-task-by-id command', () => {
|
|
test('should update task with structured output (full update mode)', async () => {
|
|
const result = await updateTaskById(
|
|
testTasksFile,
|
|
3, // Update task ID 3
|
|
'Add GraphQL support alongside REST API for more flexible queries',
|
|
false, // Append mode off (full update)
|
|
false, // Don't use research mode
|
|
{
|
|
projectRoot: process.cwd(),
|
|
tag: 'master'
|
|
},
|
|
'json'
|
|
);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result.updatedTask).toBeDefined();
|
|
expect(result.updatedTask.id).toBe(3);
|
|
expect(result.updatedTask.description.toLowerCase()).toContain('graphql');
|
|
expect(result.telemetryData).toBeDefined();
|
|
}, 30000);
|
|
|
|
test('should append to task details (append mode)', async () => {
|
|
const result = await updateTaskById(
|
|
testTasksFile,
|
|
2, // Update task ID 2
|
|
'Add support for multi-factor authentication',
|
|
true, // Append mode on
|
|
false, // Don't use research mode
|
|
{
|
|
projectRoot: process.cwd(),
|
|
tag: 'master'
|
|
},
|
|
'json'
|
|
);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result.updatedTask).toBeDefined();
|
|
expect(result.updatedTask.details).toContain(
|
|
'multi-factor authentication'
|
|
);
|
|
expect(result.telemetryData).toBeDefined();
|
|
}, 30000);
|
|
});
|
|
|
|
describe('update-tasks command', () => {
|
|
test('should update multiple tasks with structured output', async () => {
|
|
const result = await updateTasks(
|
|
testTasksFile,
|
|
2, // Update from task ID 2 onwards
|
|
'Migrate to microservices architecture for better scalability',
|
|
false, // Don't use research mode
|
|
{
|
|
projectRoot: process.cwd(),
|
|
tag: 'master'
|
|
},
|
|
'json'
|
|
);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result.success).toBe(true);
|
|
expect(result.updatedTasks).toBeDefined();
|
|
expect(Array.isArray(result.updatedTasks)).toBe(true);
|
|
expect(result.updatedTasks.length).toBeGreaterThan(0);
|
|
|
|
// Tasks 2 and 3 should be updated (not done)
|
|
const task2 = result.updatedTasks.find((t) => t.id === 2);
|
|
const task3 = result.updatedTasks.find((t) => t.id === 3);
|
|
expect(task2).toBeDefined();
|
|
expect(task3).toBeDefined();
|
|
expect(task2.description.toLowerCase()).toMatch(
|
|
/microservice|scalability/
|
|
);
|
|
expect(task3.description.toLowerCase()).toMatch(
|
|
/microservice|scalability/
|
|
);
|
|
}, 30000);
|
|
});
|
|
|
|
describe('parse-prd command', () => {
|
|
test('should parse PRD and generate tasks with structured output', async () => {
|
|
// Use a new file for PRD output to avoid conflicts
|
|
const prdTasksFile = path.join(testDir, 'prd-tasks.json');
|
|
|
|
const result = await parsePRD(
|
|
testPrdFile,
|
|
prdTasksFile,
|
|
5, // Generate 5 tasks
|
|
{
|
|
projectRoot: process.cwd(),
|
|
force: true,
|
|
append: false,
|
|
research: false,
|
|
tag: 'master'
|
|
}
|
|
);
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result.success).toBe(true);
|
|
expect(result.tasksPath).toBe(prdTasksFile);
|
|
expect(result.telemetryData).toBeDefined();
|
|
|
|
// Verify tasks were generated
|
|
const generatedData = JSON.parse(fs.readFileSync(prdTasksFile, 'utf8'));
|
|
expect(generatedData.master).toBeDefined();
|
|
expect(generatedData.master.tasks).toBeDefined();
|
|
expect(generatedData.master.tasks.length).toBeGreaterThan(0);
|
|
expect(generatedData.master.tasks.length).toBeLessThanOrEqual(5);
|
|
|
|
// Verify task quality
|
|
const firstTask = generatedData.master.tasks[0];
|
|
expect(firstTask.title).toBeTruthy();
|
|
expect(firstTask.description).toBeTruthy();
|
|
expect(firstTask.status).toBe('pending');
|
|
expect(firstTask.priority).toMatch(/low|medium|high/);
|
|
|
|
// Clean up
|
|
fs.unlinkSync(prdTasksFile);
|
|
}, 30000);
|
|
});
|
|
|
|
describe('Command Integration Flow', () => {
|
|
test('should handle a complete workflow with multiple commands', async () => {
|
|
// 1. Add a new task
|
|
const addResult = await addTask(
|
|
testTasksFile,
|
|
'Implement comprehensive logging system',
|
|
[1],
|
|
'high',
|
|
{ projectRoot: process.cwd(), tag: 'master' },
|
|
'json'
|
|
);
|
|
const newTaskId = addResult.newTaskId;
|
|
|
|
// 2. Analyze its complexity
|
|
const complexityResult = await analyzeTaskComplexity(
|
|
testTasksFile,
|
|
newTaskId,
|
|
false,
|
|
{ projectRoot: process.cwd(), tag: 'master' },
|
|
'json'
|
|
);
|
|
expect(complexityResult.complexityAnalysis).toBeDefined();
|
|
|
|
// 3. Expand it into subtasks
|
|
const expandResult = await expandTask(
|
|
testTasksFile,
|
|
newTaskId,
|
|
3,
|
|
false,
|
|
{ projectRoot: process.cwd(), tag: 'master' },
|
|
'json'
|
|
);
|
|
expect(expandResult.generatedSubtasks.length).toBeGreaterThan(0);
|
|
|
|
// 4. Update the task with additional context
|
|
const updateResult = await updateTaskById(
|
|
testTasksFile,
|
|
newTaskId,
|
|
'Include structured logging with JSON format and log aggregation support',
|
|
false,
|
|
false,
|
|
{ projectRoot: process.cwd(), tag: 'master' },
|
|
'json'
|
|
);
|
|
expect(updateResult.updatedTask.description).toContain('JSON format');
|
|
|
|
// 5. Verify final state
|
|
const finalData = JSON.parse(fs.readFileSync(testTasksFile, 'utf8'));
|
|
const finalTask = finalData.master.tasks.find((t) => t.id === newTaskId);
|
|
expect(finalTask).toBeDefined();
|
|
expect(finalTask.subtasks.length).toBeGreaterThan(0);
|
|
expect(finalTask.description).toContain('JSON format');
|
|
}, 60000); // Longer timeout for multiple operations
|
|
});
|
|
|
|
describe('Error Handling', () => {
|
|
test('should handle invalid task IDs gracefully', async () => {
|
|
await expect(
|
|
analyzeTaskComplexity(
|
|
testTasksFile,
|
|
999, // Non-existent task ID
|
|
false,
|
|
{ projectRoot: process.cwd(), tag: 'master' },
|
|
'json'
|
|
)
|
|
).rejects.toThrow('Task with ID 999 not found');
|
|
});
|
|
|
|
test('should handle empty prompts', async () => {
|
|
await expect(
|
|
addTask(
|
|
testTasksFile,
|
|
'', // Empty prompt
|
|
[],
|
|
'medium',
|
|
{ projectRoot: process.cwd(), tag: 'master' },
|
|
'json'
|
|
)
|
|
).rejects.toThrow();
|
|
});
|
|
|
|
test('should handle invalid dependencies', async () => {
|
|
const result = await addTask(
|
|
testTasksFile,
|
|
'New task with invalid dependency',
|
|
[999], // Non-existent dependency
|
|
'medium',
|
|
{ projectRoot: process.cwd(), tag: 'master' },
|
|
'json'
|
|
);
|
|
|
|
// Should succeed but filter out invalid dependency
|
|
expect(result.newTaskId).toBeDefined();
|
|
const data = JSON.parse(fs.readFileSync(testTasksFile, 'utf8'));
|
|
const newTask = data.master.tasks.find((t) => t.id === result.newTaskId);
|
|
expect(newTask.dependencies).not.toContain(999);
|
|
});
|
|
});
|
|
});
|