fix: resolve all remaining test failures and improve test reliability

- Fix clear-subtasks test by implementing deep copy of mock data to prevent mutation issues between tests
- Fix add-task test by uncommenting and properly configuring generateTaskFiles call with correct parameters
- Fix analyze-task-complexity tests by properly mocking fs.writeFileSync with shared mock function
- Update test expectations to match actual function signatures and data structures
- Improve mock setup consistency across all test suites
- Ensure all tests now pass (329 total: 318 passed, 11 skipped, 0 failed)
This commit is contained in:
Eyal Toledano
2025-06-13 22:31:24 -04:00
parent 2e2d290c63
commit d5360f625f
22 changed files with 1119 additions and 7640 deletions

View File

@@ -14,7 +14,42 @@ jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({
temperature: 0.7,
debug: false
},
truncate: jest.fn((text) => text)
sanitizePrompt: jest.fn((prompt) => prompt),
truncate: jest.fn((text) => text),
isSilentMode: jest.fn(() => false),
findTaskById: jest.fn((tasks, id) => {
if (!tasks) return null;
const allTasks = [];
const queue = [...tasks];
while (queue.length > 0) {
const task = queue.shift();
allTasks.push(task);
if (task.subtasks) {
queue.push(...task.subtasks);
}
}
return allTasks.find((task) => String(task.id) === String(id));
}),
getCurrentTag: jest.fn(() => 'master'),
ensureTagMetadata: jest.fn((tagObj) => tagObj),
flattenTasksWithSubtasks: jest.fn((tasks) => {
const allTasks = [];
const queue = [...(tasks || [])];
while (queue.length > 0) {
const task = queue.shift();
allTasks.push(task);
if (task.subtasks) {
for (const subtask of task.subtasks) {
queue.push({ ...subtask, id: `${task.id}.${subtask.id}` });
}
}
}
return allTasks;
}),
markMigrationForNotice: jest.fn(),
performCompleteTagMigration: jest.fn(),
setTasksForTag: jest.fn(),
getTasksForTag: jest.fn((data, tag) => data[tag]?.tasks || [])
}));
jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({
@@ -26,7 +61,8 @@ jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({
failLoadingIndicator: jest.fn(),
warnLoadingIndicator: jest.fn(),
infoLoadingIndicator: jest.fn(),
displayAiUsageSummary: jest.fn()
displayAiUsageSummary: jest.fn(),
displayContextAnalysis: jest.fn()
}));
jest.unstable_mockModule(
@@ -67,6 +103,19 @@ jest.unstable_mockModule(
})
);
jest.unstable_mockModule(
'../../../../../scripts/modules/utils/contextGatherer.js',
() => ({
default: jest.fn().mockImplementation(() => ({
gather: jest.fn().mockResolvedValue({
contextSummary: 'Mock context summary',
allRelatedTaskIds: [],
graphVisualization: 'Mock graph'
})
}))
})
);
jest.unstable_mockModule(
'../../../../../scripts/modules/task-manager/generate-task-files.js',
() => ({
@@ -110,9 +159,11 @@ const { generateObjectService } = await import(
'../../../../../scripts/modules/ai-services-unified.js'
);
const generateTaskFiles = await import(
'../../../../../scripts/modules/task-manager/generate-task-files.js'
);
const generateTaskFiles = (
await import(
'../../../../../scripts/modules/task-manager/generate-task-files.js'
)
).default;
// Import the module under test
const { default: addTask } = await import(
@@ -121,29 +172,31 @@ const { default: addTask } = await import(
describe('addTask', () => {
const sampleTasks = {
tasks: [
{
id: 1,
title: 'Task 1',
description: 'First task',
status: 'pending',
dependencies: []
},
{
id: 2,
title: 'Task 2',
description: 'Second task',
status: 'pending',
dependencies: []
},
{
id: 3,
title: 'Task 3',
description: 'Third task',
status: 'pending',
dependencies: [1]
}
]
master: {
tasks: [
{
id: 1,
title: 'Task 1',
description: 'First task',
status: 'pending',
dependencies: []
},
{
id: 2,
title: 'Task 2',
description: 'Second task',
status: 'pending',
dependencies: []
},
{
id: 3,
title: 'Task 3',
description: 'Third task',
status: 'pending',
dependencies: [1]
}
]
}
};
// Create a helper function for consistent mcpLog mock
@@ -171,7 +224,8 @@ describe('addTask', () => {
// Arrange
const prompt = 'Create a new authentication system';
const context = {
mcpLog: createMcpLogMock()
mcpLog: createMcpLogMock(),
projectRoot: '/mock/project/root'
};
// Act
@@ -185,23 +239,28 @@ describe('addTask', () => {
);
// Assert
expect(readJSON).toHaveBeenCalledWith('tasks/tasks.json');
expect(readJSON).toHaveBeenCalledWith(
'tasks/tasks.json',
'/mock/project/root'
);
expect(generateObjectService).toHaveBeenCalledWith(expect.any(Object));
expect(writeJSON).toHaveBeenCalledWith(
'tasks/tasks.json',
expect.objectContaining({
tasks: expect.arrayContaining([
expect.objectContaining({
id: 4, // Next ID after existing tasks
title: expect.stringContaining(
'Create a new authentication system'
),
status: 'pending'
})
])
master: expect.objectContaining({
tasks: expect.arrayContaining([
expect.objectContaining({
id: 4, // Next ID after existing tasks
title: expect.stringContaining(
'Create a new authentication system'
),
status: 'pending'
})
])
})
})
);
expect(generateTaskFiles.default).toHaveBeenCalled();
expect(generateTaskFiles).toHaveBeenCalled();
expect(result).toEqual(
expect.objectContaining({
newTaskId: 4,
@@ -215,7 +274,8 @@ describe('addTask', () => {
const prompt = 'Create a new authentication system';
const validDependencies = [1, 2]; // These exist in sampleTasks
const context = {
mcpLog: createMcpLogMock()
mcpLog: createMcpLogMock(),
projectRoot: '/mock/project/root'
};
// Act
@@ -232,12 +292,14 @@ describe('addTask', () => {
expect(writeJSON).toHaveBeenCalledWith(
'tasks/tasks.json',
expect.objectContaining({
tasks: expect.arrayContaining([
expect.objectContaining({
id: 4,
dependencies: validDependencies
})
])
master: expect.objectContaining({
tasks: expect.arrayContaining([
expect.objectContaining({
id: 4,
dependencies: validDependencies
})
])
})
})
);
});
@@ -246,7 +308,10 @@ describe('addTask', () => {
// Arrange
const prompt = 'Create a new authentication system';
const invalidDependencies = [999]; // Non-existent task ID
const context = { mcpLog: createMcpLogMock() };
const context = {
mcpLog: createMcpLogMock(),
projectRoot: '/mock/project/root'
};
// Act
const result = await addTask(
@@ -262,12 +327,14 @@ describe('addTask', () => {
expect(writeJSON).toHaveBeenCalledWith(
'tasks/tasks.json',
expect.objectContaining({
tasks: expect.arrayContaining([
expect.objectContaining({
id: 4,
dependencies: [] // Invalid dependencies should be filtered out
})
])
master: expect.objectContaining({
tasks: expect.arrayContaining([
expect.objectContaining({
id: 4,
dependencies: [] // Invalid dependencies should be filtered out
})
])
})
})
);
expect(context.mcpLog.warn).toHaveBeenCalledWith(
@@ -282,7 +349,8 @@ describe('addTask', () => {
const prompt = 'Create a new authentication system';
const priority = 'high';
const context = {
mcpLog: createMcpLogMock()
mcpLog: createMcpLogMock(),
projectRoot: '/mock/project/root'
};
// Act
@@ -292,21 +360,24 @@ describe('addTask', () => {
expect(writeJSON).toHaveBeenCalledWith(
'tasks/tasks.json',
expect.objectContaining({
tasks: expect.arrayContaining([
expect.objectContaining({
priority: priority
})
])
master: expect.objectContaining({
tasks: expect.arrayContaining([
expect.objectContaining({
priority: priority
})
])
})
})
);
});
test('should handle empty tasks file', async () => {
// Arrange
readJSON.mockReturnValue({ tasks: [] });
readJSON.mockReturnValue({ master: { tasks: [] } });
const prompt = 'Create a new authentication system';
const context = {
mcpLog: createMcpLogMock()
mcpLog: createMcpLogMock(),
projectRoot: '/mock/project/root'
};
// Act
@@ -324,11 +395,13 @@ describe('addTask', () => {
expect(writeJSON).toHaveBeenCalledWith(
'tasks/tasks.json',
expect.objectContaining({
tasks: expect.arrayContaining([
expect.objectContaining({
id: 1
})
])
master: expect.objectContaining({
tasks: expect.arrayContaining([
expect.objectContaining({
id: 1
})
])
})
})
);
});
@@ -338,7 +411,8 @@ describe('addTask', () => {
readJSON.mockReturnValue(null);
const prompt = 'Create a new authentication system';
const context = {
mcpLog: createMcpLogMock()
mcpLog: createMcpLogMock(),
projectRoot: '/mock/project/root'
};
// Act
@@ -353,7 +427,7 @@ describe('addTask', () => {
// Assert
expect(result.newTaskId).toBe(1); // First task should have ID 1
expect(writeJSON).toHaveBeenCalledTimes(2); // Once to create file, once to add task
expect(writeJSON).toHaveBeenCalledTimes(1); // Should create file and add task in one go.
});
test('should handle AI service errors', async () => {
@@ -361,7 +435,8 @@ describe('addTask', () => {
generateObjectService.mockRejectedValueOnce(new Error('AI service failed'));
const prompt = 'Create a new authentication system';
const context = {
mcpLog: createMcpLogMock()
mcpLog: createMcpLogMock(),
projectRoot: '/mock/project/root'
};
// Act & Assert
@@ -377,7 +452,8 @@ describe('addTask', () => {
});
const prompt = 'Create a new authentication system';
const context = {
mcpLog: createMcpLogMock()
mcpLog: createMcpLogMock(),
projectRoot: '/mock/project/root'
};
// Act & Assert
@@ -393,7 +469,8 @@ describe('addTask', () => {
});
const prompt = 'Create a new authentication system';
const context = {
mcpLog: createMcpLogMock()
mcpLog: createMcpLogMock(),
projectRoot: '/mock/project/root'
};
// Act & Assert