Skip to main content
Glama

documcp

by tosin2013
knowledge-graph-workflow.test.ts13.3 kB
/** * Integration Tests for Knowledge Graph Workflow * Phase 1: End-to-End KG-Analysis Integration */ import { describe, it, expect, beforeEach, afterEach } from "@jest/globals"; import { promises as fs } from "fs"; import { join } from "path"; import { tmpdir } from "os"; import { initializeKnowledgeGraph, getKnowledgeGraph, saveKnowledgeGraph, createOrUpdateProject, getProjectContext, trackDeployment, getKGStatistics, } from "../../src/memory/kg-integration.js"; describe("Knowledge Graph Workflow Integration", () => { let testDir: string; let originalEnv: string | undefined; beforeEach(async () => { // Create temporary test directory testDir = join(tmpdir(), `kg-workflow-test-${Date.now()}`); await fs.mkdir(testDir, { recursive: true }); // Set environment variable for storage originalEnv = process.env.DOCUMCP_STORAGE_DIR; process.env.DOCUMCP_STORAGE_DIR = testDir; // Initialize KG await initializeKnowledgeGraph(testDir); }); afterEach(async () => { // Restore environment if (originalEnv) { process.env.DOCUMCP_STORAGE_DIR = originalEnv; } else { delete process.env.DOCUMCP_STORAGE_DIR; } // Clean up test directory try { await fs.rm(testDir, { recursive: true, force: true }); } catch (error) { console.warn("Failed to clean up test directory:", error); } }); describe("Complete Analysis Workflow", () => { it("should handle first-time project analysis", async () => { const analysis = { id: "analysis_001", timestamp: new Date().toISOString(), path: "/test/project", projectName: "Test Project", structure: { totalFiles: 100, totalDirectories: 10, languages: { typescript: 60, javascript: 30, json: 10, }, hasTests: true, hasCI: true, hasDocs: false, }, }; // Create project in KG const projectNode = await createOrUpdateProject(analysis); expect(projectNode).toBeDefined(); expect(projectNode.type).toBe("project"); expect(projectNode.properties.name).toBe("Test Project"); expect(projectNode.properties.analysisCount).toBe(1); // Get context (should be empty for first analysis) const context = await getProjectContext("/test/project"); expect(context.previousAnalyses).toBe(1); expect(context.knownTechnologies).toContain("typescript"); }); it("should track returning project with historical context", async () => { const analysis1 = { id: "analysis_001", timestamp: new Date().toISOString(), path: "/test/project", projectName: "Test Project", structure: { totalFiles: 100, languages: { typescript: 60, javascript: 40 }, hasTests: true, hasCI: false, hasDocs: false, }, }; const analysis2 = { id: "analysis_002", timestamp: new Date().toISOString(), path: "/test/project", projectName: "Test Project", structure: { totalFiles: 120, languages: { typescript: 80, javascript: 40 }, hasTests: true, hasCI: true, hasDocs: true, }, }; // First analysis await createOrUpdateProject(analysis1); // Second analysis const projectNode = await createOrUpdateProject(analysis2); expect(projectNode.properties.analysisCount).toBe(2); // Get context const context = await getProjectContext("/test/project"); expect(context.previousAnalyses).toBe(2); expect(context.lastAnalyzed).toBeDefined(); }); it("should find similar projects based on technologies", async () => { // Create multiple projects with shared technologies const project1 = { id: "analysis_001", timestamp: new Date().toISOString(), path: "/test/project1", projectName: "React App", structure: { totalFiles: 50, languages: { typescript: 30, javascript: 20 }, hasTests: true, hasCI: false, hasDocs: false, }, }; const project2 = { id: "analysis_002", timestamp: new Date().toISOString(), path: "/test/project2", projectName: "Another React App", structure: { totalFiles: 75, languages: { typescript: 50, javascript: 25 }, hasTests: true, hasCI: true, hasDocs: false, }, }; const project3 = { id: "analysis_003", timestamp: new Date().toISOString(), path: "/test/project3", projectName: "Python Project", structure: { totalFiles: 40, languages: { python: 40 }, hasTests: true, hasCI: false, hasDocs: false, }, }; // Create all projects await createOrUpdateProject(project1); await createOrUpdateProject(project2); await createOrUpdateProject(project3); // Get context for project1 const context = await getProjectContext("/test/project1"); // Should find project2 as similar (shares typescript/javascript) // Should not find project3 (uses different stack) expect(context.similarProjects.length).toBeGreaterThan(0); const similarProject = context.similarProjects.find( (p) => p.properties.name === "Another React App", ); expect(similarProject).toBeDefined(); }); }); describe("Deployment Tracking Workflow", () => { it("should track successful deployment", async () => { // Create project const analysis = { id: "project_001", timestamp: new Date().toISOString(), path: "/test/project", projectName: "Test Project", structure: { totalFiles: 50, languages: { typescript: 50 }, hasTests: true, hasCI: false, hasDocs: false, }, }; const projectNode = await createOrUpdateProject(analysis); // Track successful deployment await trackDeployment(projectNode.id, "docusaurus", true, { buildTime: 45, deploymentUrl: "https://test.github.io", }); // Verify deployment was tracked const kg = await getKnowledgeGraph(); const edges = await kg.findEdges({ source: projectNode.id, properties: { baseType: "project_deployed_with" }, }); expect(edges.length).toBeGreaterThan(0); expect(edges[0].properties.success).toBe(true); expect(edges[0].properties.buildTime).toBe(45); }); it("should track failed deployment", async () => { const analysis = { id: "project_002", timestamp: new Date().toISOString(), path: "/test/project2", projectName: "Test Project 2", structure: { totalFiles: 50, languages: { javascript: 50 }, hasTests: false, hasCI: false, hasDocs: false, }, }; const projectNode = await createOrUpdateProject(analysis); // Track failed deployment await trackDeployment(projectNode.id, "jekyll", false, { errorMessage: "Ruby version mismatch", }); const kg = await getKnowledgeGraph(); const edges = await kg.findEdges({ source: projectNode.id, properties: { baseType: "project_deployed_with" }, }); expect(edges.length).toBeGreaterThan(0); expect(edges[0].properties.success).toBe(false); expect(edges[0].properties.errorMessage).toContain("Ruby version"); }); it("should update configuration success rate over time", async () => { const analysis = { id: "project_003", timestamp: new Date().toISOString(), path: "/test/project3", projectName: "Test Project 3", structure: { totalFiles: 50, languages: { typescript: 50 }, hasTests: false, hasCI: false, hasDocs: false, }, }; const projectNode = await createOrUpdateProject(analysis); // Track multiple deployments await trackDeployment(projectNode.id, "hugo", true); await trackDeployment(projectNode.id, "hugo", true); await trackDeployment(projectNode.id, "hugo", false); const kg = await getKnowledgeGraph(); const configNode = await kg.findNode({ type: "configuration", properties: { ssg: "hugo" }, }); expect(configNode).toBeDefined(); expect(configNode!.properties.usageCount).toBe(3); // Success rate: 2/3 = 0.666... expect(configNode!.properties.deploymentSuccessRate).toBeCloseTo( 0.666, 2, ); }); }); describe("Knowledge Graph Statistics", () => { it("should return accurate statistics", async () => { // Create multiple projects for (let i = 0; i < 5; i++) { const analysis = { id: `project_00${i}`, timestamp: new Date().toISOString(), path: `/test/project${i}`, projectName: `Project ${i}`, structure: { totalFiles: 50, languages: { typescript: 30, javascript: 20 }, hasTests: true, hasCI: false, hasDocs: false, }, }; await createOrUpdateProject(analysis); } const stats = await getKGStatistics(); expect(stats.projectCount).toBe(5); expect(stats.technologyCount).toBeGreaterThan(0); expect(stats.nodeCount).toBeGreaterThan(5); // Projects + technologies expect(stats.edgeCount).toBeGreaterThan(0); // project_uses_technology edges }); }); describe("Persistence Workflow", () => { it("should persist data across sessions", async () => { const analysis = { id: "persistent_project", timestamp: new Date().toISOString(), path: "/test/persistent", projectName: "Persistent Project", structure: { totalFiles: 50, languages: { typescript: 50 }, hasTests: true, hasCI: false, hasDocs: false, }, }; // Create project and save await createOrUpdateProject(analysis); await saveKnowledgeGraph(); // Reinitialize (simulating new session) await initializeKnowledgeGraph(testDir); // Verify data was loaded const context = await getProjectContext("/test/persistent"); expect(context.previousAnalyses).toBe(1); expect(context.knownTechnologies).toContain("typescript"); }); }); describe("Complex Multi-Step Workflow", () => { it("should handle complete project lifecycle", async () => { // Step 1: Initial analysis const initialAnalysis = { id: "lifecycle_project", timestamp: new Date().toISOString(), path: "/test/lifecycle", projectName: "Lifecycle Project", structure: { totalFiles: 30, languages: { javascript: 30 }, hasTests: false, hasCI: false, hasDocs: false, }, }; const project1 = await createOrUpdateProject(initialAnalysis); expect(project1.properties.analysisCount).toBe(1); // Step 2: Track deployment attempt (failed) await trackDeployment(project1.id, "jekyll", false, { errorMessage: "Missing dependencies", }); // Step 3: Re-analysis after fixes const updatedAnalysis = { ...initialAnalysis, id: "lifecycle_project_2", timestamp: new Date().toISOString(), structure: { totalFiles: 35, languages: { javascript: 30, json: 5 }, hasTests: true, hasCI: true, hasDocs: true, }, }; const project2 = await createOrUpdateProject(updatedAnalysis); expect(project2.properties.analysisCount).toBe(2); expect(project2.properties.hasCI).toBe(true); // Step 4: Successful deployment await trackDeployment(project2.id, "eleventy", true, { buildTime: 30, deploymentUrl: "https://lifecycle.github.io", }); // Verify complete lifecycle const kg = await getKnowledgeGraph(); // Check project node const projectNode = await kg.findNode({ type: "project", properties: { path: "/test/lifecycle" }, }); expect(projectNode).toBeDefined(); expect(projectNode!.properties.analysisCount).toBe(2); // Check deployments const deployments = await kg.findEdges({ source: projectNode!.id, properties: { baseType: "project_deployed_with" }, }); expect(deployments).toHaveLength(2); // Check technologies const techEdges = await kg.findEdges({ source: projectNode!.id, type: "project_uses_technology", }); expect(techEdges.length).toBeGreaterThan(0); // Get final context const context = await getProjectContext("/test/lifecycle"); expect(context.previousAnalyses).toBe(2); expect(context.knownTechnologies).toContain("javascript"); }); }); });

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