mirror of https://github.com/buster-so/buster.git
326 lines
11 KiB
TypeScript
326 lines
11 KiB
TypeScript
|
import { describe, expect, test } from 'vitest';
|
||
|
import {
|
||
|
messageUserClarifyingQuestionTool,
|
||
|
processClarificationResponseTool,
|
||
|
shouldAskClarification,
|
||
|
} from '../../../src/tools/communication-tools/message-user-clarifying-question-tool';
|
||
|
|
||
|
describe('Message User Clarifying Question Tool Unit Tests', () => {
|
||
|
test('should have correct configuration', () => {
|
||
|
expect(messageUserClarifyingQuestionTool.id).toBe('message-user-clarifying-question');
|
||
|
expect(messageUserClarifyingQuestionTool.description).toBe(
|
||
|
'Ask the user a clarifying question when requirements are unclear'
|
||
|
);
|
||
|
expect(messageUserClarifyingQuestionTool.inputSchema).toBeDefined();
|
||
|
expect(messageUserClarifyingQuestionTool.outputSchema).toBeDefined();
|
||
|
expect(messageUserClarifyingQuestionTool.execute).toBeDefined();
|
||
|
});
|
||
|
|
||
|
test('should validate input schema', () => {
|
||
|
const validInput = {
|
||
|
question: 'Which time period would you like to analyze?',
|
||
|
context: 'Multiple time periods are available in the data',
|
||
|
question_type: 'open_ended' as const,
|
||
|
priority: 'medium' as const,
|
||
|
};
|
||
|
const result = messageUserClarifyingQuestionTool.inputSchema.safeParse(validInput);
|
||
|
expect(result.success).toBe(true);
|
||
|
});
|
||
|
|
||
|
test('should validate output schema structure', () => {
|
||
|
const validOutput = {
|
||
|
success: true,
|
||
|
message_sent: true,
|
||
|
formatted_question: 'Which time period would you like to analyze?',
|
||
|
requires_response: true,
|
||
|
conversation_paused: true,
|
||
|
};
|
||
|
|
||
|
const result = messageUserClarifyingQuestionTool.outputSchema.safeParse(validOutput);
|
||
|
expect(result.success).toBe(true);
|
||
|
});
|
||
|
|
||
|
test('should format open-ended question correctly', async () => {
|
||
|
const result = await messageUserClarifyingQuestionTool.execute({
|
||
|
context: {
|
||
|
question: 'Which time period would you like to analyze?',
|
||
|
context: 'Multiple time periods are available in the data',
|
||
|
question_type: 'open_ended',
|
||
|
},
|
||
|
});
|
||
|
|
||
|
expect(result.success).toBe(true);
|
||
|
expect(result.formatted_question).toContain('Which time period');
|
||
|
expect(result.formatted_question).toContain('Context:');
|
||
|
expect(result.conversation_paused).toBe(true);
|
||
|
expect(result.requires_response).toBe(true);
|
||
|
});
|
||
|
|
||
|
test('should format multiple choice question with options', async () => {
|
||
|
const result = await messageUserClarifyingQuestionTool.execute({
|
||
|
context: {
|
||
|
question: 'Which metric would you like to focus on?',
|
||
|
question_type: 'multiple_choice',
|
||
|
options: [
|
||
|
{ value: 'revenue', label: 'Revenue', description: 'Total sales revenue' },
|
||
|
{ value: 'profit', label: 'Profit', description: 'Net profit after costs' },
|
||
|
{ value: 'volume', label: 'Volume', description: 'Number of units sold' },
|
||
|
],
|
||
|
},
|
||
|
});
|
||
|
|
||
|
expect(result.success).toBe(true);
|
||
|
expect(result.formatted_question).toContain('1. **Revenue**');
|
||
|
expect(result.formatted_question).toContain('Total sales revenue');
|
||
|
expect(result.formatted_question).toContain('2. **Profit**');
|
||
|
expect(result.formatted_question).toContain('3. **Volume**');
|
||
|
});
|
||
|
|
||
|
test('should format yes/no question correctly', async () => {
|
||
|
const result = await messageUserClarifyingQuestionTool.execute({
|
||
|
context: {
|
||
|
question: 'Should I include historical data?',
|
||
|
question_type: 'yes_no',
|
||
|
},
|
||
|
});
|
||
|
|
||
|
expect(result.success).toBe(true);
|
||
|
expect(result.formatted_question).toContain('Should I include historical data?');
|
||
|
expect(result.formatted_question).toContain('**Yes** or **No**');
|
||
|
});
|
||
|
|
||
|
test('should format confirmation question correctly', async () => {
|
||
|
const result = await messageUserClarifyingQuestionTool.execute({
|
||
|
context: {
|
||
|
question: 'Please confirm before proceeding with data deletion',
|
||
|
question_type: 'confirmation',
|
||
|
},
|
||
|
});
|
||
|
|
||
|
expect(result.success).toBe(true);
|
||
|
expect(result.formatted_question).toContain('data deletion');
|
||
|
expect(result.formatted_question).toContain('**Confirm**');
|
||
|
});
|
||
|
|
||
|
test('should handle high priority questions', async () => {
|
||
|
const result = await messageUserClarifyingQuestionTool.execute({
|
||
|
context: {
|
||
|
question: 'Please confirm before proceeding with data deletion',
|
||
|
question_type: 'confirmation',
|
||
|
priority: 'high',
|
||
|
},
|
||
|
});
|
||
|
|
||
|
expect(result.success).toBe(true);
|
||
|
expect(result.formatted_question).toContain('🔴 **Important Clarification Needed**');
|
||
|
});
|
||
|
|
||
|
test('should include related context', async () => {
|
||
|
const result = await messageUserClarifyingQuestionTool.execute({
|
||
|
context: {
|
||
|
question: 'Which format should I use?',
|
||
|
question_type: 'open_ended',
|
||
|
related_to: 'data export functionality',
|
||
|
},
|
||
|
});
|
||
|
|
||
|
expect(result.success).toBe(true);
|
||
|
expect(result.formatted_question).toContain('related to: data export functionality');
|
||
|
});
|
||
|
|
||
|
test('should handle medium and low priority questions', async () => {
|
||
|
const mediumResult = await messageUserClarifyingQuestionTool.execute({
|
||
|
context: {
|
||
|
question: 'Which chart type?',
|
||
|
priority: 'medium',
|
||
|
},
|
||
|
});
|
||
|
|
||
|
const lowResult = await messageUserClarifyingQuestionTool.execute({
|
||
|
context: {
|
||
|
question: 'Which color scheme?',
|
||
|
priority: 'low',
|
||
|
},
|
||
|
});
|
||
|
|
||
|
expect(mediumResult.formatted_question).not.toContain('🔴');
|
||
|
expect(lowResult.formatted_question).not.toContain('🔴');
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('Process Clarification Response Tool Tests', () => {
|
||
|
test('should have correct configuration', () => {
|
||
|
expect(processClarificationResponseTool.id).toBe('process-clarification-response');
|
||
|
expect(processClarificationResponseTool.description).toBe(
|
||
|
'Process user response to a clarifying question'
|
||
|
);
|
||
|
});
|
||
|
|
||
|
test('should validate input schema', () => {
|
||
|
const validInput = {
|
||
|
response: 'Yes, include historical data',
|
||
|
question_id: 'clarify_123',
|
||
|
};
|
||
|
const result = processClarificationResponseTool.inputSchema.safeParse(validInput);
|
||
|
expect(result.success).toBe(true);
|
||
|
});
|
||
|
|
||
|
test('should process valid yes/no response', async () => {
|
||
|
// First ask a yes/no question
|
||
|
await messageUserClarifyingQuestionTool.execute({
|
||
|
context: {
|
||
|
question: 'Should I include historical data?',
|
||
|
question_type: 'yes_no',
|
||
|
},
|
||
|
});
|
||
|
|
||
|
// Then process the response
|
||
|
const result = await processClarificationResponseTool.execute({
|
||
|
context: { response: 'Yes' },
|
||
|
});
|
||
|
|
||
|
expect(result.success).toBe(true);
|
||
|
expect(result.response_processed).toBe(true);
|
||
|
expect(result.workflow_resumed).toBe(true);
|
||
|
expect(result.validation_result?.valid).toBe(true);
|
||
|
});
|
||
|
|
||
|
test('should reject invalid yes/no response', async () => {
|
||
|
// First ask a yes/no question
|
||
|
await messageUserClarifyingQuestionTool.execute({
|
||
|
context: {
|
||
|
question: 'Should I proceed?',
|
||
|
question_type: 'yes_no',
|
||
|
},
|
||
|
});
|
||
|
|
||
|
// Then process invalid response
|
||
|
const result = await processClarificationResponseTool.execute({
|
||
|
context: { response: 'Maybe' },
|
||
|
});
|
||
|
|
||
|
expect(result.success).toBe(false);
|
||
|
expect(result.response_processed).toBe(false);
|
||
|
expect(result.workflow_resumed).toBe(false);
|
||
|
expect(result.validation_result?.valid).toBe(false);
|
||
|
expect(result.validation_result?.error_message).toContain('Yes or No');
|
||
|
});
|
||
|
|
||
|
test('should process valid confirmation response', async () => {
|
||
|
// First ask a confirmation question
|
||
|
await messageUserClarifyingQuestionTool.execute({
|
||
|
context: {
|
||
|
question: 'Please confirm the data deletion',
|
||
|
question_type: 'confirmation',
|
||
|
},
|
||
|
});
|
||
|
|
||
|
// Then process confirmation
|
||
|
const result = await processClarificationResponseTool.execute({
|
||
|
context: { response: 'Confirm' },
|
||
|
});
|
||
|
|
||
|
expect(result.success).toBe(true);
|
||
|
expect(result.validation_result?.valid).toBe(true);
|
||
|
});
|
||
|
|
||
|
test('should reject invalid confirmation response', async () => {
|
||
|
// First ask a confirmation question
|
||
|
await messageUserClarifyingQuestionTool.execute({
|
||
|
context: {
|
||
|
question: 'Please confirm the action',
|
||
|
question_type: 'confirmation',
|
||
|
},
|
||
|
});
|
||
|
|
||
|
// Then process short/invalid response
|
||
|
const result = await processClarificationResponseTool.execute({
|
||
|
context: { response: 'ok' },
|
||
|
});
|
||
|
|
||
|
expect(result.success).toBe(false);
|
||
|
expect(result.validation_result?.error_message).toContain('Confirm');
|
||
|
});
|
||
|
|
||
|
test('should process open-ended responses', async () => {
|
||
|
// First ask an open-ended question
|
||
|
await messageUserClarifyingQuestionTool.execute({
|
||
|
context: {
|
||
|
question: 'What time period would you like?',
|
||
|
question_type: 'open_ended',
|
||
|
},
|
||
|
});
|
||
|
|
||
|
// Then process any response (open-ended doesn't validate)
|
||
|
const result = await processClarificationResponseTool.execute({
|
||
|
context: { response: 'Last 3 months' },
|
||
|
});
|
||
|
|
||
|
expect(result.success).toBe(true);
|
||
|
expect(result.validation_result?.valid).toBe(true);
|
||
|
});
|
||
|
|
||
|
test('should handle no pending clarification request', async () => {
|
||
|
// Try to process response without asking question first
|
||
|
const result = await processClarificationResponseTool.execute({
|
||
|
context: { response: 'Some response' },
|
||
|
});
|
||
|
|
||
|
// The tool may handle this gracefully rather than throwing
|
||
|
expect(result.success).toBe(true);
|
||
|
expect(result.response_processed).toBe(true);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('Should Ask Clarification Helper Tests', () => {
|
||
|
test('should ask clarification for high ambiguity', () => {
|
||
|
const result = shouldAskClarification({
|
||
|
ambiguity_score: 0.8,
|
||
|
missing_requirements: [],
|
||
|
confidence_level: 0.7,
|
||
|
});
|
||
|
|
||
|
expect(result).toBe(true);
|
||
|
});
|
||
|
|
||
|
test('should ask clarification for missing requirements', () => {
|
||
|
const result = shouldAskClarification({
|
||
|
ambiguity_score: 0.3,
|
||
|
missing_requirements: ['time_period', 'metrics'],
|
||
|
confidence_level: 0.7,
|
||
|
});
|
||
|
|
||
|
expect(result).toBe(true);
|
||
|
});
|
||
|
|
||
|
test('should ask clarification for low confidence', () => {
|
||
|
const result = shouldAskClarification({
|
||
|
ambiguity_score: 0.3,
|
||
|
missing_requirements: [],
|
||
|
confidence_level: 0.4,
|
||
|
});
|
||
|
|
||
|
expect(result).toBe(true);
|
||
|
});
|
||
|
|
||
|
test('should not ask clarification when all conditions are good', () => {
|
||
|
const result = shouldAskClarification({
|
||
|
ambiguity_score: 0.2,
|
||
|
missing_requirements: [],
|
||
|
confidence_level: 0.8,
|
||
|
});
|
||
|
|
||
|
expect(result).toBe(false);
|
||
|
});
|
||
|
|
||
|
test('should ask clarification when multiple conditions are borderline', () => {
|
||
|
const result = shouldAskClarification({
|
||
|
ambiguity_score: 0.6,
|
||
|
missing_requirements: ['time_period'],
|
||
|
confidence_level: 0.6,
|
||
|
});
|
||
|
|
||
|
expect(result).toBe(true);
|
||
|
});
|
||
|
});
|