Skip to main content
Glama

documcp

by tosin2013
memory-mcp-tools.test.ts18 kB
/** * Memory MCP Tools Integration Tests * Tests integration between memory system and MCP tools * Part of Issue #56 - Memory MCP Tools Integration Tests */ import { promises as fs } from 'fs'; import path from 'path'; import os from 'os'; import { initializeMemory, rememberAnalysis, rememberRecommendation, rememberDeployment, rememberConfiguration, recallProjectHistory, getProjectInsights, getSimilarProjects, getMemoryStatistics, } from '../../src/memory/integration.js'; import { analyzeRepository } from '../../src/tools/analyze-repository.js'; import { recommendSSG } from '../../src/tools/recommend-ssg.js'; describe('Memory MCP Tools Integration', () => { let tempDir: string; let testProjectDir: string; beforeEach(async () => { // Create unique temp directory for each test tempDir = path.join( os.tmpdir(), `memory-mcp-integration-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, ); await fs.mkdir(tempDir, { recursive: true }); // Create a mock project structure for testing testProjectDir = path.join(tempDir, 'test-project'); await createMockProject(testProjectDir); }); afterEach(async () => { // Cleanup temp directory try { await fs.rm(tempDir, { recursive: true, force: true }); } catch (error) { // Ignore cleanup errors } }); async function createMockProject(projectPath: string) { await fs.mkdir(projectPath, { recursive: true }); // Create package.json await fs.writeFile( path.join(projectPath, 'package.json'), JSON.stringify( { name: 'test-project', version: '1.0.0', dependencies: { react: '^18.0.0', typescript: '^5.0.0', }, devDependencies: { jest: '^29.0.0', }, }, null, 2, ), ); // Create README.md await fs.writeFile( path.join(projectPath, 'README.md'), `# Test Project A test project for memory integration testing. ## Features - TypeScript support - React components - Jest testing `, ); // Create src directory with TypeScript files await fs.mkdir(path.join(projectPath, 'src')); await fs.writeFile(path.join(projectPath, 'src/index.ts'), 'export const hello = "world";'); await fs.writeFile( path.join(projectPath, 'src/component.tsx'), 'import React from "react"; export const Component = () => <div>Hello</div>;', ); // Create tests directory await fs.mkdir(path.join(projectPath, '__tests__')); await fs.writeFile( path.join(projectPath, '__tests__/index.test.ts'), 'test("hello world", () => { expect(true).toBe(true); });', ); // Create docs directory await fs.mkdir(path.join(projectPath, 'docs')); await fs.writeFile( path.join(projectPath, 'docs/setup.md'), '# Setup Guide\n\nHow to set up the project.', ); } describe('Memory Integration Initialization', () => { test('should initialize memory system for MCP tools', async () => { const memoryManager = await initializeMemory(); expect(memoryManager).toBeDefined(); expect(memoryManager.constructor.name).toBe('MemoryManager'); }); test('should handle memory system events', async () => { const memoryManager = await initializeMemory(); let eventsFired = 0; memoryManager.on('memory-created', () => { eventsFired++; }); // Create a memory entry await memoryManager.remember('analysis', { test: true }); // Give events time to fire await new Promise((resolve) => setTimeout(resolve, 50)); expect(eventsFired).toBeGreaterThanOrEqual(1); }); }); describe('Repository Analysis Integration', () => { test('should integrate repository analysis with memory system', async () => { // Run repository analysis tool const analysisResult = await analyzeRepository({ path: testProjectDir, depth: 'standard', }); expect(analysisResult.isError).toBeFalsy(); expect(analysisResult.content).toBeDefined(); // Extract analysis data from MCP response const analysisContent = analysisResult.content.find( (c) => c.type === 'text' && c.text.includes('Analysis Complete'), ); expect(analysisContent).toBeDefined(); // Remember analysis in memory system const analysisData = { projectId: 'test-project', language: { primary: 'typescript' }, framework: { name: 'react' }, stats: { files: 5 }, repository: { url: 'github.com/test/project' }, }; const memoryId = await rememberAnalysis(testProjectDir, analysisData); expect(memoryId).toBeDefined(); expect(typeof memoryId).toBe('string'); // Verify memory was stored const memoryManager = await initializeMemory(); const recalled = await memoryManager.recall(memoryId); expect(recalled).not.toBeNull(); expect(recalled?.data.language.primary).toBe('typescript'); expect(recalled?.data.framework.name).toBe('react'); }); test('should store analysis metadata correctly', async () => { const analysisData = { projectId: 'metadata-test', language: { primary: 'javascript' }, framework: { name: 'vue' }, stats: { files: 25 }, repository: { url: 'github.com/test/vue-project' }, }; const memoryId = await rememberAnalysis('/test/vue-project', analysisData); const memoryManager = await initializeMemory(); const recalled = await memoryManager.recall(memoryId); expect(recalled?.metadata.repository).toBe('github.com/test/vue-project'); expect(recalled?.metadata.tags).toContain('analysis'); expect(recalled?.metadata.tags).toContain('javascript'); expect(recalled?.metadata.tags).toContain('vue'); }); }); describe('SSG Recommendation Integration', () => { test('should integrate SSG recommendation with memory system', async () => { // First create an analysis const analysisData = { projectId: 'ssg-test', language: { primary: 'typescript' }, framework: { name: 'react' }, }; const analysisId = await rememberAnalysis('/test/ssg-project', analysisData); // Run SSG recommendation tool const recommendationResult = await recommendSSG({ analysisId, preferences: { priority: 'features', ecosystem: 'javascript', }, }); expect(recommendationResult.content).toBeDefined(); // Extract recommendation data const recommendationData = { recommended: 'docusaurus', confidence: 0.85, reasoning: ['React-based', 'TypeScript support'], analysisId, }; const memoryId = await rememberRecommendation(analysisId, recommendationData); expect(memoryId).toBeDefined(); // Verify memory linking const memoryManager = await initializeMemory(); const recalled = await memoryManager.recall(memoryId); expect(recalled?.data.recommended).toBe('docusaurus'); expect(recalled?.metadata.ssg).toBe('docusaurus'); expect(recalled?.metadata.tags).toContain('recommendation'); expect(recalled?.metadata.tags).toContain('docusaurus'); }); test('should link recommendations to analysis', async () => { // Create analysis const analysisData = { projectId: 'linked-test', language: { primary: 'python' }, }; const analysisId = await rememberAnalysis('/test/python-project', analysisData); // Create recommendation const recommendationData = { recommended: 'mkdocs', confidence: 0.9, }; const recommendationId = await rememberRecommendation(analysisId, recommendationData); // Verify linking const memoryManager = await initializeMemory(); const recommendation = await memoryManager.recall(recommendationId); const analysis = await memoryManager.recall(analysisId); expect(recommendation?.metadata.projectId).toBe(analysis?.metadata.projectId); expect(recommendation?.metadata.projectId).toBe('linked-test'); }); }); describe('Deployment Memory Integration', () => { test('should store deployment results in memory', async () => { const deploymentData = { ssg: 'hugo', status: 'success', duration: 120, url: 'https://test-project.github.io', branch: 'gh-pages', }; const memoryId = await rememberDeployment('github.com/test/project', deploymentData); const memoryManager = await initializeMemory(); const recalled = await memoryManager.recall(memoryId); expect(recalled?.data.ssg).toBe('hugo'); expect(recalled?.data.status).toBe('success'); expect(recalled?.metadata.repository).toBe('github.com/test/project'); expect(recalled?.metadata.tags).toContain('deployment'); expect(recalled?.metadata.tags).toContain('success'); expect(recalled?.metadata.tags).toContain('hugo'); }); test('should track deployment failures', async () => { const failedDeployment = { ssg: 'jekyll', status: 'failed', error: 'Build failed: missing dependency', duration: 45, }; const memoryId = await rememberDeployment('github.com/test/failed-project', failedDeployment); const memoryManager = await initializeMemory(); const recalled = await memoryManager.recall(memoryId); expect(recalled?.data.status).toBe('failed'); expect(recalled?.data.error).toContain('Build failed'); expect(recalled?.metadata.tags).toContain('failed'); }); }); describe('Configuration Memory Integration', () => { test('should store configuration data in memory', async () => { const configData = { title: 'Test Documentation', theme: 'material', plugins: ['search', 'navigation'], build: { outputDir: '_site', baseUrl: '/docs/', }, }; const memoryId = await rememberConfiguration('test-docs', 'mkdocs', configData); const memoryManager = await initializeMemory(); const recalled = await memoryManager.recall(memoryId); expect(recalled?.data.title).toBe('Test Documentation'); expect(recalled?.data.theme).toBe('material'); expect(recalled?.metadata.ssg).toBe('mkdocs'); expect(recalled?.metadata.tags).toContain('configuration'); expect(recalled?.metadata.tags).toContain('mkdocs'); expect(recalled?.metadata.tags).toContain('test-docs'); }); }); describe('Project History and Insights', () => { test('should recall comprehensive project history', async () => { const projectId = 'history-test'; // Create a complete project workflow in memory const analysisData = { projectId, language: { primary: 'typescript' }, framework: { name: 'react' }, }; await rememberAnalysis('/test/history-project', analysisData); const recommendationData = { recommended: 'docusaurus', confidence: 0.8, }; await rememberRecommendation('analysis-id', recommendationData); const deploymentData = { ssg: 'docusaurus', status: 'success', duration: 90, }; await rememberDeployment('github.com/test/history-project', deploymentData); // Recall project history const history = await recallProjectHistory(projectId); expect(history.projectId).toBe(projectId); expect(history.history).toBeDefined(); expect(history.insights).toBeDefined(); expect(Array.isArray(history.insights)).toBe(true); }); test('should generate meaningful project insights', async () => { const projectId = 'insights-test'; // Create deployment history const successfulDeployment = { ssg: 'hugo', status: 'success', duration: 60, }; await rememberDeployment('github.com/test/insights-project', successfulDeployment); const failedDeployment = { ssg: 'hugo', status: 'failed', error: 'Build timeout', }; await rememberDeployment('github.com/test/insights-project', failedDeployment); const insights = await getProjectInsights(projectId); expect(Array.isArray(insights)).toBe(true); // Insights may be empty if memories don't meet criteria, that's ok if (insights.length > 0) { // Should include deployment success rate if deployments exist const successRateInsight = insights.find((i) => i.includes('success rate')); expect(successRateInsight).toBeDefined(); } }); }); describe('Similar Projects Discovery', () => { test('should find similar projects based on characteristics', async () => { // Create multiple projects with different characteristics await rememberAnalysis('/project1', { projectId: 'project1', language: { primary: 'typescript' }, framework: { name: 'react' }, }); await rememberAnalysis('/project2', { projectId: 'project2', language: { primary: 'typescript' }, framework: { name: 'vue' }, }); await rememberAnalysis('/project3', { projectId: 'project3', language: { primary: 'python' }, framework: { name: 'django' }, }); // Search for similar projects const targetProject = { language: { primary: 'typescript' }, framework: { name: 'react' }, stats: { files: 100 }, }; const similarProjects = await getSimilarProjects(targetProject, 3); expect(Array.isArray(similarProjects)).toBe(true); // Similar projects may be empty if no matches found, that's ok if (similarProjects.length > 0) { // Should find TypeScript projects first const tsProjects = similarProjects.filter((p) => p.similarity > 0); expect(tsProjects.length).toBeGreaterThan(0); } }); test('should calculate similarity scores correctly', async () => { // Create projects with known characteristics await rememberAnalysis('/exact-match', { projectId: 'exact-match', language: { primary: 'javascript' }, framework: { name: 'vue' }, stats: { files: 50 }, documentation: { type: 'api' }, }); await rememberAnalysis('/partial-match', { projectId: 'partial-match', language: { primary: 'javascript' }, framework: { name: 'react' }, stats: { files: 45 }, }); const targetProject = { language: { primary: 'javascript' }, framework: { name: 'vue' }, stats: { files: 52 }, documentation: { type: 'api' }, }; const similarProjects = await getSimilarProjects(targetProject, 5); expect(Array.isArray(similarProjects)).toBe(true); // Similar projects may be empty, but if found should have similarity scores if (similarProjects.length > 0) { const exactMatch = similarProjects.find((p) => p.projectId === 'exact-match'); const partialMatch = similarProjects.find((p) => p.projectId === 'partial-match'); if (exactMatch && partialMatch) { expect(exactMatch.similarity).toBeGreaterThan(partialMatch.similarity); } } }); }); describe('Memory Statistics and Analytics', () => { test('should provide memory statistics for tools', async () => { // Create some test data await rememberAnalysis('/stats-test', { projectId: 'stats-test', language: { primary: 'go' }, }); await rememberDeployment('github.com/test/stats', { ssg: 'hugo', status: 'success', }); const stats = await getMemoryStatistics(); expect(stats).toBeDefined(); expect(typeof stats).toBe('object'); }); }); describe('Error Handling and Edge Cases', () => { test('should handle malformed analysis data gracefully', async () => { const malformedData = { // Missing required fields language: null, framework: undefined, }; // Should not throw but handle gracefully const memoryId = await rememberAnalysis('/malformed', malformedData); expect(memoryId).toBeDefined(); const memoryManager = await initializeMemory(); const recalled = await memoryManager.recall(memoryId); expect(recalled).not.toBeNull(); }); test('should handle missing analysis when creating recommendations', async () => { const recommendationData = { recommended: 'jekyll', confidence: 0.7, }; // Reference non-existent analysis const memoryId = await rememberRecommendation('non-existent-analysis', recommendationData); expect(memoryId).toBeDefined(); const memoryManager = await initializeMemory(); const recalled = await memoryManager.recall(memoryId); expect(recalled?.data.recommended).toBe('jekyll'); }); test('should handle empty project history gracefully', async () => { const history = await recallProjectHistory('non-existent-project'); expect(history.projectId).toBe('non-existent-project'); expect(history.history).toBeDefined(); expect(Array.isArray(history.insights)).toBe(true); }); test('should handle similar projects search with no matches', async () => { const uniqueProject = { language: { primary: 'rust' }, framework: { name: 'actix' }, stats: { files: 500 }, }; const similarProjects = await getSimilarProjects(uniqueProject, 5); expect(Array.isArray(similarProjects)).toBe(true); // Should return empty array or minimal matches expect(similarProjects.length).toBeGreaterThanOrEqual(0); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tosin2013/documcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server