Skip to main content
Glama
index-manager.test.ts.broken11 kB
/** * IndexManager Test Suite - Infrastructure Foundation * Target: Achieve 75% coverage for database schema management * Scope: Constraint creation, index management, schema validation * Strategy: Mock Neo4j session to test schema operations without database */ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { IndexManager } from '../../../src/infrastructure/database/index-manager'; import { Session } from 'neo4j-driver'; describe('IndexManager - Infrastructure Foundation', () => { let indexManager: IndexManager; let mockSession: Session; beforeEach(() => { mockSession = { run: vi.fn() } as any; indexManager = new IndexManager(mockSession); }); describe('Constraint Management', () => { it('should ensure all required constraints exist', async () => { // Mock the database info call to return a valid database name (mockSession.run as any) .mockResolvedValueOnce({ records: [{ get: () => 'test-db' }] }) .mockResolvedValue({ records: [] }); await indexManager.ensureConstraints(); expect(mockSession.run).toHaveBeenCalledWith( 'CREATE CONSTRAINT IF NOT EXISTS FOR (m:Memory) REQUIRE m.id IS UNIQUE' ); expect(mockSession.run).toHaveBeenCalledWith( 'CREATE CONSTRAINT IF NOT EXISTS FOR (o:Observation) REQUIRE o.id IS UNIQUE' ); expect(mockSession.run).toHaveBeenCalledTimes(2); }); it('should handle constraint creation failures', async () => { const constraintError = new Error('Constraint creation failed'); (mockSession.run as any).mockRejectedValue(constraintError); await expect(indexManager.ensureConstraints()) .rejects.toThrow('Constraint creation failed'); }); it('should continue after first constraint succeeds and second fails', async () => { (mockSession.run as any) .mockResolvedValueOnce({ records: [] }) .mockRejectedValueOnce(new Error('Second constraint failed')); await expect(indexManager.ensureConstraints()) .rejects.toThrow('Second constraint failed'); // Should have attempted both constraints expect(mockSession.run).toHaveBeenCalledTimes(2); }); }); describe('Standard Index Management', () => { it('should ensure all required indexes exist', async () => { (mockSession.run as any).mockResolvedValue({ records: [] }); await indexManager.ensureIndexes(); const expectedIndexes = [ 'CREATE INDEX memory_type_idx IF NOT EXISTS FOR (m:Memory) ON (m.memoryType)', 'CREATE INDEX memory_name_idx IF NOT EXISTS FOR (m:Memory) ON (m.name)', 'CREATE INDEX memory_accessed_idx IF NOT EXISTS FOR (m:Memory) ON (m.lastAccessed)', 'CREATE INDEX relation_type_idx IF NOT EXISTS FOR ()-[r:RELATES_TO]-() ON (r.relationType)' ]; expectedIndexes.forEach(index => { expect(mockSession.run).toHaveBeenCalledWith(index); }); expect(mockSession.run).toHaveBeenCalledTimes(4); }); it('should handle index creation failures', async () => { const indexError = new Error('Index creation failed'); (mockSession.run as any).mockRejectedValue(indexError); await expect(indexManager.ensureIndexes()) .rejects.toThrow('Index creation failed'); }); it('should continue creating indexes after partial failure', async () => { (mockSession.run as any) .mockResolvedValueOnce({ records: [] }) .mockResolvedValueOnce({ records: [] }) .mockRejectedValueOnce(new Error('Third index failed')) .mockResolvedValueOnce({ records: [] }); await expect(indexManager.ensureIndexes()) .rejects.toThrow('Third index failed'); // Should have attempted first 3 indexes before failing expect(mockSession.run).toHaveBeenCalledTimes(3); }); }); describe('Fulltext Index Management', () => { it('should ensure all required fulltext indexes exist', async () => { (mockSession.run as any).mockResolvedValue({ records: [] }); await indexManager.ensureFulltextIndexes(); expect(mockSession.run).toHaveBeenCalledWith( 'CREATE FULLTEXT INDEX memory_metadata_idx IF NOT EXISTS FOR (m:Memory) ON EACH [m.metadata]' ); expect(mockSession.run).toHaveBeenCalledWith( 'CREATE FULLTEXT INDEX observation_content_idx IF NOT EXISTS FOR (o:Observation) ON EACH [o.content]' ); expect(mockSession.run).toHaveBeenCalledTimes(2); }); it('should handle fulltext index creation failures', async () => { const fulltextError = new Error('Fulltext index creation failed'); (mockSession.run as any).mockRejectedValue(fulltextError); await expect(indexManager.ensureFulltextIndexes()) .rejects.toThrow('Fulltext index creation failed'); }); }); describe('Vector Index Management', () => { it('should attempt to create vector indexes', async () => { // Mock SmartEmbeddingManager const mockEmbeddingManager = { getModelDimensions: vi.fn().mockResolvedValue(384) }; // Mock the dynamic import vi.doMock('../../../src/infrastructure/services/smart-embedding-manager', () => ({ SmartEmbeddingManager: vi.fn().mockImplementation(() => mockEmbeddingManager) })); (mockSession.run as any).mockResolvedValue({ records: [] }); await indexManager.ensureVectorIndexes(); // Should attempt to create both vector indexes expect(mockSession.run).toHaveBeenCalledWith( expect.stringContaining('CREATE VECTOR INDEX memory_name_vector_idx IF NOT EXISTS') ); expect(mockSession.run).toHaveBeenCalledWith( expect.stringContaining('CREATE VECTOR INDEX observation_embedding_vector_idx IF NOT EXISTS') ); }); it('should handle vector index creation failures gracefully', async () => { const vectorError = new Error('Vector index not supported'); (mockSession.run as any).mockRejectedValue(vectorError); // Should not throw - vector indexes are optional await expect(indexManager.ensureVectorIndexes()).resolves.not.toThrow(); }); it('should handle SmartEmbeddingManager import failures', async () => { vi.doMock('../../../src/infrastructure/services/smart-embedding-manager', () => { throw new Error('Module not found'); }); // Should not throw - vector indexes are optional await expect(indexManager.ensureVectorIndexes()).resolves.not.toThrow(); }); }); describe('Schema Validation', () => { it('should detect existing schema correctly', async () => { // Mock constraint check - has Memory.id constraint (mockSession.run as any) .mockResolvedValueOnce({ records: [{ get: () => 'some-constraint-data' }] }) .mockResolvedValueOnce({ records: [{ get: () => 'memory_type_idx' }] }); const hasSchema = await indexManager.hasRequiredSchema(); expect(hasSchema).toBe(true); expect(mockSession.run).toHaveBeenCalledWith( expect.stringContaining('SHOW CONSTRAINTS') ); expect(mockSession.run).toHaveBeenCalledWith( expect.stringContaining('SHOW INDEXES') ); }); it('should detect missing constraints', async () => { // Mock constraint check - no Memory.id constraint (mockSession.run as any) .mockResolvedValueOnce({ records: [] }) .mockResolvedValueOnce({ records: [{ get: () => 'memory_type_idx' }] }); const hasSchema = await indexManager.hasRequiredSchema(); expect(hasSchema).toBe(false); }); it('should detect missing indexes', async () => { // Mock constraint check - has constraint but no index (mockSession.run as any) .mockResolvedValueOnce({ records: [{ get: () => 'some-constraint-data' }] }) .mockResolvedValueOnce({ records: [] }); const hasSchema = await indexManager.hasRequiredSchema(); expect(hasSchema).toBe(false); }); it('should handle schema check failures gracefully', async () => { (mockSession.run as any).mockRejectedValue(new Error('Database unavailable')); const hasSchema = await indexManager.hasRequiredSchema(); expect(hasSchema).toBe(false); }); it('should assume missing schema when checks fail', async () => { (mockSession.run as any).mockRejectedValue(new Error('Permission denied')); const hasSchema = await indexManager.hasRequiredSchema(); expect(hasSchema).toBe(false); }); }); describe('Complete Schema Initialization', () => { it('should initialize all schema elements successfully', async () => { // Mock SmartEmbeddingManager for vector indexes const mockEmbeddingManager = { getModelDimensions: vi.fn().mockResolvedValue(384) }; vi.doMock('../../../src/infrastructure/services/smart-embedding-manager', () => ({ SmartEmbeddingManager: vi.fn().mockImplementation(() => mockEmbeddingManager) })); (mockSession.run as any).mockResolvedValue({ records: [] }); await indexManager.initializeSchema(); // Should call all schema initialization methods const totalExpectedCalls = 2 + 4 + 2 + 2; // constraints + indexes + fulltext + vector expect(mockSession.run).toHaveBeenCalledTimes(totalExpectedCalls); }); it('should fail fast on schema initialization errors', async () => { (mockSession.run as any).mockRejectedValue(new Error('Critical schema error')); await expect(indexManager.initializeSchema()) .rejects.toThrow('Critical schema error'); }); it('should handle partial initialization failures', async () => { // Constraints succeed, indexes fail (mockSession.run as any) .mockResolvedValueOnce({ records: [] }) // First constraint .mockResolvedValueOnce({ records: [] }) // Second constraint .mockRejectedValueOnce(new Error('Index creation failed')); // First index fails await expect(indexManager.initializeSchema()) .rejects.toThrow('Index creation failed'); }); }); describe('Error Handling and Resilience', () => { it('should handle network timeouts gracefully', async () => { (mockSession.run as any).mockRejectedValue(new Error('Network timeout')); await expect(indexManager.ensureConstraints()) .rejects.toThrow('Network timeout'); }); it('should handle database permission errors', async () => { (mockSession.run as any).mockRejectedValue(new Error('Permission denied')); await expect(indexManager.ensureIndexes()) .rejects.toThrow('Permission denied'); }); it('should handle malformed Cypher queries', async () => { (mockSession.run as any).mockRejectedValue(new Error('Syntax error')); await expect(indexManager.ensureFulltextIndexes()) .rejects.toThrow('Syntax error'); }); }); });

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/sylweriusz/mcp-neo4j-memory-server'

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