This commit introduces significant enhancements and refactoring to the Task Master CLI, focusing on improved testing, integration with Perplexity AI for research-backed task updates, and core logic refactoring for better maintainability and functionality.
**Testing Infrastructure Setup:**
- Implemented Jest as the primary testing framework, setting up a comprehensive testing environment.
- Added new test scripts to including , , and for streamlined testing workflows.
- Integrated necessary devDependencies for testing, such as , , , , and , to support unit, integration, and end-to-end testing.
**Dependency Updates:**
- Updated and to reflect the latest dependency versions, ensuring project stability and access to the newest features and security patches.
- Upgraded to version 0.9.16 and usage: openai [-h] [-v] [-b API_BASE] [-k API_KEY] [-p PROXY [PROXY ...]]
[-o ORGANIZATION] [-t {openai,azure}]
[--api-version API_VERSION] [--azure-endpoint AZURE_ENDPOINT]
[--azure-ad-token AZURE_AD_TOKEN] [-V]
{api,tools,migrate,grit} ...
positional arguments:
{api,tools,migrate,grit}
api Direct API calls
tools Client side tools for convenience
options:
-h, --help show this help message and exit
-v, --verbose Set verbosity.
-b, --api-base API_BASE
What API base url to use.
-k, --api-key API_KEY
What API key to use.
-p, --proxy PROXY [PROXY ...]
What proxy to use.
-o, --organization ORGANIZATION
Which organization to run as (will use your default
organization if not specified)
-t, --api-type {openai,azure}
The backend API to call, must be `openai` or `azure`
--api-version API_VERSION
The Azure API version, e.g.
'https://learn.microsoft.com/en-us/azure/ai-
services/openai/reference#rest-api-versioning'
--azure-endpoint AZURE_ENDPOINT
The Azure endpoint, e.g.
'https://endpoint.openai.azure.com'
--azure-ad-token AZURE_AD_TOKEN
A token from Azure Active Directory,
https://www.microsoft.com/en-
us/security/business/identity-access/microsoft-entra-
id
-V, --version show program's version number and exit to 4.89.0.
- Added dependency (version 2.3.0) and updated related dependencies to their latest versions.
**Perplexity AI Integration for Research-Backed Updates:**
- Introduced an option to leverage Perplexity AI for task updates, enabling research-backed enhancements to task details.
- Implemented logic to initialize a Perplexity AI client if the environment variable is available.
- Modified the function to accept a parameter, allowing dynamic selection between Perplexity AI and Claude AI for task updates based on API key availability and user preference.
- Enhanced to handle responses from Perplexity AI and update tasks accordingly, including improved error handling and logging for robust operation.
**Core Logic Refactoring and Improvements:**
- Refactored the function to utilize task IDs instead of dependency IDs, ensuring consistency and clarity in dependency management.
- Implemented a new function to rigorously check for both circular dependencies and self-dependencies within tasks, improving task relationship integrity.
- Enhanced UI elements in :
- Refactored to incorporate icons for different task statuses and utilize a object for color mapping, improving visual representation of task status.
- Updated to display colored complexity scores with emojis, providing a more intuitive and visually appealing representation of task complexity.
- Refactored the task data structure creation and validation process:
- Updated the JSON Schema for to reflect a more streamlined and efficient task structure.
- Implemented Task Model Classes for better data modeling and type safety.
- Improved File System Operations for task data management.
- Developed robust Validation Functions and an Error Handling System to ensure data integrity and application stability.
**Testing Guidelines Implementation:**
- Implemented guidelines for writing testable code when developing new features, promoting a test-driven development approach.
- Added testing requirements and best practices for unit, integration, and edge case testing to ensure comprehensive test coverage.
- Updated the development workflow to mandate writing tests before proceeding with configuration and documentation updates, reinforcing the importance of testing throughout the development lifecycle.
This commit collectively enhances the Task Master CLI's reliability, functionality, and developer experience through improved testing practices, AI-powered research capabilities, and a more robust and maintainable codebase.
288 lines
7.4 KiB
JavaScript
288 lines
7.4 KiB
JavaScript
/**
|
|
* AI Services module tests
|
|
*/
|
|
|
|
import { jest } from '@jest/globals';
|
|
import { parseSubtasksFromText } from '../../scripts/modules/ai-services.js';
|
|
|
|
// Create a mock log function we can check later
|
|
const mockLog = jest.fn();
|
|
|
|
// Mock dependencies
|
|
jest.mock('@anthropic-ai/sdk', () => {
|
|
return {
|
|
Anthropic: jest.fn().mockImplementation(() => ({
|
|
messages: {
|
|
create: jest.fn().mockResolvedValue({
|
|
content: [{ text: 'AI response' }],
|
|
}),
|
|
},
|
|
})),
|
|
};
|
|
});
|
|
|
|
// Use jest.fn() directly for OpenAI mock
|
|
const mockOpenAIInstance = {
|
|
chat: {
|
|
completions: {
|
|
create: jest.fn().mockResolvedValue({
|
|
choices: [{ message: { content: 'Perplexity response' } }],
|
|
}),
|
|
},
|
|
},
|
|
};
|
|
const mockOpenAI = jest.fn().mockImplementation(() => mockOpenAIInstance);
|
|
|
|
jest.mock('openai', () => {
|
|
return { default: mockOpenAI };
|
|
});
|
|
|
|
jest.mock('dotenv', () => ({
|
|
config: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('../../scripts/modules/utils.js', () => ({
|
|
CONFIG: {
|
|
model: 'claude-3-sonnet-20240229',
|
|
temperature: 0.7,
|
|
maxTokens: 4000,
|
|
},
|
|
log: mockLog,
|
|
sanitizePrompt: jest.fn(text => text),
|
|
}));
|
|
|
|
jest.mock('../../scripts/modules/ui.js', () => ({
|
|
startLoadingIndicator: jest.fn().mockReturnValue('mockLoader'),
|
|
stopLoadingIndicator: jest.fn(),
|
|
}));
|
|
|
|
// Mock anthropic global object
|
|
global.anthropic = {
|
|
messages: {
|
|
create: jest.fn().mockResolvedValue({
|
|
content: [{ text: '[{"id": 1, "title": "Test", "description": "Test", "dependencies": [], "details": "Test"}]' }],
|
|
}),
|
|
},
|
|
};
|
|
|
|
// Mock process.env
|
|
const originalEnv = process.env;
|
|
|
|
describe('AI Services Module', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
process.env = { ...originalEnv };
|
|
process.env.ANTHROPIC_API_KEY = 'test-anthropic-key';
|
|
process.env.PERPLEXITY_API_KEY = 'test-perplexity-key';
|
|
});
|
|
|
|
afterEach(() => {
|
|
process.env = originalEnv;
|
|
});
|
|
|
|
describe('parseSubtasksFromText function', () => {
|
|
test('should parse subtasks from JSON text', () => {
|
|
const text = `Here's your list of subtasks:
|
|
|
|
[
|
|
{
|
|
"id": 1,
|
|
"title": "Implement database schema",
|
|
"description": "Design and implement the database schema for user data",
|
|
"dependencies": [],
|
|
"details": "Create tables for users, preferences, and settings"
|
|
},
|
|
{
|
|
"id": 2,
|
|
"title": "Create API endpoints",
|
|
"description": "Develop RESTful API endpoints for user operations",
|
|
"dependencies": [],
|
|
"details": "Implement CRUD operations for user management"
|
|
}
|
|
]
|
|
|
|
These subtasks will help you implement the parent task efficiently.`;
|
|
|
|
const result = parseSubtasksFromText(text, 1, 2, 5);
|
|
|
|
expect(result).toHaveLength(2);
|
|
expect(result[0]).toEqual({
|
|
id: 1,
|
|
title: 'Implement database schema',
|
|
description: 'Design and implement the database schema for user data',
|
|
status: 'pending',
|
|
dependencies: [],
|
|
details: 'Create tables for users, preferences, and settings',
|
|
parentTaskId: 5
|
|
});
|
|
expect(result[1]).toEqual({
|
|
id: 2,
|
|
title: 'Create API endpoints',
|
|
description: 'Develop RESTful API endpoints for user operations',
|
|
status: 'pending',
|
|
dependencies: [],
|
|
details: 'Implement CRUD operations for user management',
|
|
parentTaskId: 5
|
|
});
|
|
});
|
|
|
|
test('should handle subtasks with dependencies', () => {
|
|
const text = `
|
|
[
|
|
{
|
|
"id": 1,
|
|
"title": "Setup React environment",
|
|
"description": "Initialize React app with necessary dependencies",
|
|
"dependencies": [],
|
|
"details": "Use Create React App or Vite to set up a new project"
|
|
},
|
|
{
|
|
"id": 2,
|
|
"title": "Create component structure",
|
|
"description": "Design and implement component hierarchy",
|
|
"dependencies": [1],
|
|
"details": "Organize components by feature and reusability"
|
|
}
|
|
]`;
|
|
|
|
const result = parseSubtasksFromText(text, 1, 2, 5);
|
|
|
|
expect(result).toHaveLength(2);
|
|
expect(result[0].dependencies).toEqual([]);
|
|
expect(result[1].dependencies).toEqual([1]);
|
|
});
|
|
|
|
test('should handle complex dependency lists', () => {
|
|
const text = `
|
|
[
|
|
{
|
|
"id": 1,
|
|
"title": "Setup database",
|
|
"description": "Initialize database structure",
|
|
"dependencies": [],
|
|
"details": "Set up PostgreSQL database"
|
|
},
|
|
{
|
|
"id": 2,
|
|
"title": "Create models",
|
|
"description": "Implement data models",
|
|
"dependencies": [1],
|
|
"details": "Define Prisma models"
|
|
},
|
|
{
|
|
"id": 3,
|
|
"title": "Implement controllers",
|
|
"description": "Create API controllers",
|
|
"dependencies": [1, 2],
|
|
"details": "Build controllers for all endpoints"
|
|
}
|
|
]`;
|
|
|
|
const result = parseSubtasksFromText(text, 1, 3, 5);
|
|
|
|
expect(result).toHaveLength(3);
|
|
expect(result[2].dependencies).toEqual([1, 2]);
|
|
});
|
|
|
|
test('should create fallback subtasks for empty text', () => {
|
|
const emptyText = '';
|
|
|
|
const result = parseSubtasksFromText(emptyText, 1, 2, 5);
|
|
|
|
// Verify fallback subtasks structure
|
|
expect(result).toHaveLength(2);
|
|
expect(result[0]).toMatchObject({
|
|
id: 1,
|
|
title: 'Subtask 1',
|
|
description: 'Auto-generated fallback subtask',
|
|
status: 'pending',
|
|
dependencies: [],
|
|
parentTaskId: 5
|
|
});
|
|
expect(result[1]).toMatchObject({
|
|
id: 2,
|
|
title: 'Subtask 2',
|
|
description: 'Auto-generated fallback subtask',
|
|
status: 'pending',
|
|
dependencies: [],
|
|
parentTaskId: 5
|
|
});
|
|
});
|
|
|
|
test('should normalize subtask IDs', () => {
|
|
const text = `
|
|
[
|
|
{
|
|
"id": 10,
|
|
"title": "First task with incorrect ID",
|
|
"description": "First description",
|
|
"dependencies": [],
|
|
"details": "First details"
|
|
},
|
|
{
|
|
"id": 20,
|
|
"title": "Second task with incorrect ID",
|
|
"description": "Second description",
|
|
"dependencies": [],
|
|
"details": "Second details"
|
|
}
|
|
]`;
|
|
|
|
const result = parseSubtasksFromText(text, 1, 2, 5);
|
|
|
|
expect(result).toHaveLength(2);
|
|
expect(result[0].id).toBe(1); // Should normalize to starting ID
|
|
expect(result[1].id).toBe(2); // Should normalize to starting ID + 1
|
|
});
|
|
|
|
test('should convert string dependencies to numbers', () => {
|
|
const text = `
|
|
[
|
|
{
|
|
"id": 1,
|
|
"title": "First task",
|
|
"description": "First description",
|
|
"dependencies": [],
|
|
"details": "First details"
|
|
},
|
|
{
|
|
"id": 2,
|
|
"title": "Second task",
|
|
"description": "Second description",
|
|
"dependencies": ["1"],
|
|
"details": "Second details"
|
|
}
|
|
]`;
|
|
|
|
const result = parseSubtasksFromText(text, 1, 2, 5);
|
|
|
|
expect(result[1].dependencies).toEqual([1]);
|
|
expect(typeof result[1].dependencies[0]).toBe('number');
|
|
});
|
|
|
|
test('should create fallback subtasks for invalid JSON', () => {
|
|
const text = `This is not valid JSON and cannot be parsed`;
|
|
|
|
const result = parseSubtasksFromText(text, 1, 2, 5);
|
|
|
|
// Verify fallback subtasks structure
|
|
expect(result).toHaveLength(2);
|
|
expect(result[0]).toMatchObject({
|
|
id: 1,
|
|
title: 'Subtask 1',
|
|
description: 'Auto-generated fallback subtask',
|
|
status: 'pending',
|
|
dependencies: [],
|
|
parentTaskId: 5
|
|
});
|
|
expect(result[1]).toMatchObject({
|
|
id: 2,
|
|
title: 'Subtask 2',
|
|
description: 'Auto-generated fallback subtask',
|
|
status: 'pending',
|
|
dependencies: [],
|
|
parentTaskId: 5
|
|
});
|
|
});
|
|
});
|
|
});
|