Skip to main content
Glama

documcp

by tosin2013
knowledge-graph.test.ts12.1 kB
/** * Basic unit tests for Knowledge Graph System * Tests basic instantiation and core functionality * Part of Issue #54 - Core Memory System Unit Tests */ import { promises as fs } from "fs"; import path from "path"; import os from "os"; import { MemoryManager } from "../../src/memory/manager.js"; import { KnowledgeGraph, GraphNode, GraphEdge, } from "../../src/memory/knowledge-graph.js"; describe("KnowledgeGraph", () => { let tempDir: string; let memoryManager: MemoryManager; let graph: KnowledgeGraph; beforeEach(async () => { // Create unique temp directory for each test tempDir = path.join( os.tmpdir(), `memory-graph-test-${Date.now()}-${Math.random() .toString(36) .substr(2, 9)}`, ); await fs.mkdir(tempDir, { recursive: true }); // Create memory manager for knowledge graph memoryManager = new MemoryManager(tempDir); await memoryManager.initialize(); graph = new KnowledgeGraph(memoryManager); await graph.initialize(); }); afterEach(async () => { // Cleanup temp directory try { await fs.rm(tempDir, { recursive: true, force: true }); } catch (error) { // Ignore cleanup errors } }); describe("Basic Graph Operations", () => { test("should create knowledge graph instance", () => { expect(graph).toBeDefined(); expect(graph).toBeInstanceOf(KnowledgeGraph); }); test("should add nodes to the graph", () => { const projectNode: Omit<GraphNode, "lastUpdated"> = { id: "project:test-project", type: "project", label: "Test Project", properties: { language: "typescript", framework: "react", }, weight: 1.0, }; const addedNode = graph.addNode(projectNode); expect(addedNode).toBeDefined(); expect(addedNode.id).toBe("project:test-project"); expect(addedNode.type).toBe("project"); expect(addedNode.lastUpdated).toBeDefined(); }); test("should add edges to the graph", () => { // First add nodes const projectNode = graph.addNode({ id: "project:web-app", type: "project", label: "Web App", properties: { language: "typescript" }, weight: 1.0, }); const techNode = graph.addNode({ id: "tech:react", type: "technology", label: "React", properties: { category: "framework" }, weight: 1.0, }); // Add edge const edge: Omit<GraphEdge, "id" | "lastUpdated"> = { source: projectNode.id, target: techNode.id, type: "uses", weight: 1.0, confidence: 0.9, properties: { importance: "high" }, }; const addedEdge = graph.addEdge(edge); expect(addedEdge).toBeDefined(); expect(addedEdge.source).toBe(projectNode.id); expect(addedEdge.target).toBe(techNode.id); expect(addedEdge.id).toBeDefined(); }); test("should get all nodes", async () => { // Add some nodes graph.addNode({ id: "project:test1", type: "project", label: "Test 1", properties: {}, weight: 1.0, }); graph.addNode({ id: "tech:vue", type: "technology", label: "Vue", properties: {}, weight: 1.0, }); const nodes = await graph.getAllNodes(); expect(Array.isArray(nodes)).toBe(true); expect(nodes.length).toBe(2); }); test("should get all edges", async () => { // Add nodes and edges const node1 = graph.addNode({ id: "project:test2", type: "project", label: "Test 2", properties: {}, weight: 1.0, }); const node2 = graph.addNode({ id: "tech:angular", type: "technology", label: "Angular", properties: {}, weight: 1.0, }); graph.addEdge({ source: node1.id, target: node2.id, type: "uses", weight: 1.0, confidence: 0.8, properties: {}, }); const edges = await graph.getAllEdges(); expect(Array.isArray(edges)).toBe(true); expect(edges.length).toBe(1); }); }); describe("Graph Queries", () => { test("should query nodes by type", () => { // Add multiple nodes of different types graph.addNode({ id: "project:project-a", type: "project", label: "Project A", properties: {}, weight: 1.0, }); graph.addNode({ id: "project:project-b", type: "project", label: "Project B", properties: {}, weight: 1.0, }); graph.addNode({ id: "tech:vue", type: "technology", label: "Vue", properties: { category: "framework" }, weight: 1.0, }); const results = graph.query({ nodeTypes: ["project"], }); expect(results).toBeDefined(); expect(Array.isArray(results.nodes)).toBe(true); expect(results.nodes.length).toBe(2); expect(results.nodes.every((node) => node.type === "project")).toBe(true); }); test("should find connections for a node", async () => { // Add nodes and create connections const projectNode = graph.addNode({ id: "project:connected-test", type: "project", label: "Connected Test", properties: {}, weight: 1.0, }); const techNode = graph.addNode({ id: "tech:express", type: "technology", label: "Express", properties: {}, weight: 1.0, }); graph.addEdge({ source: projectNode.id, target: techNode.id, type: "uses", weight: 1.0, confidence: 0.9, properties: {}, }); const connections = await graph.getConnections(projectNode.id); expect(Array.isArray(connections)).toBe(true); expect(connections.length).toBe(1); expect(connections[0]).toBe(techNode.id); }); test("should find paths between nodes", () => { // Add nodes and create a path const projectNode = graph.addNode({ id: "project:path-test", type: "project", label: "Path Test Project", properties: {}, weight: 1.0, }); const techNode = graph.addNode({ id: "tech:nodejs", type: "technology", label: "Node.js", properties: {}, weight: 1.0, }); graph.addEdge({ source: projectNode.id, target: techNode.id, type: "uses", weight: 1.0, confidence: 0.9, properties: {}, }); const path = graph.findPath(projectNode.id, techNode.id); expect(path).toBeDefined(); expect(path?.nodes.length).toBe(2); expect(path?.edges.length).toBe(1); }); }); describe("Graph Analysis", () => { test("should build from memory entries", async () => { // Add some test memory entries first await memoryManager.remember( "analysis", { language: { primary: "python" }, framework: { name: "django" }, }, { projectId: "analysis-project", }, ); await memoryManager.remember( "recommendation", { recommended: "mkdocs", confidence: 0.9, }, { projectId: "analysis-project", }, ); // Build graph from memories await graph.buildFromMemories(); const nodes = await graph.getAllNodes(); // The buildFromMemories method might be implemented differently // Just verify it doesn't throw and returns an array expect(Array.isArray(nodes)).toBe(true); // The graph might start empty, which is okay for this basic test if (nodes.length > 0) { // Optionally check node types if any were created const nodeTypes = [...new Set(nodes.map((n) => n.type))]; expect(nodeTypes.length).toBeGreaterThan(0); } }); test("should generate graph-based recommendations", async () => { // Add some memory data first await memoryManager.remember( "analysis", { language: { primary: "javascript" }, framework: { name: "react" }, }, { projectId: "rec-test-project", }, ); await graph.buildFromMemories(); const projectFeatures = { language: "javascript", framework: "react", }; const recommendations = await graph.getGraphBasedRecommendation( projectFeatures, ["docusaurus", "gatsby"], ); expect(Array.isArray(recommendations)).toBe(true); // Even if no recommendations found, should return empty array }); test("should provide graph statistics", async () => { // Add some nodes graph.addNode({ id: "project:stats-test", type: "project", label: "Stats Test", properties: {}, weight: 1.0, }); graph.addNode({ id: "tech:webpack", type: "technology", label: "Webpack", properties: {}, weight: 1.0, }); const stats = await graph.getStatistics(); expect(stats).toBeDefined(); expect(typeof stats.nodeCount).toBe("number"); expect(typeof stats.edgeCount).toBe("number"); expect(typeof stats.nodesByType).toBe("object"); expect(typeof stats.averageConnectivity).toBe("number"); expect(Array.isArray(stats.mostConnectedNodes)).toBe(true); }); }); describe("Error Handling", () => { test("should handle removing non-existent nodes", async () => { const removed = await graph.removeNode("non-existent-node"); expect(removed).toBe(false); }); test("should handle concurrent graph operations", () => { // Create multiple nodes concurrently const nodes = Array.from({ length: 10 }, (_, i) => graph.addNode({ id: `project:concurrent-${i}`, type: "project", label: `Concurrent Project ${i}`, properties: { index: i }, weight: 1.0, }), ); expect(nodes).toHaveLength(10); expect(nodes.every((node) => typeof node.id === "string")).toBe(true); }); test("should handle invalid query parameters", () => { const results = graph.query({ nodeTypes: ["non-existent-type"], }); expect(results).toBeDefined(); expect(Array.isArray(results.nodes)).toBe(true); expect(results.nodes.length).toBe(0); }); test("should handle empty graph operations", async () => { // Test operations on empty graph const path = graph.findPath("non-existent-1", "non-existent-2"); expect(path).toBeNull(); const connections = await graph.getConnections("non-existent-node"); expect(Array.isArray(connections)).toBe(true); expect(connections.length).toBe(0); }); }); describe("Persistence and Memory Integration", () => { test("should save and load from memory", async () => { // Add some data to the graph graph.addNode({ id: "project:persistence-test", type: "project", label: "Persistence Test", properties: {}, weight: 1.0, }); // Save to memory await graph.saveToMemory(); // Create new graph and load const newGraph = new KnowledgeGraph(memoryManager); await newGraph.loadFromMemory(); const nodes = await newGraph.getAllNodes(); expect(nodes.length).toBeGreaterThanOrEqual(0); }); test("should handle empty graph statistics", async () => { const stats = await graph.getStatistics(); expect(stats).toBeDefined(); expect(typeof stats.nodeCount).toBe("number"); expect(typeof stats.edgeCount).toBe("number"); expect(stats.nodeCount).toBe(0); // Empty graph initially expect(stats.edgeCount).toBe(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