import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { registerTools } from '../tools/index.js';
import { ProgressStore } from '../storage/index.js';
import { Summariser } from '../summariser/index.js';
import fs from 'fs';
import path from 'path';
import os from 'os';
describe('integration', () => {
let store: ProgressStore;
let summariser: Summariser;
let server: McpServer;
let testDbPath: string;
let mockSummarise: ReturnType<typeof vi.fn>;
beforeEach(() => {
testDbPath = path.join(os.tmpdir(), `test-progress-${Date.now()}.db`);
store = new ProgressStore(testDbPath);
// Create a mock summariser
mockSummarise = vi.fn().mockResolvedValue({
summary: 'Integration test summary',
isFallback: false,
});
summariser = {
summarise: mockSummarise,
} as unknown as Summariser;
server = new McpServer({
name: 'agent-progress-mcp',
version: '1.0.0',
});
registerTools(server, store, summariser);
});
afterEach(() => {
store.close();
if (fs.existsSync(testDbPath)) {
fs.unlinkSync(testDbPath);
}
if (fs.existsSync(`${testDbPath}-wal`)) {
fs.unlinkSync(`${testDbPath}-wal`);
}
if (fs.existsSync(`${testDbPath}-shm`)) {
fs.unlinkSync(`${testDbPath}-shm`);
}
});
it('server starts and exposes three tools', async () => {
// The server should have registered three tools
// We verify this by checking the tools are accessible
// In the actual MCP SDK, tools are registered on the server instance
// For now, we verify the server was created successfully
expect(server).toBeDefined();
// The registerTools function should have registered:
// - log_progress
// - get_context
// - search_logs
// This is verified by the server not throwing during registration
});
it('full workflow — log → search → get_context', async () => {
// Step 1: Log progress
const logResult = await callTool(server, 'log_progress', {
projectId: 'integration-test',
title: 'Test Entry for Workflow',
content: 'This is the full content for the integration test workflow.',
tags: ['test', 'integration'],
});
expect(logResult.isError).toBeFalsy();
const entryId = logResult.structuredContent.id;
expect(entryId).toHaveLength(12);
// Step 2: Search logs
const searchResult = await callTool(server, 'search_logs', {
projectId: 'integration-test',
query: 'Workflow',
});
expect(searchResult.isError).toBeFalsy();
expect(searchResult.structuredContent.entries.length).toBe(1);
expect(searchResult.structuredContent.entries[0].id).toBe(entryId);
// Step 3: Get context
const contextResult = await callTool(server, 'get_context', {
projectId: 'integration-test',
id: entryId,
});
expect(contextResult.isError).toBeFalsy();
expect(contextResult.structuredContent.summary).toBe('Integration test summary');
expect(mockSummarise).toHaveBeenCalledTimes(1);
});
it('summary cached after first get_context', async () => {
// Log an entry
const logResult = await callTool(server, 'log_progress', {
projectId: 'cache-test',
title: 'Cache Test Entry',
content: 'Content for cache testing.',
});
const entryId = logResult.structuredContent.id;
// First get_context call
await callTool(server, 'get_context', {
projectId: 'cache-test',
id: entryId,
});
expect(mockSummarise).toHaveBeenCalledTimes(1);
// Second get_context call
await callTool(server, 'get_context', {
projectId: 'cache-test',
id: entryId,
});
// Should still be 1 - no additional LLM call
expect(mockSummarise).toHaveBeenCalledTimes(1);
});
});
// Helper function to simulate calling a tool
// In actual implementation, this would go through the MCP protocol
async function callTool(
server: McpServer,
toolName: string,
args: Record<string, unknown>
): Promise<{
isError?: boolean;
content: Array<{ type: string; text: string }>;
structuredContent: Record<string, unknown>;
}> {
// This is a simplified simulation of the tool call
// The actual implementation would use the MCP SDK's tool invocation mechanism
// For testing purposes, we access the registered tools directly
// Get the tool handler from the server's internal registry
// @ts-expect-error - accessing internal for testing
const tools = server._registeredTools || server.tools || {};
const tool = tools[toolName];
if (!tool) {
throw new Error(`Tool ${toolName} not found`);
}
return await tool.callback(args);
}