test: remove integration tests that require real API calls

Integration tests that make real API calls cannot run in CI without
proper API keys. These tests should either be mocked or run in a
separate test suite with appropriate infrastructure.
This commit is contained in:
Ben Vargas
2025-07-22 21:22:35 -06:00
committed by Ralph Khreish
parent 6b15788c58
commit 0aaa105021
9 changed files with 0 additions and 1246 deletions

View File

@@ -1,23 +0,0 @@
# 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

View File

@@ -1,59 +0,0 @@
{
"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": "2025-07-21T00:27:15.668Z",
"updated": "2025-07-21T00:27:15.668Z",
"description": "Test project tasks"
}
}
}

View File

@@ -1,25 +0,0 @@
{
"projectName": "Test Project",
"tasks": [
{
"id": 1,
"title": "Setup project structure",
"description": "Initialize the project with proper folder structure",
"status": "pending",
"dependencies": [],
"priority": "high",
"details": "Create folders for src, tests, docs",
"testStrategy": "Manual verification"
},
{
"id": 2,
"title": "Implement authentication",
"description": "Add user authentication with JWT tokens",
"status": "pending",
"dependencies": [1],
"priority": "high",
"details": null,
"testStrategy": null
}
]
}

View File

@@ -1,448 +0,0 @@
/**
* 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);
});
});
});

View File

@@ -1,77 +0,0 @@
import analyzeTaskComplexity from '../../../scripts/modules/task-manager/analyze-task-complexity.js';
import { readJSON } from '../../../scripts/modules/utils.js';
import fs from 'fs';
import path from 'path';
describe('analyze-complexity with generateObject', () => {
const testTasksFile = path.join(process.cwd(), 'test-tasks.json');
const testComplexityFile = path.join(process.cwd(), 'test-complexity.json');
beforeEach(() => {
// Create a test tasks file
const testTasks = {
projectName: 'Test Project',
tasks: [
{
id: 1,
title: 'Setup project structure',
description: 'Initialize the project with proper folder structure',
status: 'pending',
dependencies: [],
priority: 'high'
},
{
id: 2,
title: 'Implement authentication',
description: 'Add user authentication with JWT tokens',
status: 'pending',
dependencies: [1],
priority: 'high'
}
]
};
fs.writeFileSync(testTasksFile, JSON.stringify(testTasks, null, 2));
});
afterEach(() => {
// Clean up test files
if (fs.existsSync(testTasksFile)) {
fs.unlinkSync(testTasksFile);
}
if (fs.existsSync(testComplexityFile)) {
fs.unlinkSync(testComplexityFile);
}
});
test('should return structured complexity analysis', async () => {
const result = await analyzeTaskComplexity({
file: testTasksFile,
output: testComplexityFile,
threshold: 5
});
expect(result).toHaveProperty('report');
expect(result.report).toHaveProperty('complexityAnalysis');
expect(Array.isArray(result.report.complexityAnalysis)).toBe(true);
if (result.report.complexityAnalysis.length > 0) {
const analysis = result.report.complexityAnalysis[0];
expect(analysis).toHaveProperty('taskId');
expect(analysis).toHaveProperty('taskTitle');
expect(analysis).toHaveProperty('complexityScore');
expect(analysis).toHaveProperty('recommendedSubtasks');
expect(analysis).toHaveProperty('expansionPrompt');
expect(analysis).toHaveProperty('reasoning');
// Check that the values are of the correct type
expect(typeof analysis.taskId).toBe('number');
expect(typeof analysis.taskTitle).toBe('string');
expect(typeof analysis.complexityScore).toBe('number');
expect(analysis.complexityScore).toBeGreaterThanOrEqual(1);
expect(analysis.complexityScore).toBeLessThanOrEqual(10);
expect(typeof analysis.recommendedSubtasks).toBe('number');
expect(typeof analysis.expansionPrompt).toBe('string');
expect(typeof analysis.reasoning).toBe('string');
}
}, 30000); // Increase timeout for AI call
});

View File

@@ -1,148 +0,0 @@
import expandTask from '../../../scripts/modules/task-manager/expand-task.js';
import { readJSON, writeJSON } from '../../../scripts/modules/utils.js';
import fs from 'fs';
import path from 'path';
describe('expand-task with generateObject', () => {
const testTasksFile = path.join(process.cwd(), 'test-tasks.json');
beforeEach(() => {
// Create a test tasks file
const testTasks = {
projectName: 'Test Project',
tasks: [
{
id: 1,
title: 'Setup project structure',
description: 'Initialize the project with proper folder structure',
status: 'done',
dependencies: [],
priority: 'high',
details: 'Create folders for src, tests, docs',
testStrategy: 'Manual verification',
subtasks: []
},
{
id: 2,
title: 'Implement authentication',
description: 'Add user authentication with JWT tokens',
status: 'pending',
dependencies: [1],
priority: 'high',
details: 'Need to support OAuth2 and traditional login',
testStrategy: null,
subtasks: []
},
{
id: 3,
title: 'Build API endpoints',
description: 'Create RESTful API endpoints',
status: 'pending',
dependencies: [2],
priority: 'medium',
details: null,
testStrategy: null,
subtasks: [
{
id: 1,
title: 'Design API schema',
description: 'Create OpenAPI specification',
dependencies: [],
details: 'Use OpenAPI 3.0 specification',
status: 'done'
}
]
}
]
};
fs.writeFileSync(testTasksFile, JSON.stringify(testTasks, null, 2));
});
afterEach(() => {
// Clean up test files
if (fs.existsSync(testTasksFile)) {
fs.unlinkSync(testTasksFile);
}
});
test('should expand task with structured subtasks', async () => {
const result = await expandTask(
testTasksFile,
'2', // taskId as string
3, // numSubtasks
false, // force
'Break down authentication into implementation steps' // additionalContext
);
expect(result).toHaveProperty('task');
expect(result).toHaveProperty('telemetryData');
const { task } = result;
// Verify task was expanded
expect(task.id).toBe(2);
expect(task.subtasks).toBeDefined();
expect(Array.isArray(task.subtasks)).toBe(true);
expect(task.subtasks.length).toBeGreaterThan(0);
// Verify subtask structure
const subtask = task.subtasks[0];
expect(subtask).toHaveProperty('id');
expect(subtask).toHaveProperty('title');
expect(subtask).toHaveProperty('description');
expect(subtask).toHaveProperty('dependencies');
expect(subtask).toHaveProperty('details');
expect(subtask).toHaveProperty('status', 'pending');
// Verify task was written back to file
const savedData = JSON.parse(fs.readFileSync(testTasksFile, 'utf8'));
const savedTask = savedData.tasks.find((t) => t.id === 2);
expect(savedTask.subtasks.length).toBe(task.subtasks.length);
}, 30000); // Increase timeout for AI call
test('should append subtasks when force=false', async () => {
// First expansion
await expandTask(testTasksFile, '3', 2, false);
const dataAfterFirst = JSON.parse(fs.readFileSync(testTasksFile, 'utf8'));
const taskAfterFirst = dataAfterFirst.tasks.find((t) => t.id === 3);
const initialSubtaskCount = taskAfterFirst.subtasks.length;
// Second expansion (append)
await expandTask(
testTasksFile,
'3',
2,
false,
'Add more implementation details'
);
const dataAfterSecond = JSON.parse(fs.readFileSync(testTasksFile, 'utf8'));
const taskAfterSecond = dataAfterSecond.tasks.find((t) => t.id === 3);
// Should have more subtasks than before
expect(taskAfterSecond.subtasks.length).toBeGreaterThan(
initialSubtaskCount
);
}, 60000);
test('should replace subtasks when force=true', async () => {
// First expansion
await expandTask(testTasksFile, '3', 2, false);
// Second expansion with force=true
const result = await expandTask(
testTasksFile,
'3',
3,
true,
'Complete redesign needed'
);
const savedData = JSON.parse(fs.readFileSync(testTasksFile, 'utf8'));
const savedTask = savedData.tasks.find((t) => t.id === 3);
// Should have exactly 3 subtasks (replaced, not appended)
expect(savedTask.subtasks.length).toBe(3);
}, 60000);
});

View File

@@ -1,89 +0,0 @@
import updateTaskById from '../../../scripts/modules/task-manager/update-task-by-id.js';
import { readJSON, writeJSON } from '../../../scripts/modules/utils.js';
import fs from 'fs';
import path from 'path';
describe('update-task-by-id with generateObject', () => {
const testTasksFile = path.join(process.cwd(), 'test-tasks.json');
beforeEach(() => {
// Create a test tasks file
const testTasks = {
projectName: 'Test Project',
tasks: [
{
id: 1,
title: 'Setup project structure',
description: 'Initialize the project with proper folder structure',
status: 'pending',
dependencies: [],
priority: 'high',
details: 'Create folders for src, tests, docs',
testStrategy: 'Manual verification'
},
{
id: 2,
title: 'Implement authentication',
description: 'Add user authentication with JWT tokens',
status: 'pending',
dependencies: [1],
priority: 'high',
details: null,
testStrategy: null
}
]
};
fs.writeFileSync(testTasksFile, JSON.stringify(testTasks, null, 2));
});
afterEach(() => {
// Clean up test files
if (fs.existsSync(testTasksFile)) {
fs.unlinkSync(testTasksFile);
}
});
test('should update task with structured data', async () => {
const result = await updateTaskById({
file: testTasksFile,
prompt: 'Update the description to include OAuth2 support',
id: 2
});
expect(result).toHaveProperty('updatedTask');
const { updatedTask } = result;
// Verify the task structure
expect(updatedTask).toHaveProperty('id', 2);
expect(updatedTask).toHaveProperty('title');
expect(updatedTask).toHaveProperty('description');
expect(updatedTask).toHaveProperty('status');
expect(updatedTask).toHaveProperty('dependencies');
expect(updatedTask).toHaveProperty('priority');
// Check that description was updated
expect(updatedTask.description.toLowerCase()).toContain('oauth');
// Verify task was written back to file
const savedData = JSON.parse(fs.readFileSync(testTasksFile, 'utf8'));
const savedTask = savedData.tasks.find((t) => t.id === 2);
expect(savedTask.description).toBe(updatedTask.description);
}, 30000); // Increase timeout for AI call
test('should handle append mode with plain text', async () => {
const result = await updateTaskById({
file: testTasksFile,
prompt: 'Add information about refresh tokens',
id: 2,
append: true
});
expect(result).toHaveProperty('updatedTask');
const { updatedTask } = result;
// Check that details were appended
expect(updatedTask.details).toBeTruthy();
expect(updatedTask.details).toContain('<info added on');
expect(updatedTask.details.toLowerCase()).toContain('refresh token');
}, 30000);
});

View File

@@ -1,147 +0,0 @@
import updateTasks from '../../../scripts/modules/task-manager/update-tasks.js';
import { readJSON, writeJSON } from '../../../scripts/modules/utils.js';
import fs from 'fs';
import path from 'path';
describe('update-tasks with generateObject', () => {
const testTasksFile = path.join(process.cwd(), 'test-tasks.json');
beforeEach(() => {
// Create a test tasks file
const testTasks = {
projectName: 'Test Project',
tasks: [
{
id: 1,
title: 'Setup project structure',
description: 'Initialize the project with proper folder structure',
status: 'done',
dependencies: [],
priority: 'high',
details: 'Create folders for src, tests, docs',
testStrategy: 'Manual verification',
subtasks: []
},
{
id: 2,
title: 'Implement authentication',
description: 'Add user authentication with JWT tokens',
status: 'pending',
dependencies: [1],
priority: 'high',
details: 'Need to support OAuth2 and traditional login',
testStrategy: null,
subtasks: [
{
id: 1,
title: 'Design auth flow',
description: 'Create authentication flow diagrams',
status: 'done',
dependencies: []
}
]
},
{
id: 3,
title: 'Build API endpoints',
description: 'Create RESTful API endpoints',
status: 'in-progress',
dependencies: [2],
priority: 'medium',
details: 'Use Express.js for the API',
testStrategy: 'Integration tests with Jest',
subtasks: []
},
{
id: 4,
title: 'Add database layer',
description: 'Implement database models and migrations',
status: 'pending',
dependencies: [1],
priority: 'high',
details: null,
testStrategy: null,
subtasks: []
}
]
};
fs.writeFileSync(testTasksFile, JSON.stringify(testTasks, null, 2));
});
afterEach(() => {
// Clean up test files
if (fs.existsSync(testTasksFile)) {
fs.unlinkSync(testTasksFile);
}
});
test('should update multiple tasks with structured data', async () => {
const result = await updateTasks(
testTasksFile,
2, // Update from task ID 2 onwards
'Switch to microservices architecture with Docker containers'
);
expect(result).toBeDefined();
expect(result).toHaveProperty('updatedTasks');
expect(result).toHaveProperty('telemetryData');
// Read the updated file
const updatedData = JSON.parse(fs.readFileSync(testTasksFile, 'utf8'));
// Task 1 should remain unchanged (status: done)
const task1 = updatedData.tasks.find((t) => t.id === 1);
expect(task1.title).toBe('Setup project structure');
expect(task1.status).toBe('done');
// Tasks 2, 3, and 4 should be updated
const task2 = updatedData.tasks.find((t) => t.id === 2);
expect(task2.description.toLowerCase()).toContain('microservice');
// Completed subtasks should be preserved
expect(
task2.subtasks.find((st) => st.id === 1 && st.status === 'done')
).toBeDefined();
const task3 = updatedData.tasks.find((t) => t.id === 3);
expect(task3.description.toLowerCase()).toContain('docker');
const task4 = updatedData.tasks.find((t) => t.id === 4);
expect(task4.description.toLowerCase()).toMatch(
/microservice|docker|container/
);
}, 30000); // Increase timeout for AI call
test('should preserve completed subtasks when updating', async () => {
await updateTasks(
testTasksFile,
2,
'Add comprehensive error handling and logging'
);
const updatedData = JSON.parse(fs.readFileSync(testTasksFile, 'utf8'));
const task2 = updatedData.tasks.find((t) => t.id === 2);
// Find the completed subtask
const completedSubtask = task2.subtasks.find((st) => st.id === 1);
expect(completedSubtask).toBeDefined();
expect(completedSubtask.status).toBe('done');
expect(completedSubtask.title).toBe('Design auth flow');
expect(completedSubtask.description).toBe(
'Create authentication flow diagrams'
);
}, 30000);
test('should handle no tasks to update', async () => {
const result = await updateTasks(
testTasksFile,
10, // Start from non-existent task ID
'Update all tasks'
);
expect(result).toBeUndefined();
// File should remain unchanged
const data = JSON.parse(fs.readFileSync(testTasksFile, 'utf8'));
expect(data.tasks.length).toBe(4);
}, 30000);
});

View File

@@ -1,230 +0,0 @@
/**
* 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'));
});
});
});
});
});