File size: 5,538 Bytes
34367da
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import { describe, test, expect, beforeAll, afterAll } from 'vitest';
import { neo4jService } from '../database/Neo4jService';
import { graphMemoryService } from '../memory/GraphMemoryService';

const shouldRunNeo4jTests = process.env.RUN_NEO4J_TESTS === 'true';
const describeGraphRag = shouldRunNeo4jTests ? describe : describe.skip;
let neo4jAvailable = false;

describeGraphRag('GraphRAG Integration Tests', () => {
    beforeAll(async () => {
        if (!shouldRunNeo4jTests) {
            neo4jAvailable = false;
            return;
        }

        try {
            await neo4jService.connect();
            neo4jAvailable = await neo4jService.healthCheck();
        } catch {
            neo4jAvailable = false;
            console.log('⚠️ Neo4j not available - GraphRAG tests will be skipped');
        }
    });

    afterAll(async () => {
        if (neo4jAvailable) {
            try {
                await neo4jService.runQuery('MATCH (n:TestEntity) DETACH DELETE n');
            } catch { /* ignore cleanup errors */ }
            await neo4jService.disconnect();
        }
    });

    test('should create entity and retrieve it', async () => {
        const entity = await graphMemoryService.createEntity(
            'TestEntity',
            'Test Person',
            { role: 'developer', team: 'backend' }
        );

        expect(entity.id).toBeDefined();
        expect(entity.name).toBe('Test Person');
        expect(entity.type).toBe('TestEntity');

        const retrieved = await neo4jService.getNodeById(entity.id);
        expect(retrieved).toBeDefined();
        expect(retrieved?.properties.role).toBe('developer');
    });

    test('should create relation between entities', async () => {
        const person = await graphMemoryService.createEntity('TestEntity', 'Alice', {});
        const project = await graphMemoryService.createEntity('TestEntity', 'Project X', {});

        const relation = await graphMemoryService.createRelation(
            person.id,
            project.id,
            'WORKS_ON',
            { since: '2025-01-01' }
        );

        expect(relation.id).toBeDefined();
        expect(relation.type).toBe('WORKS_ON');
        expect(relation.sourceId).toBe(person.id);
        expect(relation.targetId).toBe(project.id);
    });

    test('should search entities by name', async () => {
        await graphMemoryService.createEntity('TestEntity', 'Bob Smith', { role: 'designer' });
        await graphMemoryService.createEntity('TestEntity', 'Bob Jones', { role: 'developer' });

        const results = await graphMemoryService.searchEntities('Bob');

        expect(results.length).toBeGreaterThanOrEqual(2);
        expect(results.some(e => e.name.includes('Bob'))).toBe(true);
    });

    test('should get related entities', async () => {
        const alice = await graphMemoryService.createEntity('TestEntity', 'Alice', {});
        const bob = await graphMemoryService.createEntity('TestEntity', 'Bob', {});
        const charlie = await graphMemoryService.createEntity('TestEntity', 'Charlie', {});

        await graphMemoryService.createRelation(alice.id, bob.id, 'KNOWS');
        await graphMemoryService.createRelation(alice.id, charlie.id, 'KNOWS');

        const related = await graphMemoryService.getRelatedEntities(alice.id);

        expect(related.length).toBe(2);
        expect(related.some(e => e.name === 'Bob')).toBe(true);
        expect(related.some(e => e.name === 'Charlie')).toBe(true);
    });

    test('should find path between entities', async () => {
        const a = await graphMemoryService.createEntity('TestEntity', 'A', {});
        const b = await graphMemoryService.createEntity('TestEntity', 'B', {});
        const c = await graphMemoryService.createEntity('TestEntity', 'C', {});

        await graphMemoryService.createRelation(a.id, b.id, 'CONNECTS');
        await graphMemoryService.createRelation(b.id, c.id, 'CONNECTS');

        const path = await graphMemoryService.findPath(a.id, c.id);

        expect(path).toBeDefined();
        expect(path?.path.length).toBe(3); // A -> B -> C
        expect(path?.relations.length).toBe(2);
    });

    test('should get graph statistics', async () => {
        const stats = await graphMemoryService.getStatistics();

        expect(stats.totalEntities).toBeGreaterThan(0);
        expect(stats.entityTypes).toBeDefined();
        expect(stats.relationTypes).toBeDefined();
    });

    test('should update entity properties', async () => {
        const entity = await graphMemoryService.createEntity('TestEntity', 'Test', { version: 1 });

        const updated = await graphMemoryService.updateEntity(entity.id, { version: 2, status: 'active' });

        expect(updated).toBeDefined();
        expect(updated?.properties.version).toBe(2);
        expect(updated?.properties.status).toBe('active');
    });

    test('should delete entity and its relations', async () => {
        const entity = await graphMemoryService.createEntity('TestEntity', 'ToDelete', {});
        const other = await graphMemoryService.createEntity('TestEntity', 'Other', {});

        await graphMemoryService.createRelation(entity.id, other.id, 'TEMP');

        await graphMemoryService.deleteEntity(entity.id);

        const retrieved = await neo4jService.getNodeById(entity.id);
        expect(retrieved).toBeNull();
    });
});

export { };