mirror of
https://github.com/eyaltoledano/claude-task-master.git
synced 2025-11-23 05:27:09 +00:00
* feat: enhance move command with cross-tag functionality - Updated the `move` command to allow moving tasks between different tags, including options for handling dependencies. - Added new options: `--from-tag`, `--to-tag`, `--with-dependencies`, `--ignore-dependencies`, and `--force`. - Implemented validation for cross-tag moves and dependency checks. - Introduced helper functions in the dependency manager for validating and resolving cross-tag dependencies. - Added integration and unit tests to cover new functionality and edge cases. * fix: refactor cross-tag move logic and enhance validation - Moved the import of `moveTasksBetweenTags` to the correct location in `commands.js` for better clarity. - Added new helper functions in `dependency-manager.js` to improve validation and error handling for cross-tag moves. - Enhanced existing functions to ensure proper handling of task dependencies and conflicts. - Updated tests to cover new validation scenarios and ensure robust error messaging for invalid task IDs and tags. * fix: improve task ID handling and error messaging in cross-tag moves - Refactored `moveTasksBetweenTags` to normalize task IDs for comparison, ensuring consistent handling of string and numeric IDs. - Enhanced error messages for cases where source and target tags are the same but no destination is specified. - Updated tests to validate new behavior, including handling string dependencies correctly during cross-tag moves. - Cleaned up existing code for better readability and maintainability. * test: add comprehensive tests for cross-tag move and dependency validation - Introduced new test files for `move-cross-tag` and `cross-tag-dependencies` to cover various scenarios in cross-tag task movement. - Implemented tests for handling task movement with and without dependencies, including edge cases for error handling. - Enhanced existing tests in `fix-dependencies-command` and `move-task` to ensure robust validation of task IDs and dependencies. - Mocked necessary modules and functions to isolate tests and improve reliability. - Ensured coverage for both successful and failed cross-tag move operations, validating expected outcomes and error messages. * test: refactor cross-tag move tests for better clarity and reusability - Introduced a helper function `simulateCrossTagMove` to streamline cross-tag move test cases, reducing redundancy and improving readability. - Updated existing tests to utilize the new helper function, ensuring consistent handling of expected messages and options. - Enhanced test coverage for various scenarios, including handling of dependencies and flags. * feat: add cross-tag task movement functionality - Introduced new commands for moving tasks between different tags, enhancing project organization capabilities. - Updated README with usage examples for cross-tag movement, including options for handling dependencies. - Created comprehensive documentation for cross-tag task movement, detailing usage, error handling, and best practices. - Implemented core logic for cross-tag moves, including validation for dependencies and error handling. - Added integration and unit tests to ensure robust functionality and coverage for various scenarios, including edge cases. * fix: enhance error handling and logging in cross-tag task movement - Improved logging in `moveTaskCrossTagDirect` to include detailed arguments for better traceability. - Refactored error handling to utilize structured error objects, providing clearer suggestions for resolving cross-tag dependency conflicts and subtask movement restrictions. - Updated documentation to reflect changes in error handling and provide clearer guidance on task movement options. - Added integration tests for cross-tag movement scenarios, ensuring robust validation of error handling and task movement logic. - Cleaned up existing tests for clarity and reusability, enhancing overall test coverage. * feat: enhance dependency resolution and error handling in task movement - Added recursive dependency resolution for tasks in `moveTasksBetweenTags`, improving handling of complex task relationships. - Introduced helper functions to find all dependencies and reverse dependencies, ensuring comprehensive coverage during task moves. - Enhanced error messages in `validateSubtaskMove` and `displaySubtaskMoveError` for better clarity on movement restrictions. - Updated tests to cover new functionality, including integration tests for complex cross-tag movement scenarios and edge cases. - Refactored existing code for improved readability and maintainability, ensuring consistent handling of task IDs and dependencies. * feat: unify dependency traversal and enhance task management utilities - Introduced `traverseDependencies` utility for unified forward and reverse dependency traversal, improving code reusability and clarity. - Refactored `findAllDependenciesRecursively` to leverage the new utility, streamlining dependency resolution in task management. - Added `formatTaskIdForDisplay` helper for better task ID formatting in UI, enhancing user experience during error displays. - Updated tests to cover new utility functions and ensure robust validation of dependency handling across various scenarios. - Improved overall code organization and readability, ensuring consistent handling of task dependencies and IDs. * fix: improve validation for dependency parameters in `findAllDependenciesRecursively` - Added checks to ensure `sourceTasks` and `allTasks` are arrays, throwing errors if not, to prevent runtime issues. - Updated documentation comment for clarity on the function's purpose and parameters. * fix: remove `force` option from task movement parameters - Eliminated the `force` parameter from the `moveTaskCrossTagDirect` function and related tools, simplifying the task movement logic. - Updated documentation and tests to reflect the removal of the `force` option, ensuring clarity and consistency across the codebase. - Adjusted related functions and tests to focus on `ignoreDependencies` as the primary control for handling dependency conflicts during task moves. * Add cross-tag task movement functionality - Introduced functionality for organizing tasks across different contexts by enabling cross-tag movement. - Added `formatTaskIdForDisplay` helper to improve task ID formatting in UI error messages. - Updated relevant tests to incorporate new functionality and ensure accurate error displays during task movements. * Update scripts/modules/dependency-manager.js Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * refactor(dependency-manager): Fix subtask resolution and extract helper functions 1. Fix subtask finding logic (lines 1315-1330): - Correctly locate parent task by numeric ID - Search within parent's subtasks array instead of top-level tasks - Properly handle relative subtask references 2. Extract helper functions from getDependentTaskIds (lines 1440-1636): - Move findTasksThatDependOn as module-level function - Move taskDependsOnSource as module-level function - Move subtasksDependOnSource as module-level function - Improves readability, maintainability, and testability Both fixes address architectural issues and improve code organization. * refactor(dependency-manager): Enhance subtask resolution and dependency validation - Improved subtask resolution logic to correctly find parent tasks and their subtasks, ensuring accurate identification of dependencies. - Filtered out null/undefined dependencies before processing, enhancing robustness in dependency checks. - Updated comments for clarity on the logic flow and purpose of changes, improving code maintainability. * refactor(move-task): clarify destination ID description and improve skipped task handling - Updated the description for the destination ID to clarify its usage in cross-tag moves. - Simplified the handling of skipped tasks during multiple task movements, improving readability and logging. - Enhanced the API result response to include detailed information about moved and skipped tasks, ensuring better feedback for users. * refactor(commands): remove redundant tag validation logic - Eliminated the check for identical source and target tags in the task movement logic, simplifying the code. - This change streamlines the flow for within-tag moves, enhancing readability and maintainability. * refactor(commands): enhance move command logic and error handling - Introduced helper functions for better organization of cross-tag and within-tag move logic, improving code readability and maintainability. - Enhanced error handling with structured error objects, providing clearer feedback for dependency conflicts and invalid tag combinations. - Updated move command help output to include best practices and error resolution tips, ensuring users have comprehensive guidance during task movements. - Streamlined task movement logic to handle multiple tasks more effectively, including detailed logging of successful and failed moves. * test(dependency-manager): add subtasks to task structure and mock dependency traversal - Updated `circular-dependencies.test.js` to include subtasks in task definitions, enhancing test coverage for task structures with nested dependencies. - Mocked `traverseDependencies` in `fix-dependencies-command.test.js` to ensure consistent behavior during tests, improving reliability of dependency-related tests. * refactor(dependency-manager): extract subtask finding logic into helper function - Added `findSubtaskInParent` function to encapsulate subtask resolution within a parent task's subtasks array, improving code organization and readability. - Updated `findDependencyTask` to utilize the new helper function, streamlining the logic for finding subtasks and enhancing maintainability. - Enhanced comments for clarity on the purpose and functionality of the new subtask finding logic. * refactor(ui): enhance subtask ID validation and improve error handling - Added validation for subtask ID format in `formatDependenciesWithStatus` and `taskExists`, ensuring proper handling of invalid formats. - Updated error logging in `displaySubtaskMoveError` to provide warnings for unexpected task ID formats, improving user feedback. - Converted hints to a Set in `displayDependencyValidationHints` to ensure unique hints are displayed, enhancing clarity in the UI. * test(cli): remove redundant timing check in complex cross-tag scenarios - Eliminated the timing check for task completion within 5 seconds in `complex-cross-tag-scenarios.test.js`, streamlining the test logic. - This change focuses on verifying task success without unnecessary timing constraints, enhancing test clarity and maintainability. * test(integration): enhance task movement tests with mock file system - Added integration tests for moving tasks within the same tag and between different tags using the actual `moveTask` and `moveTasksBetweenTags` functions. - Implemented `mock-fs` to simulate file system interactions, improving test isolation and reliability. - Verified task movement success and ensured proper handling of subtasks and dependencies, enhancing overall test coverage for task management functionality. - Included error handling tests for missing tags and task IDs to ensure robustness in task movement operations. * test(unit): add comprehensive tests for moveTaskCrossTagDirect functionality - Introduced new test cases to verify mock functionality, ensuring that mocks for `findTasksPath` and `readJSON` are working as expected. - Added tests for parameter validation, error handling, and function call flow, including scenarios for missing project roots and identical source/target tags. - Enhanced coverage for ID parsing and move options, ensuring robust handling of various input conditions and improving overall test reliability. * test(integration): skip tests for dependency conflict handling and withDependencies option - Marked tests for handling dependency conflicts and the withDependencies option as skipped due to issues with the mock setup. - Added TODOs to address the mock-fs setup for complex dependency scenarios, ensuring future improvements in test reliability. * test(unit): expand cross-tag move command tests with comprehensive mocks - Added extensive mocks for various modules to enhance the testing of the cross-tag move functionality in `move-cross-tag.test.js`. - Implemented detailed test cases for handling cross-tag moves, including validation for missing parameters and identical source/target tags. - Improved error handling tests to ensure robust feedback for invalid operations, enhancing overall test reliability and coverage. * test(integration): add complex dependency scenarios to task movement tests - Introduced new integration tests for handling complex dependency scenarios in task movement, utilizing the actual `moveTasksBetweenTags` function. - Added tests for circular dependencies, nested dependency chains, and cross-tag dependency resolution, enhancing coverage and reliability. - Documented limitations of the mock-fs setup for complex scenarios and provided warnings in the test output to guide future improvements. - Skipped tests for dependency conflicts and the withDependencies option due to mock setup issues, with TODOs for resolution. * test(unit): refactor move-cross-tag tests with focused mock system - Simplified mocking in `move-cross-tag.test.js` by implementing a configuration-driven mock system, reducing the number of mocked modules from 20+ to 5 core functionalities. - Introduced a reusable mock factory to streamline the creation of mocks based on configuration, enhancing maintainability and clarity. - Added documentation for the new mock system, detailing usage examples and benefits, including reduced complexity and improved test focus. - Implemented tests to validate the mock configuration, ensuring flexibility in enabling/disabling specific mocks. * test(unit): clean up mocks and improve isEmpty function in fix-dependencies-command tests - Removed the mock for `traverseDependencies` as it was unnecessary, simplifying the test setup. - Updated the `isEmpty` function to clarify its behavior regarding null and undefined values, enhancing code readability and maintainability. * test(unit): update traverseDependencies mock for consistency across tests - Standardized the mock implementation of `traverseDependencies` in both `fix-dependencies-command.test.js` and `complexity-report-tag-isolation.test.js` to accept `sourceTasks`, `allTasks`, and `options` parameters, ensuring uniformity in test setups. - This change enhances clarity and maintainability of the tests by aligning the mock behavior across different test files. * fix(core): improve task movement error handling and ID normalization - Wrapped task movement logic in a try-finally block to ensure console output is restored even on errors, enhancing reliability. - Normalized source IDs to handle mixed string/number comparisons, preventing potential issues in dependency checks. - Added tests for ID type consistency to verify that the normalization fix works correctly across various scenarios, improving test coverage and robustness. * refactor(task-manager): restructure task movement logic for improved validation and execution - Renamed and refactored `moveTasksBetweenTags` to streamline the task movement process into distinct phases: validation, data preparation, dependency resolution, execution, and finalization. - Introduced `validateMove`, `prepareTaskData`, `resolveDependencies`, `executeMoveOperation`, and `finalizeMove` functions to enhance modularity and clarity. - Updated documentation comments to reflect changes in function responsibilities and parameters. - Added comprehensive unit tests for the new structure, ensuring robust validation and error handling across various scenarios. - Improved handling of dependencies and task existence checks during the move operation, enhancing overall reliability. * fix(move-task): streamline task movement logic and improve error handling - Refactored the task movement process to enhance clarity and maintainability by replacing `forEach` with a `for...of` loop for better async handling. - Consolidated error handling and result logging to ensure consistent feedback during task moves. - Updated the logic for generating files only on the last move, improving performance and reducing unnecessary operations. - Enhanced validation for skipped tasks, ensuring accurate reporting of moved and skipped tasks in the final result. * fix(docs): update error message formatting and enhance clarity in task movement documentation - Changed code block syntax from generic to `text` for better readability in error messages related to task movement and dependency conflicts. - Ensured consistent formatting across all error message examples to improve user understanding of task movement restrictions and resolutions. - Added a newline at the end of the file for proper formatting. * Update .changeset/crazy-meals-hope.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * chore: improve changeset * chore: improve changeset * fix referenced bug in docs and remove docs * chore: fix format --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com>
464 lines
12 KiB
JavaScript
464 lines
12 KiB
JavaScript
/**
|
|
* Tests for the analyze-task-complexity.js module
|
|
*/
|
|
import { jest } from '@jest/globals';
|
|
import {
|
|
createGetTagAwareFilePathMock,
|
|
createSlugifyTagForFilePathMock
|
|
} from './setup.js';
|
|
|
|
// Mock the dependencies before importing the module under test
|
|
jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({
|
|
readJSON: jest.fn(),
|
|
writeJSON: jest.fn(),
|
|
log: jest.fn(),
|
|
CONFIG: {
|
|
model: 'mock-claude-model',
|
|
maxTokens: 4000,
|
|
temperature: 0.7,
|
|
debug: false,
|
|
defaultSubtasks: 3
|
|
},
|
|
findTaskById: jest.fn(),
|
|
readComplexityReport: jest.fn(),
|
|
findTaskInComplexityReport: jest.fn(),
|
|
findProjectRoot: jest.fn(() => '/mock/project/root'),
|
|
resolveEnvVariable: jest.fn((varName) => `mock_${varName}`),
|
|
isSilentMode: jest.fn(() => false),
|
|
findCycles: jest.fn(() => []),
|
|
formatTaskId: jest.fn((id) => `Task ${id}`),
|
|
taskExists: jest.fn((tasks, id) => tasks.some((t) => t.id === id)),
|
|
enableSilentMode: jest.fn(),
|
|
disableSilentMode: jest.fn(),
|
|
truncate: jest.fn((text) => text),
|
|
addComplexityToTask: jest.fn((task, complexity) => ({ ...task, complexity })),
|
|
aggregateTelemetry: jest.fn((telemetryArray) => telemetryArray[0] || {}),
|
|
ensureTagMetadata: jest.fn((tagObj) => tagObj),
|
|
getCurrentTag: jest.fn(() => 'master'),
|
|
flattenTasksWithSubtasks: jest.fn((tasks) => tasks),
|
|
getTagAwareFilePath: createGetTagAwareFilePathMock(),
|
|
slugifyTagForFilePath: createSlugifyTagForFilePathMock(),
|
|
markMigrationForNotice: jest.fn(),
|
|
performCompleteTagMigration: jest.fn(),
|
|
setTasksForTag: jest.fn(),
|
|
getTasksForTag: jest.fn((data, tag) => data[tag]?.tasks || []),
|
|
traverseDependencies: jest.fn((tasks, taskId, visited) => [])
|
|
}));
|
|
|
|
jest.unstable_mockModule(
|
|
'../../../../../scripts/modules/ai-services-unified.js',
|
|
() => ({
|
|
generateObjectService: jest.fn().mockResolvedValue({
|
|
mainResult: {
|
|
tasks: []
|
|
},
|
|
telemetryData: {
|
|
timestamp: new Date().toISOString(),
|
|
userId: '1234567890',
|
|
commandName: 'analyze-complexity',
|
|
modelUsed: 'claude-3-5-sonnet',
|
|
providerName: 'anthropic',
|
|
inputTokens: 1000,
|
|
outputTokens: 500,
|
|
totalTokens: 1500,
|
|
totalCost: 0.012414,
|
|
currency: 'USD'
|
|
}
|
|
}),
|
|
generateTextService: jest.fn().mockResolvedValue({
|
|
mainResult: '[]',
|
|
telemetryData: {
|
|
timestamp: new Date().toISOString(),
|
|
userId: '1234567890',
|
|
commandName: 'analyze-complexity',
|
|
modelUsed: 'claude-3-5-sonnet',
|
|
providerName: 'anthropic',
|
|
inputTokens: 1000,
|
|
outputTokens: 500,
|
|
totalTokens: 1500,
|
|
totalCost: 0.012414,
|
|
currency: 'USD'
|
|
}
|
|
})
|
|
})
|
|
);
|
|
|
|
jest.unstable_mockModule(
|
|
'../../../../../scripts/modules/config-manager.js',
|
|
() => ({
|
|
// Core config access
|
|
getConfig: jest.fn(() => ({
|
|
models: { main: { provider: 'anthropic', modelId: 'claude-3-5-sonnet' } },
|
|
global: { projectName: 'Test Project' }
|
|
})),
|
|
writeConfig: jest.fn(() => true),
|
|
ConfigurationError: class extends Error {},
|
|
isConfigFilePresent: jest.fn(() => true),
|
|
|
|
// Validation
|
|
validateProvider: jest.fn(() => true),
|
|
validateProviderModelCombination: jest.fn(() => true),
|
|
VALID_PROVIDERS: ['anthropic', 'openai', 'perplexity'],
|
|
MODEL_MAP: {
|
|
anthropic: [
|
|
{
|
|
id: 'claude-3-5-sonnet',
|
|
cost_per_1m_tokens: { input: 3, output: 15 }
|
|
}
|
|
],
|
|
openai: [{ id: 'gpt-4', cost_per_1m_tokens: { input: 30, output: 60 } }]
|
|
},
|
|
getAvailableModels: jest.fn(() => [
|
|
{
|
|
id: 'claude-3-5-sonnet',
|
|
name: 'Claude 3.5 Sonnet',
|
|
provider: 'anthropic'
|
|
},
|
|
{ id: 'gpt-4', name: 'GPT-4', provider: 'openai' }
|
|
]),
|
|
|
|
// Role-specific getters
|
|
getMainProvider: jest.fn(() => 'anthropic'),
|
|
getMainModelId: jest.fn(() => 'claude-3-5-sonnet'),
|
|
getMainMaxTokens: jest.fn(() => 4000),
|
|
getMainTemperature: jest.fn(() => 0.7),
|
|
getResearchProvider: jest.fn(() => 'perplexity'),
|
|
getResearchModelId: jest.fn(() => 'sonar-pro'),
|
|
getResearchMaxTokens: jest.fn(() => 8700),
|
|
getResearchTemperature: jest.fn(() => 0.1),
|
|
getFallbackProvider: jest.fn(() => 'anthropic'),
|
|
getFallbackModelId: jest.fn(() => 'claude-3-5-sonnet'),
|
|
getFallbackMaxTokens: jest.fn(() => 4000),
|
|
getFallbackTemperature: jest.fn(() => 0.7),
|
|
getBaseUrlForRole: jest.fn(() => undefined),
|
|
|
|
// Global setting getters
|
|
getLogLevel: jest.fn(() => 'info'),
|
|
getDebugFlag: jest.fn(() => false),
|
|
getDefaultNumTasks: jest.fn(() => 10),
|
|
getDefaultSubtasks: jest.fn(() => 5),
|
|
getDefaultPriority: jest.fn(() => 'medium'),
|
|
getProjectName: jest.fn(() => 'Test Project'),
|
|
getOllamaBaseURL: jest.fn(() => 'http://localhost:11434/api'),
|
|
getAzureBaseURL: jest.fn(() => undefined),
|
|
getBedrockBaseURL: jest.fn(() => undefined),
|
|
getParametersForRole: jest.fn(() => ({
|
|
maxTokens: 4000,
|
|
temperature: 0.7
|
|
})),
|
|
getUserId: jest.fn(() => '1234567890'),
|
|
|
|
// API Key Checkers
|
|
isApiKeySet: jest.fn(() => true),
|
|
getMcpApiKeyStatus: jest.fn(() => true),
|
|
|
|
// Additional functions
|
|
getAllProviders: jest.fn(() => ['anthropic', 'openai', 'perplexity']),
|
|
getVertexProjectId: jest.fn(() => undefined),
|
|
getVertexLocation: jest.fn(() => undefined)
|
|
})
|
|
);
|
|
|
|
// Mock fs module
|
|
const mockWriteFileSync = jest.fn();
|
|
jest.unstable_mockModule('fs', () => ({
|
|
default: {
|
|
existsSync: jest.fn(() => false),
|
|
readFileSync: jest.fn(),
|
|
writeFileSync: mockWriteFileSync
|
|
},
|
|
existsSync: jest.fn(() => false),
|
|
readFileSync: jest.fn(),
|
|
writeFileSync: mockWriteFileSync
|
|
}));
|
|
|
|
jest.unstable_mockModule(
|
|
'../../../../../scripts/modules/prompt-manager.js',
|
|
() => ({
|
|
getPromptManager: jest.fn().mockReturnValue({
|
|
loadPrompt: jest.fn().mockResolvedValue({
|
|
systemPrompt: 'Mocked system prompt',
|
|
userPrompt: 'Mocked user prompt'
|
|
})
|
|
})
|
|
})
|
|
);
|
|
|
|
// Import the mocked modules
|
|
const { readJSON, writeJSON, log, CONFIG, findTaskById } = await import(
|
|
'../../../../../scripts/modules/utils.js'
|
|
);
|
|
|
|
const { generateObjectService, generateTextService } = await import(
|
|
'../../../../../scripts/modules/ai-services-unified.js'
|
|
);
|
|
|
|
const fs = await import('fs');
|
|
|
|
// Import the module under test
|
|
const { default: analyzeTaskComplexity } = await import(
|
|
'../../../../../scripts/modules/task-manager/analyze-task-complexity.js'
|
|
);
|
|
|
|
describe('analyzeTaskComplexity', () => {
|
|
// Sample response structure (simplified for these tests)
|
|
const sampleApiResponse = {
|
|
mainResult: JSON.stringify({
|
|
tasks: [
|
|
{ id: 1, complexity: 3, subtaskCount: 2 },
|
|
{ id: 2, complexity: 7, subtaskCount: 5 },
|
|
{ id: 3, complexity: 9, subtaskCount: 8 }
|
|
]
|
|
}),
|
|
telemetryData: {
|
|
timestamp: new Date().toISOString(),
|
|
userId: '1234567890',
|
|
commandName: 'analyze-complexity',
|
|
modelUsed: 'claude-3-5-sonnet',
|
|
providerName: 'anthropic',
|
|
inputTokens: 1000,
|
|
outputTokens: 500,
|
|
totalTokens: 1500,
|
|
totalCost: 0.012414,
|
|
currency: 'USD'
|
|
}
|
|
};
|
|
|
|
const sampleTasks = {
|
|
master: {
|
|
tasks: [
|
|
{
|
|
id: 1,
|
|
title: 'Task 1',
|
|
description: 'First task description',
|
|
status: 'pending',
|
|
dependencies: [],
|
|
priority: 'high'
|
|
},
|
|
{
|
|
id: 2,
|
|
title: 'Task 2',
|
|
description: 'Second task description',
|
|
status: 'pending',
|
|
dependencies: [1],
|
|
priority: 'medium'
|
|
},
|
|
{
|
|
id: 3,
|
|
title: 'Task 3',
|
|
description: 'Third task description',
|
|
status: 'done',
|
|
dependencies: [1, 2],
|
|
priority: 'high'
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
|
|
// Default mock implementations - readJSON should return the resolved view with tasks at top level
|
|
readJSON.mockImplementation((tasksPath, projectRoot, tag) => {
|
|
return {
|
|
...sampleTasks.master,
|
|
tag: tag || 'master',
|
|
_rawTaggedData: sampleTasks
|
|
};
|
|
});
|
|
|
|
// Mock findTaskById to return the expected structure
|
|
findTaskById.mockImplementation((tasks, taskId) => {
|
|
const task = tasks?.find((t) => t.id === parseInt(taskId));
|
|
return { task: task || null, originalSubtaskCount: null };
|
|
});
|
|
|
|
generateTextService.mockResolvedValue(sampleApiResponse);
|
|
});
|
|
|
|
test('should call generateTextService with the correct parameters', async () => {
|
|
// Arrange
|
|
const options = {
|
|
file: 'tasks/tasks.json',
|
|
output: 'scripts/task-complexity-report.json',
|
|
threshold: '5',
|
|
research: false,
|
|
projectRoot: '/mock/project/root'
|
|
};
|
|
|
|
// Act
|
|
await analyzeTaskComplexity(options, {
|
|
projectRoot: '/mock/project/root',
|
|
mcpLog: {
|
|
info: jest.fn(),
|
|
warn: jest.fn(),
|
|
error: jest.fn(),
|
|
debug: jest.fn(),
|
|
success: jest.fn()
|
|
}
|
|
});
|
|
|
|
// Assert
|
|
expect(readJSON).toHaveBeenCalledWith(
|
|
'tasks/tasks.json',
|
|
'/mock/project/root',
|
|
undefined
|
|
);
|
|
expect(generateTextService).toHaveBeenCalledWith(expect.any(Object));
|
|
expect(mockWriteFileSync).toHaveBeenCalledWith(
|
|
expect.stringContaining('task-complexity-report.json'),
|
|
expect.stringContaining('"thresholdScore": 5'),
|
|
'utf8'
|
|
);
|
|
});
|
|
|
|
test('should use research flag to determine which AI service to use', async () => {
|
|
// Arrange
|
|
const researchOptions = {
|
|
file: 'tasks/tasks.json',
|
|
output: 'scripts/task-complexity-report.json',
|
|
threshold: '5',
|
|
research: true,
|
|
projectRoot: '/mock/project/root'
|
|
};
|
|
|
|
// Act
|
|
await analyzeTaskComplexity(researchOptions, {
|
|
projectRoot: '/mock/project/root',
|
|
mcpLog: {
|
|
info: jest.fn(),
|
|
warn: jest.fn(),
|
|
error: jest.fn(),
|
|
debug: jest.fn(),
|
|
success: jest.fn()
|
|
}
|
|
});
|
|
|
|
// Assert
|
|
expect(generateTextService).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
role: 'research' // This should be present when research is true
|
|
})
|
|
);
|
|
});
|
|
|
|
test('should handle different threshold parameter types correctly', async () => {
|
|
// Test with string threshold
|
|
let options = {
|
|
file: 'tasks/tasks.json',
|
|
output: 'scripts/task-complexity-report.json',
|
|
threshold: '7',
|
|
projectRoot: '/mock/project/root'
|
|
};
|
|
|
|
await analyzeTaskComplexity(options, {
|
|
projectRoot: '/mock/project/root',
|
|
mcpLog: {
|
|
info: jest.fn(),
|
|
warn: jest.fn(),
|
|
error: jest.fn(),
|
|
debug: jest.fn(),
|
|
success: jest.fn()
|
|
}
|
|
});
|
|
|
|
expect(mockWriteFileSync).toHaveBeenCalledWith(
|
|
expect.stringContaining('task-complexity-report.json'),
|
|
expect.stringContaining('"thresholdScore": 7'),
|
|
'utf8'
|
|
);
|
|
|
|
// Reset mocks
|
|
jest.clearAllMocks();
|
|
|
|
// Test with number threshold
|
|
options = {
|
|
file: 'tasks/tasks.json',
|
|
output: 'scripts/task-complexity-report.json',
|
|
threshold: 8,
|
|
projectRoot: '/mock/project/root'
|
|
};
|
|
|
|
await analyzeTaskComplexity(options, {
|
|
projectRoot: '/mock/project/root',
|
|
mcpLog: {
|
|
info: jest.fn(),
|
|
warn: jest.fn(),
|
|
error: jest.fn(),
|
|
debug: jest.fn(),
|
|
success: jest.fn()
|
|
}
|
|
});
|
|
|
|
expect(mockWriteFileSync).toHaveBeenCalledWith(
|
|
expect.stringContaining('task-complexity-report.json'),
|
|
expect.stringContaining('"thresholdScore": 8'),
|
|
'utf8'
|
|
);
|
|
});
|
|
|
|
test('should filter out completed tasks from analysis', async () => {
|
|
// Arrange
|
|
const options = {
|
|
file: 'tasks/tasks.json',
|
|
output: 'scripts/task-complexity-report.json',
|
|
threshold: '5',
|
|
projectRoot: '/mock/project/root'
|
|
};
|
|
|
|
// Act
|
|
await analyzeTaskComplexity(options, {
|
|
projectRoot: '/mock/project/root',
|
|
mcpLog: {
|
|
info: jest.fn(),
|
|
warn: jest.fn(),
|
|
error: jest.fn(),
|
|
debug: jest.fn(),
|
|
success: jest.fn()
|
|
}
|
|
});
|
|
|
|
// Assert
|
|
// Check if the prompt sent to AI doesn't include the completed task (id: 3)
|
|
expect(generateTextService).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
prompt: expect.not.stringContaining('"id": 3')
|
|
})
|
|
);
|
|
});
|
|
|
|
test('should handle API errors gracefully', async () => {
|
|
// Arrange
|
|
const options = {
|
|
file: 'tasks/tasks.json',
|
|
output: 'scripts/task-complexity-report.json',
|
|
threshold: '5',
|
|
projectRoot: '/mock/project/root'
|
|
};
|
|
|
|
// Force API error
|
|
generateTextService.mockRejectedValueOnce(new Error('API Error'));
|
|
|
|
const mockMcpLog = {
|
|
info: jest.fn(),
|
|
warn: jest.fn(),
|
|
error: jest.fn(),
|
|
debug: jest.fn(),
|
|
success: jest.fn()
|
|
};
|
|
|
|
// Act & Assert
|
|
await expect(
|
|
analyzeTaskComplexity(options, {
|
|
projectRoot: '/mock/project/root',
|
|
mcpLog: mockMcpLog
|
|
})
|
|
).rejects.toThrow('API Error');
|
|
|
|
// Check that the error was logged via mcpLog
|
|
expect(mockMcpLog.error).toHaveBeenCalledWith(
|
|
expect.stringContaining('API Error')
|
|
);
|
|
});
|
|
});
|