Skip to main content
Glama
graph-traversal-edge-cases.test.ts11.2 kB
/** * Graph Traversal Edge Cases and Null Handling Tests * * Tests for edge cases including null metadata fields, explicit zero values, * and boundary conditions in graph traversal operations. * * Requirements: 2.3, 2.4, 2.5 */ import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { DatabaseConnectionManager } from "../../../database/connection-manager"; import { GraphTraversal } from "../../../graph/graph-traversal"; describe("GraphTraversal - Null Handling and Edge Cases", () => { let db: DatabaseConnectionManager; let graphTraversal: GraphTraversal; beforeEach(async () => { db = new DatabaseConnectionManager({ host: process.env.DB_HOST || "localhost", port: parseInt(process.env.DB_PORT || "5432"), database: process.env.DB_NAME || "thoughtmcp_test", user: process.env.DB_USER || "postgres", password: process.env.DB_PASSWORD || "postgres", poolSize: 5, }); await db.connect(); graphTraversal = new GraphTraversal(db); const client = await db.getConnection(); try { await client.query("DELETE FROM memory_links WHERE source_id LIKE 'test-branch-%'"); await client.query("DELETE FROM memory_metadata WHERE memory_id LIKE 'test-branch-%'"); await client.query("DELETE FROM memories WHERE id LIKE 'test-branch-%'"); } finally { db.releaseConnection(client); } }); afterEach(async () => { if (db) { const client = await db.getConnection(); try { await client.query("DELETE FROM memory_links WHERE source_id LIKE 'test-branch-%'"); await client.query("DELETE FROM memory_metadata WHERE memory_id LIKE 'test-branch-%'"); await client.query("DELETE FROM memories WHERE id LIKE 'test-branch-%'"); } finally { db.releaseConnection(client); } await db.disconnect(); } }); async function createTestMemory( id: string, content: string, metadata?: { keywords?: string[] | null; tags?: string[] | null; category?: string | null; context?: string | null; importance?: number | null; is_atomic?: boolean | null; parent_id?: string | null; } ): Promise<void> { const client = await db.getConnection(); try { await client.query( `INSERT INTO memories (id, content, created_at, last_accessed, access_count, salience, strength, user_id, session_id, primary_sector) VALUES ($1, $2, NOW(), NOW(), 0, 0.5, 1.0, 'test-user', 'test-session', 'semantic')`, [id, content] ); // Insert metadata with explicit null values if provided await client.query( `INSERT INTO memory_metadata (memory_id, keywords, tags, category, context, importance, is_atomic, parent_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, [ id, metadata?.keywords ?? [], metadata?.tags ?? [], metadata?.category ?? "test", metadata?.context ?? "", metadata?.importance ?? 0.5, metadata?.is_atomic ?? true, metadata?.parent_id ?? null, ] ); } finally { db.releaseConnection(client); } } async function createTestLink( sourceId: string, targetId: string, linkType: string, weight: number ): Promise<void> { const client = await db.getConnection(); try { await client.query( `INSERT INTO memory_links (source_id, target_id, link_type, weight, created_at, traversal_count) VALUES ($1, $2, $3, $4, NOW(), 0)`, [sourceId, targetId, linkType, weight] ); } finally { db.releaseConnection(client); } } it("should apply default values for null metadata fields", async () => { // Requirement 2.4: Test metadata null handling with appropriate defaults // Nullable fields: category, context, importance, is_atomic, parent_id // Given: Memory with nullable metadata fields set to NULL const client = await db.getConnection(); try { await client.query( `INSERT INTO memories (id, content, created_at, last_accessed, access_count, salience, strength, user_id, session_id, primary_sector) VALUES ($1, $2, NOW(), NOW(), 0, 0.5, 1.0, 'test-user', 'test-session', 'semantic')`, ["test-branch-null-metadata", "Memory with null metadata"] ); // Insert metadata with NULL values for nullable fields await client.query( `INSERT INTO memory_metadata (memory_id, keywords, tags, category, context, importance, is_atomic, parent_id) VALUES ($1, $2, $3, NULL, NULL, NULL, NULL, NULL)`, ["test-branch-null-metadata", [], []] ); } finally { db.releaseConnection(client); } // When: Retrieving the memory (which calls getMemory internally) const result = await graphTraversal.getConnectedMemories("test-branch-null-metadata", { maxDepth: 0, }); // Then: Should handle null metadata gracefully with defaults expect(result.memories).toHaveLength(1); const memory = result.memories[0]; expect(memory.metadata.keywords).toEqual([]); expect(memory.metadata.tags).toEqual([]); expect(memory.metadata.category).toBe(""); // NULL -> "" expect(memory.metadata.context).toBe(""); // NULL -> "" expect(memory.metadata.importance).toBe(0.5); // NULL -> 0.5 expect(memory.metadata.isAtomic).toBe(true); // NULL -> true expect(memory.metadata.parentId).toBeUndefined(); // NULL -> undefined }); it("should include all links when minWeight is explicitly set to 0", async () => { // Requirement 2.4: Test explicit zero minWeight vs undefined // Given: Memories with links of varying weights including 0 await createTestMemory("test-branch-minweight-A", "Memory A"); await createTestMemory("test-branch-minweight-B", "Memory B"); await createTestMemory("test-branch-minweight-C", "Memory C"); await createTestLink("test-branch-minweight-A", "test-branch-minweight-B", "semantic", 0.0); await createTestLink("test-branch-minweight-A", "test-branch-minweight-C", "semantic", 0.5); // When: Using explicit minWeight of 0 const result = await graphTraversal.getConnectedMemories("test-branch-minweight-A", { maxDepth: 1, minWeight: 0, // Explicit 0, not undefined }); // Then: Should include all links >= 0 (which is all links) expect(result.memories).toHaveLength(3); // A, B, C const ids = result.memories.map((m) => m.id); expect(ids).toContain("test-branch-minweight-B"); // 0.0 weight expect(ids).toContain("test-branch-minweight-C"); // 0.5 weight }); it("should filter links by weight threshold in BFS traversal", async () => { // Requirement 2.4: Test link weight filtering in breadth-first search // Given: Memories with links of varying weights await createTestMemory("test-branch-bfs-minweight-A", "Memory A"); await createTestMemory("test-branch-bfs-minweight-B", "Memory B"); await createTestMemory("test-branch-bfs-minweight-C", "Memory C"); await createTestLink( "test-branch-bfs-minweight-A", "test-branch-bfs-minweight-B", "semantic", 0.3 ); await createTestLink( "test-branch-bfs-minweight-A", "test-branch-bfs-minweight-C", "semantic", 0.7 ); // When: Using explicit minWeight of 0.5 with BFS const result = await graphTraversal.getConnectedMemories("test-branch-bfs-minweight-A", { maxDepth: 1, minWeight: 0.5, traversalType: "breadth-first", }); // Then: Should only include links >= 0.5 expect(result.memories).toHaveLength(2); // A, C (not B) const ids = result.memories.map((m) => m.id); expect(ids).toContain("test-branch-bfs-minweight-A"); expect(ids).toContain("test-branch-bfs-minweight-C"); // 0.7 >= 0.5 expect(ids).not.toContain("test-branch-bfs-minweight-B"); // 0.3 < 0.5 }); it("should filter links by weight threshold in DFS traversal", async () => { // Requirement 2.4: Test link weight filtering in depth-first search // Given: Memories with links of varying weights await createTestMemory("test-branch-dfs-minweight-A", "Memory A"); await createTestMemory("test-branch-dfs-minweight-B", "Memory B"); await createTestMemory("test-branch-dfs-minweight-C", "Memory C"); await createTestLink( "test-branch-dfs-minweight-A", "test-branch-dfs-minweight-B", "semantic", 0.3 ); await createTestLink( "test-branch-dfs-minweight-A", "test-branch-dfs-minweight-C", "semantic", 0.7 ); // When: Using explicit minWeight of 0.5 with DFS const result = await graphTraversal.getConnectedMemories("test-branch-dfs-minweight-A", { maxDepth: 1, minWeight: 0.5, traversalType: "depth-first", }); // Then: Should only include links >= 0.5 expect(result.memories).toHaveLength(2); // A, C (not B) const ids = result.memories.map((m) => m.id); expect(ids).toContain("test-branch-dfs-minweight-A"); expect(ids).toContain("test-branch-dfs-minweight-C"); // 0.7 >= 0.5 expect(ids).not.toContain("test-branch-dfs-minweight-B"); // 0.3 < 0.5 }); it("should preserve is_atomic false value for composite memories", async () => { // Requirement 2.4: Test composite memory flag handling // Given: Memory with is_atomic explicitly set to false const client = await db.getConnection(); try { await client.query( `INSERT INTO memories (id, content, created_at, last_accessed, access_count, salience, strength, user_id, session_id, primary_sector) VALUES ($1, $2, NOW(), NOW(), 0, 0.5, 1.0, 'test-user', 'test-session', 'semantic')`, ["test-branch-atomic-false", "Composite memory"] ); await client.query( `INSERT INTO memory_metadata (memory_id, keywords, tags, category, context, importance, is_atomic, parent_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, ["test-branch-atomic-false", [], [], "test", "", 0.5, false, null] ); } finally { db.releaseConnection(client); } // When: Retrieving the memory const result = await graphTraversal.getConnectedMemories("test-branch-atomic-false", { maxDepth: 0, }); // Then: Should preserve is_atomic = false expect(result.memories).toHaveLength(1); expect(result.memories[0].metadata.isAtomic).toBe(false); }); it("should preserve parent_id for child memories", async () => { // Requirement 2.4: Test parent-child memory relationship tracking // Given: Memory with parent_id set await createTestMemory("test-branch-parent", "Parent memory"); await createTestMemory("test-branch-child", "Child memory", { parent_id: "test-branch-parent", }); // When: Retrieving the child memory const result = await graphTraversal.getConnectedMemories("test-branch-child", { maxDepth: 0, }); // Then: Should include parent_id expect(result.memories).toHaveLength(1); expect(result.memories[0].metadata.parentId).toBe("test-branch-parent"); }); });

Latest Blog Posts

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/keyurgolani/ThoughtMcp'

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