/**
* Integration tests for MCP OpenClaw Server
*
* These tests verify the end-to-end behavior of the MCP server
* with mocked external dependencies.
*/
import { OpenClawClient } from '../src/openclaw-client.js';
import { tools, toolHandlers } from '../src/tools/index.js';
// Mock the OpenClawClient
jest.mock('../src/openclaw-client');
const MockedOpenClawClient = OpenClawClient as jest.MockedClass<typeof OpenClawClient>;
describe('MCP Server Integration', () => {
let mockClient: jest.Mocked<OpenClawClient>;
beforeEach(() => {
mockClient = {
sendMessage: jest.fn(),
executeCommand: jest.fn(),
createCalendarEvent: jest.fn(),
sendEmail: jest.fn(),
getTaskStatus: jest.fn(),
healthCheck: jest.fn(),
close: jest.fn(),
} as unknown as jest.Mocked<OpenClawClient>;
jest.clearAllMocks();
});
describe('Tool Registration', () => {
it('should register all expected tools', () => {
const toolNames = tools.map((t) => t.name);
expect(toolNames).toContain('send_message');
expect(toolNames).toContain('execute_command');
expect(toolNames).toContain('create_calendar_event');
expect(toolNames).toContain('send_email');
expect(toolNames).toContain('get_task_status');
});
it('should have unique tool names', () => {
const toolNames = tools.map((t) => t.name);
const uniqueNames = new Set(toolNames);
expect(uniqueNames.size).toBe(toolNames.length);
});
});
describe('Tool Handlers', () => {
it('should have handlers for all tools', () => {
const handlerNames = Object.keys(toolHandlers);
const toolNames = tools.map((t) => t.name);
handlerNames.forEach((handlerName) => {
expect(toolNames).toContain(handlerName);
});
});
});
describe('End-to-End Workflows', () => {
describe('Async Command Workflow', () => {
it('should complete async command workflow', async () => {
// Mock execute_command response
mockClient.executeCommand.mockResolvedValue({
success: true,
taskId: 'task-123',
});
// Mock get_task_status responses
mockClient.getTaskStatus
.mockResolvedValueOnce({
success: true,
taskId: 'task-123',
status: 'running',
})
.mockResolvedValueOnce({
success: true,
taskId: 'task-123',
status: 'completed',
output: 'Command completed successfully',
exitCode: 0,
});
// Execute command
const executeResult = await toolHandlers.execute_command(mockClient, {
command: 'npm install',
async: true,
});
expect(executeResult.isError).toBe(false);
const executeData = JSON.parse(executeResult.content[0].text);
expect(executeData.success).toBe(true);
expect(executeData.taskId).toBe('task-123');
// Check running status
const runningStatus = await toolHandlers.get_task_status(mockClient, {
taskId: 'task-123',
});
expect(runningStatus.isError).toBe(false);
// Check final status
const finalStatus = await toolHandlers.get_task_status(mockClient, {
taskId: 'task-123',
});
expect(finalStatus.isError).toBe(false);
const finalData = JSON.parse(finalStatus.content[0].text);
expect(finalData.status).toBe('completed');
});
});
describe('Calendar and Notification Workflow', () => {
it('should create event and send notification', async () => {
// Mock createCalendarEvent
mockClient.createCalendarEvent.mockResolvedValue({
success: true,
eventId: 'event-456',
});
// Mock sendMessage
mockClient.sendMessage.mockResolvedValue({
success: true,
messageId: 'msg-789',
});
// Create event
const eventResult = await toolHandlers.create_calendar_event(mockClient, {
title: 'Team Meeting',
startTime: '2024-12-15T10:00:00Z',
endTime: '2024-12-15T11:00:00Z',
attendees: ['team@example.com'],
});
expect(eventResult.isError).toBe(false);
expect(mockClient.createCalendarEvent).toHaveBeenCalled();
// Send notification
const messageResult = await toolHandlers.send_message(mockClient, {
platform: 'telegram',
recipient: '@team',
message: 'Team meeting scheduled for Dec 15 at 10 AM',
});
expect(messageResult.isError).toBe(false);
expect(mockClient.sendMessage).toHaveBeenCalled();
});
});
describe('Batch Operations', () => {
it('should handle multiple message sends', async () => {
mockClient.sendMessage.mockResolvedValue({
success: true,
messageId: 'msg-batch',
});
const recipients = ['@user1', '@user2', '@user3'];
const promises = recipients.map((recipient) =>
toolHandlers.send_message(mockClient, {
platform: 'telegram',
recipient,
message: 'Batch message',
})
);
const results = await Promise.all(promises);
results.forEach((result) => {
expect(result.isError).toBe(false);
});
expect(mockClient.sendMessage).toHaveBeenCalledTimes(3);
});
});
});
describe('Error Scenarios', () => {
it('should handle client not initialized gracefully', () => {
// This would be tested in the server context
// Here we verify the behavior when client methods fail
mockClient.sendMessage.mockResolvedValue({
success: false,
error: 'Client not initialized',
});
const result = toolHandlers.send_message(mockClient, {
platform: 'telegram',
recipient: '@test',
message: 'Test',
});
return result.then((r) => {
expect(r.isError).toBe(true);
});
});
it('should handle unknown tool names', () => {
const unknownTool = 'unknown_tool';
const handler = toolHandlers[unknownTool as keyof typeof toolHandlers];
expect(handler).toBeUndefined();
});
});
describe('Tool Input Validation', () => {
it('should validate send_message inputs', async () => {
// Missing platform
let result = await toolHandlers.send_message(mockClient, {
platform: undefined as any,
recipient: '@test',
message: 'Test',
});
expect(result.isError).toBe(true);
// Empty recipient
result = await toolHandlers.send_message(mockClient, {
platform: 'telegram',
recipient: '',
message: 'Test',
});
expect(result.isError).toBe(true);
// Empty message
result = await toolHandlers.send_message(mockClient, {
platform: 'telegram',
recipient: '@test',
message: '',
});
expect(result.isError).toBe(true);
});
it('should validate execute_command inputs', async () => {
// Empty command
const result = await toolHandlers.execute_command(mockClient, {
command: '',
});
expect(result.isError).toBe(true);
// Invalid timeout
const timeoutResult = await toolHandlers.execute_command(mockClient, {
command: 'test',
timeout: 500,
});
expect(timeoutResult.isError).toBe(true);
});
it('should validate create_calendar_event inputs', async () => {
// Missing title
let result = await toolHandlers.create_calendar_event(mockClient, {
title: '',
startTime: '2024-12-15T10:00:00Z',
});
expect(result.isError).toBe(true);
// Invalid date format
result = await toolHandlers.create_calendar_event(mockClient, {
title: 'Test',
startTime: 'not-a-date',
});
expect(result.isError).toBe(true);
// End before start
result = await toolHandlers.create_calendar_event(mockClient, {
title: 'Test',
startTime: '2024-12-15T11:00:00Z',
endTime: '2024-12-15T10:00:00Z',
});
expect(result.isError).toBe(true);
});
it('should validate send_email inputs', async () => {
// Invalid email
let result = await toolHandlers.send_email(mockClient, {
to: 'not-an-email',
subject: 'Test',
body: 'Body',
});
expect(result.isError).toBe(true);
// Empty subject
result = await toolHandlers.send_email(mockClient, {
to: 'test@example.com',
subject: '',
body: 'Body',
});
expect(result.isError).toBe(true);
// Empty body
result = await toolHandlers.send_email(mockClient, {
to: 'test@example.com',
subject: 'Test',
body: '',
});
expect(result.isError).toBe(true);
});
it('should validate get_task_status inputs', async () => {
// Empty taskId
const result = await toolHandlers.get_task_status(mockClient, {
taskId: '',
});
expect(result.isError).toBe(true);
});
});
describe('Tool Schemas', () => {
it('should have valid schemas for all tools', () => {
tools.forEach((tool) => {
expect(tool.name).toBeDefined();
expect(tool.description).toBeDefined();
expect(tool.inputSchema).toBeDefined();
expect(tool.inputSchema.type).toBe('object');
expect(tool.inputSchema.properties).toBeDefined();
});
});
it('should have required fields defined in schemas', () => {
const sendMessageTool = tools.find((t) => t.name === 'send_message');
expect(sendMessageTool?.inputSchema.required).toContain('platform');
expect(sendMessageTool?.inputSchema.required).toContain('recipient');
expect(sendMessageTool?.inputSchema.required).toContain('message');
});
});
});