UNPKG

@wearesage/schema

Version:

A flexible schema definition and validation system for TypeScript with multi-database support

167 lines (138 loc) 5.08 kB
import "reflect-metadata"; import { Neo4jAdapter } from "../../adapters/neo4j"; import { MetadataRegistry } from "../../core/MetadataRegistry"; import { SchemaBuilder } from "../../core/SchemaBuilder"; import { Entity, Property, Id, Labels } from "../../core/decorators"; // Simple test entity to isolate the property extraction bug @Entity() @Labels(['TestEntity']) class SimpleTestEntity { @Id() id!: string; @Property({ required: true }) name!: string; @Property() description?: string; @Property() count?: number; } describe('Neo4jAdapter Property Extraction Bug', () => { let adapter: Neo4jAdapter; let registry: MetadataRegistry; beforeAll(async () => { registry = new MetadataRegistry(); const builder = new SchemaBuilder(registry); builder.registerEntities([SimpleTestEntity]); adapter = new Neo4jAdapter(registry, "bolt://127.0.0.1:7687", { username: "neo4j", password: "password" }); // Skip if Neo4j not available try { await adapter.runNativeQuery("RETURN 1"); } catch (error) { console.log("⚠️ Skipping Neo4j tests - database not available"); return; } }); afterAll(async () => { try { // Clean up any test data await adapter.runNativeQuery("MATCH (n:TestEntity) DETACH DELETE n"); if (typeof (adapter as any).close === 'function') { await (adapter as any).close(); } } catch (error) { // Ignore cleanup errors } }); beforeEach(async () => { // Skip if Neo4j not available try { await adapter.runNativeQuery("RETURN 1"); } catch (error) { return; } }); it('should extract properties correctly from entity', async () => { const entity = new SimpleTestEntity(); entity.id = 'test-123'; entity.name = 'Test Entity'; entity.description = 'A test entity for property extraction'; entity.count = 42; // Debug: Check what the schema reflector sees const entityType = entity.constructor; const schema = (adapter as any).reflector.getEntitySchema(entityType); console.log('🔍 Entity schema:', JSON.stringify(schema, null, 2)); // Access the private method using type assertion const properties = (adapter as any).entityToProperties(entity); console.log('🔍 Extracted properties:', properties); expect(properties).toEqual({ id: 'test-123', name: 'Test Entity', description: 'A test entity for property extraction', count: 42 }); }); it('should handle entities with only id property', async () => { const entity = new SimpleTestEntity(); entity.id = 'test-456'; // Only set id, no other properties const properties = (adapter as any).entityToProperties(entity); // Should still return the id expect(properties).toEqual({ id: 'test-456' }); }); it('should generate valid Cypher when entity has multiple properties', async () => { const entity = new SimpleTestEntity(); entity.id = 'test-multi-' + Date.now(); entity.name = 'Multi Property Test'; entity.description = 'Testing multiple properties'; entity.count = 99; // This should NOT throw a syntax error await expect(adapter.save(entity)).resolves.not.toThrow(); // Verify it was saved const result = await adapter.query(SimpleTestEntity, { id: entity.id }); expect(result).toBeTruthy(); expect(result).toMatchObject({ id: entity.id, name: 'Multi Property Test', description: 'Testing multiple properties', count: 99 }); // Clean up await adapter.delete(SimpleTestEntity, entity.id); }); it('should generate valid Cypher when entity has only id (the bug case)', async () => { const entity = new SimpleTestEntity(); entity.id = 'test-id-only-' + Date.now(); // Intentionally leave other properties undefined to trigger the bug // This should NOT throw a syntax error - this is the bug we're fixing! await expect(adapter.save(entity)).resolves.not.toThrow(); // Verify it was saved (even with minimal data) const result = await adapter.query(SimpleTestEntity, { id: entity.id }); expect(result).toBeTruthy(); expect(result?.id).toBe(entity.id); // Clean up await adapter.delete(SimpleTestEntity, entity.id); }); it('should handle entities with some undefined properties', async () => { const entity = new SimpleTestEntity(); entity.id = 'test-partial-' + Date.now(); entity.name = 'Partial Entity'; // description and count are undefined await expect(adapter.save(entity)).resolves.not.toThrow(); const result = await adapter.query(SimpleTestEntity, { id: entity.id }); expect(result).toBeTruthy(); expect(result).toMatchObject({ id: entity.id, name: 'Partial Entity' }); // undefined properties shouldn't be in the result expect(result?.description).toBeUndefined(); expect(result?.count).toBeUndefined(); // Clean up await adapter.delete(SimpleTestEntity, entity.id); }); });