380 lines
11 KiB
TypeScript
380 lines
11 KiB
TypeScript
import 'reflect-metadata';
|
|
import { container } from 'tsyringe';
|
|
import { TaskClassifierService } from '../../../src/services/nlp/classifiers/task-classifier.service';
|
|
import { OpenAIService } from '../../../src/services/ai/openai.service';
|
|
|
|
// Mock OpenAIService
|
|
jest.mock('../../../src/services/ai/openai.service');
|
|
|
|
describe('TaskClassifierService', () => {
|
|
let service: TaskClassifierService;
|
|
let mockOpenAIService: jest.Mocked<OpenAIService>;
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
container.clearInstances();
|
|
|
|
// Create mock OpenAI service
|
|
mockOpenAIService = {
|
|
complete: jest.fn(),
|
|
} as any;
|
|
|
|
// Register mock with container
|
|
container.registerInstance(OpenAIService, mockOpenAIService);
|
|
|
|
// Create service
|
|
service = container.resolve(TaskClassifierService);
|
|
});
|
|
|
|
describe('classify', () => {
|
|
it('should classify task type correctly', async () => {
|
|
// Arrange
|
|
const text = 'Add new user registration feature with email verification';
|
|
const entities = {
|
|
title: 'User registration feature',
|
|
description: 'Implement user registration with email verification',
|
|
};
|
|
|
|
mockOpenAIService.complete.mockResolvedValue({
|
|
content: JSON.stringify({
|
|
taskType: 'feature',
|
|
subType: 'authentication',
|
|
confidence: 0.9,
|
|
}),
|
|
usage: { totalTokens: 100 },
|
|
});
|
|
|
|
// Act
|
|
const result = await service.classify({ text, entities, language: 'en' });
|
|
|
|
// Assert
|
|
expect(result.taskType).toBe('feature');
|
|
expect(result.subType).toBe('authentication');
|
|
expect(result.confidence).toBeGreaterThan(0.8);
|
|
});
|
|
|
|
it('should classify bug reports', async () => {
|
|
// Arrange
|
|
const text = 'Fix the login button not working on mobile devices';
|
|
const entities = {
|
|
title: 'Fix login button on mobile',
|
|
priority: 'high',
|
|
};
|
|
|
|
mockOpenAIService.complete.mockResolvedValue({
|
|
content: JSON.stringify({
|
|
taskType: 'bug',
|
|
severity: 'high',
|
|
affectedArea: 'authentication',
|
|
confidence: 0.95,
|
|
}),
|
|
usage: { totalTokens: 100 },
|
|
});
|
|
|
|
// Act
|
|
const result = await service.classify({ text, entities, language: 'en' });
|
|
|
|
// Assert
|
|
expect(result.taskType).toBe('bug');
|
|
expect(result.severity).toBe('high');
|
|
expect(result.affectedArea).toBe('authentication');
|
|
});
|
|
|
|
it('should classify improvements', async () => {
|
|
// Arrange
|
|
const text = 'Optimize database queries to improve page load time';
|
|
const entities = {
|
|
title: 'Optimize database queries',
|
|
description: 'Performance improvement',
|
|
};
|
|
|
|
mockOpenAIService.complete.mockResolvedValue({
|
|
content: JSON.stringify({
|
|
taskType: 'improvement',
|
|
category: 'performance',
|
|
impact: 'medium',
|
|
confidence: 0.85,
|
|
}),
|
|
usage: { totalTokens: 100 },
|
|
});
|
|
|
|
// Act
|
|
const result = await service.classify({ text, entities, language: 'en' });
|
|
|
|
// Assert
|
|
expect(result.taskType).toBe('improvement');
|
|
expect(result.category).toBe('performance');
|
|
expect(result.impact).toBe('medium');
|
|
});
|
|
|
|
it('should detect priority from context', async () => {
|
|
// Arrange
|
|
const text = 'URGENT: Critical security patch needed ASAP';
|
|
const entities = {
|
|
title: 'Security patch',
|
|
priority: 'critical',
|
|
};
|
|
|
|
mockOpenAIService.complete.mockResolvedValue({
|
|
content: JSON.stringify({
|
|
taskType: 'security',
|
|
priority: 'critical',
|
|
urgency: 'immediate',
|
|
confidence: 0.98,
|
|
}),
|
|
usage: { totalTokens: 100 },
|
|
});
|
|
|
|
// Act
|
|
const result = await service.classify({ text, entities, language: 'en' });
|
|
|
|
// Assert
|
|
expect(result.priority).toBe('critical');
|
|
expect(result.urgency).toBe('immediate');
|
|
expect(result.confidence).toBeGreaterThan(0.95);
|
|
});
|
|
|
|
it('should analyze sentiment for urgency', async () => {
|
|
// Arrange
|
|
const text = 'This is blocking the entire team! We need this fixed NOW!';
|
|
const entities = {
|
|
title: 'Blocking issue',
|
|
};
|
|
|
|
mockOpenAIService.complete.mockResolvedValue({
|
|
content: JSON.stringify({
|
|
taskType: 'blocker',
|
|
sentiment: 'negative',
|
|
urgency: 'critical',
|
|
emotionalTone: 'frustrated',
|
|
confidence: 0.92,
|
|
}),
|
|
usage: { totalTokens: 100 },
|
|
});
|
|
|
|
// Act
|
|
const result = await service.classify({ text, entities, language: 'en' });
|
|
|
|
// Assert
|
|
expect(result.urgency).toBe('critical');
|
|
expect(result.sentiment).toBe('negative');
|
|
expect(result.emotionalTone).toBe('frustrated');
|
|
});
|
|
|
|
it('should estimate complexity', async () => {
|
|
// Arrange
|
|
const text = 'Refactor the entire authentication system to use OAuth 2.0 with multiple providers';
|
|
const entities = {
|
|
title: 'Refactor authentication system',
|
|
estimatedHours: 40,
|
|
};
|
|
|
|
mockOpenAIService.complete.mockResolvedValue({
|
|
content: JSON.stringify({
|
|
taskType: 'refactoring',
|
|
complexity: 'high',
|
|
estimatedEffort: 'large',
|
|
riskLevel: 'medium',
|
|
confidence: 0.8,
|
|
}),
|
|
usage: { totalTokens: 100 },
|
|
});
|
|
|
|
// Act
|
|
const result = await service.classify({ text, entities, language: 'en' });
|
|
|
|
// Assert
|
|
expect(result.complexity).toBe('high');
|
|
expect(result.estimatedEffort).toBe('large');
|
|
expect(result.riskLevel).toBe('medium');
|
|
});
|
|
|
|
it('should identify technical areas', async () => {
|
|
// Arrange
|
|
const text = 'Update React components to use TypeScript and add unit tests';
|
|
const entities = {
|
|
title: 'Update React components',
|
|
tags: ['frontend', 'testing'],
|
|
};
|
|
|
|
mockOpenAIService.complete.mockResolvedValue({
|
|
content: JSON.stringify({
|
|
taskType: 'technical-debt',
|
|
technicalAreas: ['frontend', 'typescript', 'testing'],
|
|
frameworks: ['react'],
|
|
confidence: 0.88,
|
|
}),
|
|
usage: { totalTokens: 100 },
|
|
});
|
|
|
|
// Act
|
|
const result = await service.classify({ text, entities, language: 'en' });
|
|
|
|
// Assert
|
|
expect(result.technicalAreas).toContain('frontend');
|
|
expect(result.technicalAreas).toContain('typescript');
|
|
expect(result.frameworks).toContain('react');
|
|
});
|
|
|
|
it('should classify documentation tasks', async () => {
|
|
// Arrange
|
|
const text = 'Write API documentation for the new endpoints';
|
|
const entities = {
|
|
title: 'API documentation',
|
|
};
|
|
|
|
mockOpenAIService.complete.mockResolvedValue({
|
|
content: JSON.stringify({
|
|
taskType: 'documentation',
|
|
documentationType: 'api',
|
|
priority: 'medium',
|
|
confidence: 0.9,
|
|
}),
|
|
usage: { totalTokens: 100 },
|
|
});
|
|
|
|
// Act
|
|
const result = await service.classify({ text, entities, language: 'en' });
|
|
|
|
// Assert
|
|
expect(result.taskType).toBe('documentation');
|
|
expect(result.documentationType).toBe('api');
|
|
});
|
|
|
|
it('should handle multi-language classification', async () => {
|
|
// Arrange
|
|
const text = 'Corregir el error en el sistema de pagos'; // Spanish
|
|
const entities = {
|
|
title: 'Error sistema de pagos',
|
|
};
|
|
|
|
mockOpenAIService.complete.mockResolvedValue({
|
|
content: JSON.stringify({
|
|
taskType: 'bug',
|
|
affectedArea: 'payments',
|
|
language: 'es',
|
|
confidence: 0.85,
|
|
}),
|
|
usage: { totalTokens: 100 },
|
|
});
|
|
|
|
// Act
|
|
const result = await service.classifyTask(text, entities, 'es');
|
|
|
|
// Assert
|
|
expect(result.taskType).toBe('bug');
|
|
expect(result.affectedArea).toBe('payments');
|
|
expect(result.language).toBe('es');
|
|
});
|
|
|
|
it('should provide confidence scores', async () => {
|
|
// Arrange
|
|
const text = 'Maybe look into improving something';
|
|
const entities = {
|
|
title: 'Improvement task',
|
|
};
|
|
|
|
mockOpenAIService.complete.mockResolvedValue({
|
|
content: JSON.stringify({
|
|
taskType: 'improvement',
|
|
confidence: 0.4,
|
|
uncertaintyReason: 'vague description',
|
|
}),
|
|
usage: { totalTokens: 100 },
|
|
});
|
|
|
|
// Act
|
|
const result = await service.classify({ text, entities, language: 'en' });
|
|
|
|
// Assert
|
|
expect(result.confidence).toBeLessThan(0.5);
|
|
expect(result.uncertaintyReason).toBe('vague description');
|
|
});
|
|
|
|
it('should handle classification errors', async () => {
|
|
// Arrange
|
|
const text = 'Test task';
|
|
const entities = { title: 'Test' };
|
|
|
|
mockOpenAIService.complete.mockRejectedValue(new Error('Classification failed'));
|
|
|
|
// Act & Assert
|
|
await expect(service.classify({ text, entities, language: 'en' })).rejects.toThrow('Classification failed');
|
|
});
|
|
|
|
it('should classify research tasks', async () => {
|
|
// Arrange
|
|
const text = 'Research and evaluate different caching strategies for the API';
|
|
const entities = {
|
|
title: 'Research caching strategies',
|
|
};
|
|
|
|
mockOpenAIService.complete.mockResolvedValue({
|
|
content: JSON.stringify({
|
|
taskType: 'research',
|
|
researchArea: 'performance',
|
|
deliverables: ['evaluation report', 'recommendations'],
|
|
confidence: 0.87,
|
|
}),
|
|
usage: { totalTokens: 100 },
|
|
});
|
|
|
|
// Act
|
|
const result = await service.classify({ text, entities, language: 'en' });
|
|
|
|
// Assert
|
|
expect(result.taskType).toBe('research');
|
|
expect(result.researchArea).toBe('performance');
|
|
expect(result.deliverables).toContain('evaluation report');
|
|
});
|
|
});
|
|
|
|
describe('pattern-based classification', () => {
|
|
it('should detect bug patterns', async () => {
|
|
// Arrange
|
|
const text = 'BUG: Application crashes when user clicks submit';
|
|
const entities = { title: 'Application crash' };
|
|
|
|
mockOpenAIService.complete.mockResolvedValue({
|
|
content: JSON.stringify({
|
|
taskType: 'bug',
|
|
patternMatch: true,
|
|
confidence: 0.99,
|
|
}),
|
|
usage: { totalTokens: 100 },
|
|
});
|
|
|
|
// Act
|
|
const result = await service.classify({ text, entities, language: 'en' });
|
|
|
|
// Assert
|
|
expect(result.taskType).toBe('bug');
|
|
expect(result.patternMatch).toBe(true);
|
|
expect(result.confidence).toBeGreaterThan(0.95);
|
|
});
|
|
|
|
it('should detect feature patterns', async () => {
|
|
// Arrange
|
|
const text = 'FEATURE REQUEST: Add dark mode to the application';
|
|
const entities = { title: 'Add dark mode' };
|
|
|
|
mockOpenAIService.complete.mockResolvedValue({
|
|
content: JSON.stringify({
|
|
taskType: 'feature',
|
|
requestType: 'enhancement',
|
|
patternMatch: true,
|
|
confidence: 0.95,
|
|
}),
|
|
usage: { totalTokens: 100 },
|
|
});
|
|
|
|
// Act
|
|
const result = await service.classify({ text, entities, language: 'en' });
|
|
|
|
// Assert
|
|
expect(result.taskType).toBe('feature');
|
|
expect(result.requestType).toBe('enhancement');
|
|
expect(result.patternMatch).toBe(true);
|
|
});
|
|
});
|
|
}); |