import { CalendarMCPServer } from '../../src/index';
import { DatabaseManager } from '../../src/database';
import { createCacheManager } from '../../src/cache';
import { Logger } from '../../src/utils/logger';
import { CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
describe('MCP Calendar Server Integration Tests', () => {
let server: CalendarMCPServer;
let database: DatabaseManager;
let cache: ReturnType<typeof createCacheManager>;
let testUserId: string;
beforeAll(async () => {
// Initialize test database and cache
database = new DatabaseManager();
cache = createCacheManager();
// Initialize database
await database.initialize();
// Connect cache if needed
if ('connect' in cache) {
await (cache as any).connect();
}
// Create test user
const testUser = await database.createUser({
email: 'integration-test@example.com',
displayName: 'Integration Test User',
timezone: 'UTC',
preferences: {
defaultCalendarId: 'primary',
workingHours: {
monday: { startTime: '09:00', endTime: '17:00' },
tuesday: { startTime: '09:00', endTime: '17:00' },
wednesday: { startTime: '09:00', endTime: '17:00' },
thursday: { startTime: '09:00', endTime: '17:00' },
friday: { startTime: '09:00', endTime: '17:00' }
},
defaultEventDuration: 60,
bufferTime: 15,
autoDeclineConflicts: false,
reminderDefaults: {
defaultReminders: [15],
emailReminders: true,
pushReminders: false
}
}
});
testUserId = testUser.id;
// Initialize MCP server
server = new CalendarMCPServer();
}, 30000);
afterAll(async () => {
// Cleanup
try {
if (database) {
await database.close();
}
if (cache && 'disconnect' in cache) {
await (cache as any).disconnect();
}
if (server) {
await server.stop();
}
} catch (error) {
console.error('Cleanup error:', error);
}
});
describe('Reminder Management Workflow', () => {
let createdReminderId: string;
it('should create a reminder through MCP tool call', async () => {
const toolCall = {
method: 'tools/call',
params: {
name: 'create_reminder',
arguments: {
user_id: testUserId,
title: 'Integration Test Reminder',
description: 'This is a test reminder created during integration testing',
priority: 'high',
due_date_time: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
tags: ['integration', 'test']
}
}
};
// This would normally go through the MCP protocol
// For testing, we'll call the tool handler directly
const calendarTools = (server as any).calendarTools;
const result = await calendarTools.handleToolCall(
'create_reminder',
toolCall.params.arguments,
testUserId
);
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data.title).toBe('Integration Test Reminder');
expect(result.data.priority).toBe('high');
expect(result.data.tags).toContain('integration');
createdReminderId = result.data.id;
});
it('should retrieve reminders through MCP tool call', async () => {
const calendarTools = (server as any).calendarTools;
const result = await calendarTools.handleToolCall(
'get_reminders',
{ user_id: testUserId },
testUserId
);
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(Array.isArray(result.data)).toBe(true);
expect(result.data.length).toBeGreaterThan(0);
const testReminder = result.data.find((r: any) => r.id === createdReminderId);
expect(testReminder).toBeDefined();
expect(testReminder.title).toBe('Integration Test Reminder');
});
it('should filter reminders by priority', async () => {
const calendarTools = (server as any).calendarTools;
const result = await calendarTools.handleToolCall(
'get_reminders',
{
user_id: testUserId,
priority: 'high'
},
testUserId
);
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(Array.isArray(result.data)).toBe(true);
// All returned reminders should be high priority
result.data.forEach((reminder: any) => {
expect(reminder.priority).toBe('high');
});
});
it('should update a reminder through MCP tool call', async () => {
const calendarTools = (server as any).calendarTools;
const result = await calendarTools.handleToolCall(
'update_reminder',
{
user_id: testUserId,
reminder_id: createdReminderId,
title: 'Updated Integration Test Reminder',
priority: 'medium',
description: 'Updated description for integration test'
},
testUserId
);
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data.title).toBe('Updated Integration Test Reminder');
expect(result.data.priority).toBe('medium');
expect(result.data.description).toBe('Updated description for integration test');
});
it('should complete a reminder through MCP tool call', async () => {
const calendarTools = (server as any).calendarTools;
const result = await calendarTools.handleToolCall(
'complete_reminder',
{
user_id: testUserId,
reminder_id: createdReminderId
},
testUserId
);
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data.status).toBe('completed');
});
});
describe('Calendar Operations Workflow', () => {
it('should get calendars through MCP tool call', async () => {
const calendarTools = (server as any).calendarTools;
const result = await calendarTools.handleToolCall(
'get_calendars',
{ user_id: testUserId },
testUserId
);
// This will fail in test environment without real Google Calendar connection
// but we can test the error handling
expect(result.success).toBeDefined();
if (!result.success) {
expect(result.error).toBeDefined();
expect(result.error.code).toBe('USER_NOT_FOUND'); // Because no connected calendar
}
});
it('should handle working hours request', async () => {
const calendarTools = (server as any).calendarTools;
const result = await calendarTools.handleToolCall(
'get_working_hours',
{
user_id: testUserId,
calendar_id: 'primary'
},
testUserId
);
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data.monday).toBeDefined();
expect(result.data.monday.startTime).toBe('09:00');
expect(result.data.monday.endTime).toBe('17:00');
});
});
describe('Error Handling', () => {
it('should handle invalid tool names gracefully', async () => {
const calendarTools = (server as any).calendarTools;
try {
await calendarTools.handleToolCall(
'invalid_tool_name',
{ user_id: testUserId },
testUserId
);
fail('Should have thrown an error');
} catch (error: any) {
expect(error.message).toContain('Unknown tool');
}
});
it('should handle missing required parameters', async () => {
const calendarTools = (server as any).calendarTools;
try {
await calendarTools.handleToolCall(
'create_reminder',
{ user_id: testUserId }, // Missing required 'title'
testUserId
);
fail('Should have thrown an error');
} catch (error: any) {
expect(error.message).toContain('Invalid parameters');
}
});
it('should handle non-existent user ID', async () => {
const calendarTools = (server as any).calendarTools;
const result = await calendarTools.handleToolCall(
'get_reminders',
{ user_id: 'non-existent-user' },
'non-existent-user'
);
expect(result.success).toBe(true);
expect(result.data).toEqual([]);
});
});
describe('Performance and Caching', () => {
it('should cache and retrieve working hours efficiently', async () => {
const calendarTools = (server as any).calendarTools;
// First call - should fetch from database
const start1 = Date.now();
const result1 = await calendarTools.handleToolCall(
'get_working_hours',
{ user_id: testUserId, calendar_id: 'primary' },
testUserId
);
const duration1 = Date.now() - start1;
expect(result1.success).toBe(true);
// Second call - should be faster due to caching (if caching is enabled)
const start2 = Date.now();
const result2 = await calendarTools.handleToolCall(
'get_working_hours',
{ user_id: testUserId, calendar_id: 'primary' },
testUserId
);
const duration2 = Date.now() - start2;
expect(result2.success).toBe(true);
expect(result2.data).toEqual(result1.data);
// If caching is enabled, second call should be faster
if (process.env.FEATURE_CACHING !== 'false') {
expect(duration2).toBeLessThanOrEqual(duration1);
}
});
it('should handle concurrent requests efficiently', async () => {
const calendarTools = (server as any).calendarTools;
// Create multiple concurrent reminder creation requests
const promises = Array.from({ length: 10 }, (_, i) =>
calendarTools.handleToolCall(
'create_reminder',
{
user_id: testUserId,
title: `Concurrent Test Reminder ${i}`,
description: `Created in concurrent test batch`,
priority: 'low'
},
testUserId
)
);
const results = await Promise.all(promises);
// All requests should succeed
results.forEach((result, index) => {
expect(result.success).toBe(true);
expect(result.data.title).toBe(`Concurrent Test Reminder ${index}`);
});
// Verify all reminders were created
const retrieveResult = await calendarTools.handleToolCall(
'get_reminders',
{ user_id: testUserId },
testUserId
);
expect(retrieveResult.success).toBe(true);
expect(retrieveResult.data.length).toBeGreaterThanOrEqual(10);
});
});
describe('Data Consistency', () => {
it('should maintain data consistency across operations', async () => {
const calendarTools = (server as any).calendarTools;
// Create a reminder
const createResult = await calendarTools.handleToolCall(
'create_reminder',
{
user_id: testUserId,
title: 'Consistency Test Reminder',
priority: 'medium'
},
testUserId
);
expect(createResult.success).toBe(true);
const reminderId = createResult.data.id;
// Retrieve all reminders
const getAllResult = await calendarTools.handleToolCall(
'get_reminders',
{ user_id: testUserId },
testUserId
);
expect(getAllResult.success).toBe(true);
const createdReminder = getAllResult.data.find((r: any) => r.id === reminderId);
expect(createdReminder).toBeDefined();
expect(createdReminder.title).toBe('Consistency Test Reminder');
// Update the reminder
const updateResult = await calendarTools.handleToolCall(
'update_reminder',
{
user_id: testUserId,
reminder_id: reminderId,
title: 'Updated Consistency Test Reminder',
priority: 'high'
},
testUserId
);
expect(updateResult.success).toBe(true);
// Verify the update is reflected in get operations
const getUpdatedResult = await calendarTools.handleToolCall(
'get_reminders',
{ user_id: testUserId },
testUserId
);
const updatedReminder = getUpdatedResult.data.find((r: any) => r.id === reminderId);
expect(updatedReminder).toBeDefined();
expect(updatedReminder.title).toBe('Updated Consistency Test Reminder');
expect(updatedReminder.priority).toBe('high');
});
});
describe('Tool Schema Validation', () => {
it('should validate create_reminder parameters correctly', async () => {
const calendarTools = (server as any).calendarTools;
// Test with invalid priority
try {
await calendarTools.handleToolCall(
'create_reminder',
{
user_id: testUserId,
title: 'Test Reminder',
priority: 'invalid_priority'
},
testUserId
);
fail('Should have thrown validation error');
} catch (error: any) {
expect(error.message).toContain('Invalid parameters');
}
});
it('should validate find_free_time parameters correctly', async () => {
const calendarTools = (server as any).calendarTools;
// Test with invalid duration
try {
await calendarTools.handleToolCall(
'find_free_time',
{
user_id: testUserId,
calendar_ids: ['primary'],
start_date: new Date().toISOString(),
end_date: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
duration_minutes: -30 // Invalid negative duration
},
testUserId
);
fail('Should have thrown validation error');
} catch (error: any) {
expect(error.message).toContain('Invalid parameters');
}
});
});
});
describe('Health Check Integration', () => {
let server: CalendarMCPServer;
beforeAll(async () => {
server = new CalendarMCPServer();
}, 15000);
afterAll(async () => {
if (server) {
await server.stop();
}
});
it('should respond to health checks', async () => {
// This would test the health endpoint if we had an HTTP server running
// For now, we'll test the underlying health check components
const database = new DatabaseManager();
const cache = createCacheManager();
try {
await database.initialize();
if ('connect' in cache) {
await (cache as any).connect();
}
const dbHealth = await database.ping();
const cacheHealth = await cache.ping();
expect(typeof dbHealth).toBe('boolean');
expect(typeof cacheHealth).toBe('boolean');
await database.close();
if ('disconnect' in cache) {
await (cache as any).disconnect();
}
} catch (error) {
// Health checks might fail in test environment - that's ok
console.log('Health check test note:', error);
}
});
});