@danielsimonjr/memory-mcp 0.7.2 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/edge-cases/edge-cases.test.js +406 -0
- package/dist/__tests__/file-path.test.js +5 -5
- package/dist/__tests__/integration/workflows.test.js +449 -0
- package/dist/__tests__/knowledge-graph.test.js +8 -3
- package/dist/__tests__/performance/benchmarks.test.js +411 -0
- package/dist/__tests__/unit/core/EntityManager.test.js +334 -0
- package/dist/__tests__/unit/core/GraphStorage.test.js +205 -0
- package/dist/__tests__/unit/core/RelationManager.test.js +274 -0
- package/dist/__tests__/unit/features/CompressionManager.test.js +350 -0
- package/dist/__tests__/unit/search/BasicSearch.test.js +311 -0
- package/dist/__tests__/unit/search/BooleanSearch.test.js +432 -0
- package/dist/__tests__/unit/search/FuzzySearch.test.js +448 -0
- package/dist/__tests__/unit/search/RankedSearch.test.js +379 -0
- package/dist/__tests__/unit/utils/levenshtein.test.js +77 -0
- package/dist/core/EntityManager.js +554 -0
- package/dist/core/GraphStorage.js +172 -0
- package/dist/core/KnowledgeGraphManager.js +400 -0
- package/dist/core/ObservationManager.js +129 -0
- package/dist/core/RelationManager.js +186 -0
- package/dist/core/TransactionManager.js +389 -0
- package/dist/core/index.js +9 -0
- package/dist/features/AnalyticsManager.js +222 -0
- package/dist/features/ArchiveManager.js +74 -0
- package/dist/features/BackupManager.js +311 -0
- package/dist/features/CompressionManager.js +310 -0
- package/dist/features/ExportManager.js +305 -0
- package/dist/features/HierarchyManager.js +219 -0
- package/dist/features/ImportExportManager.js +50 -0
- package/dist/features/ImportManager.js +328 -0
- package/dist/features/TagManager.js +210 -0
- package/dist/features/index.js +12 -0
- package/dist/index.js +13 -996
- package/dist/memory.jsonl +225 -0
- package/dist/search/BasicSearch.js +161 -0
- package/dist/search/BooleanSearch.js +304 -0
- package/dist/search/FuzzySearch.js +115 -0
- package/dist/search/RankedSearch.js +206 -0
- package/dist/search/SavedSearchManager.js +145 -0
- package/dist/search/SearchManager.js +305 -0
- package/dist/search/SearchSuggestions.js +57 -0
- package/dist/search/TFIDFIndexManager.js +217 -0
- package/dist/search/index.js +10 -0
- package/dist/server/MCPServer.js +889 -0
- package/dist/types/analytics.types.js +6 -0
- package/dist/types/entity.types.js +7 -0
- package/dist/types/import-export.types.js +7 -0
- package/dist/types/index.js +12 -0
- package/dist/types/search.types.js +7 -0
- package/dist/types/tag.types.js +6 -0
- package/dist/utils/constants.js +127 -0
- package/dist/utils/dateUtils.js +89 -0
- package/dist/utils/errors.js +121 -0
- package/dist/utils/index.js +13 -0
- package/dist/utils/levenshtein.js +62 -0
- package/dist/utils/logger.js +33 -0
- package/dist/utils/pathUtils.js +115 -0
- package/dist/utils/schemas.js +184 -0
- package/dist/utils/searchCache.js +209 -0
- package/dist/utils/tfidf.js +90 -0
- package/dist/utils/validationUtils.js +109 -0
- package/package.json +50 -48
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance Benchmarks
|
|
3
|
+
*
|
|
4
|
+
* Tests for performance budgets and benchmarks across all operations.
|
|
5
|
+
* Validates that operations complete within acceptable time limits.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
8
|
+
import { GraphStorage } from '../../core/GraphStorage.js';
|
|
9
|
+
import { EntityManager } from '../../core/EntityManager.js';
|
|
10
|
+
import { RelationManager } from '../../core/RelationManager.js';
|
|
11
|
+
import { CompressionManager } from '../../features/CompressionManager.js';
|
|
12
|
+
import { BasicSearch } from '../../search/BasicSearch.js';
|
|
13
|
+
import { RankedSearch } from '../../search/RankedSearch.js';
|
|
14
|
+
import { BooleanSearch } from '../../search/BooleanSearch.js';
|
|
15
|
+
import { FuzzySearch } from '../../search/FuzzySearch.js';
|
|
16
|
+
import { promises as fs } from 'fs';
|
|
17
|
+
import { join } from 'path';
|
|
18
|
+
import { tmpdir } from 'os';
|
|
19
|
+
describe('Performance Benchmarks', () => {
|
|
20
|
+
let storage;
|
|
21
|
+
let entityManager;
|
|
22
|
+
let relationManager;
|
|
23
|
+
let compressionManager;
|
|
24
|
+
let basicSearch;
|
|
25
|
+
let rankedSearch;
|
|
26
|
+
let booleanSearch;
|
|
27
|
+
let fuzzySearch;
|
|
28
|
+
let testDir;
|
|
29
|
+
let testFilePath;
|
|
30
|
+
beforeEach(async () => {
|
|
31
|
+
testDir = join(tmpdir(), `perf-test-${Date.now()}-${Math.random()}`);
|
|
32
|
+
await fs.mkdir(testDir, { recursive: true });
|
|
33
|
+
testFilePath = join(testDir, 'test-graph.jsonl');
|
|
34
|
+
storage = new GraphStorage(testFilePath);
|
|
35
|
+
entityManager = new EntityManager(storage);
|
|
36
|
+
relationManager = new RelationManager(storage);
|
|
37
|
+
compressionManager = new CompressionManager(storage);
|
|
38
|
+
basicSearch = new BasicSearch(storage);
|
|
39
|
+
rankedSearch = new RankedSearch(storage);
|
|
40
|
+
booleanSearch = new BooleanSearch(storage);
|
|
41
|
+
fuzzySearch = new FuzzySearch(storage);
|
|
42
|
+
});
|
|
43
|
+
afterEach(async () => {
|
|
44
|
+
try {
|
|
45
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Ignore cleanup errors
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
describe('Entity Creation Performance', () => {
|
|
52
|
+
it('should create 1 entity in < 50ms', async () => {
|
|
53
|
+
const startTime = Date.now();
|
|
54
|
+
await entityManager.createEntities([
|
|
55
|
+
{ name: 'Entity1', entityType: 'test', observations: ['Test observation'] },
|
|
56
|
+
]);
|
|
57
|
+
const duration = Date.now() - startTime;
|
|
58
|
+
expect(duration).toBeLessThan(50);
|
|
59
|
+
});
|
|
60
|
+
it('should create 100 entities in < 200ms', async () => {
|
|
61
|
+
const entities = Array.from({ length: 100 }, (_, i) => ({
|
|
62
|
+
name: `Entity${i}`,
|
|
63
|
+
entityType: 'test',
|
|
64
|
+
observations: [`Observation ${i}`],
|
|
65
|
+
importance: (i % 10) + 1,
|
|
66
|
+
}));
|
|
67
|
+
const startTime = Date.now();
|
|
68
|
+
await entityManager.createEntities(entities);
|
|
69
|
+
const duration = Date.now() - startTime;
|
|
70
|
+
expect(duration).toBeLessThan(200);
|
|
71
|
+
});
|
|
72
|
+
it('should create 1000 entities in < 1500ms', async () => {
|
|
73
|
+
const entities = Array.from({ length: 1000 }, (_, i) => ({
|
|
74
|
+
name: `Entity${i}`,
|
|
75
|
+
entityType: 'test',
|
|
76
|
+
observations: [`Observation ${i}`],
|
|
77
|
+
}));
|
|
78
|
+
const startTime = Date.now();
|
|
79
|
+
await entityManager.createEntities(entities);
|
|
80
|
+
const duration = Date.now() - startTime;
|
|
81
|
+
expect(duration).toBeLessThan(1500);
|
|
82
|
+
});
|
|
83
|
+
it('should batch update 100 entities in < 200ms', async () => {
|
|
84
|
+
// Create entities first
|
|
85
|
+
const entities = Array.from({ length: 100 }, (_, i) => ({
|
|
86
|
+
name: `Entity${i}`,
|
|
87
|
+
entityType: 'test',
|
|
88
|
+
observations: [`Observation ${i}`],
|
|
89
|
+
}));
|
|
90
|
+
await entityManager.createEntities(entities);
|
|
91
|
+
// Batch update
|
|
92
|
+
const updates = Array.from({ length: 100 }, (_, i) => ({
|
|
93
|
+
name: `Entity${i}`,
|
|
94
|
+
updates: { importance: 5 },
|
|
95
|
+
}));
|
|
96
|
+
const startTime = Date.now();
|
|
97
|
+
await entityManager.batchUpdate(updates);
|
|
98
|
+
const duration = Date.now() - startTime;
|
|
99
|
+
expect(duration).toBeLessThan(200);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
describe('Relation Creation Performance', () => {
|
|
103
|
+
it('should create 100 relations in < 200ms', async () => {
|
|
104
|
+
// Create entities first
|
|
105
|
+
const entities = Array.from({ length: 50 }, (_, i) => ({
|
|
106
|
+
name: `Entity${i}`,
|
|
107
|
+
entityType: 'test',
|
|
108
|
+
observations: ['Test'],
|
|
109
|
+
}));
|
|
110
|
+
await entityManager.createEntities(entities);
|
|
111
|
+
// Create relations
|
|
112
|
+
const relations = Array.from({ length: 100 }, (_, i) => ({
|
|
113
|
+
from: `Entity${i % 50}`,
|
|
114
|
+
to: `Entity${(i + 1) % 50}`,
|
|
115
|
+
relationType: 'connects',
|
|
116
|
+
}));
|
|
117
|
+
const startTime = Date.now();
|
|
118
|
+
await relationManager.createRelations(relations);
|
|
119
|
+
const duration = Date.now() - startTime;
|
|
120
|
+
expect(duration).toBeLessThan(200);
|
|
121
|
+
});
|
|
122
|
+
it('should create 1000 relations in < 1500ms', async () => {
|
|
123
|
+
// Create entities first
|
|
124
|
+
const entities = Array.from({ length: 100 }, (_, i) => ({
|
|
125
|
+
name: `Entity${i}`,
|
|
126
|
+
entityType: 'test',
|
|
127
|
+
observations: ['Test'],
|
|
128
|
+
}));
|
|
129
|
+
await entityManager.createEntities(entities);
|
|
130
|
+
// Create relations
|
|
131
|
+
const relations = Array.from({ length: 1000 }, (_, i) => ({
|
|
132
|
+
from: `Entity${i % 100}`,
|
|
133
|
+
to: `Entity${(i + 1) % 100}`,
|
|
134
|
+
relationType: 'connects',
|
|
135
|
+
}));
|
|
136
|
+
const startTime = Date.now();
|
|
137
|
+
await relationManager.createRelations(relations);
|
|
138
|
+
const duration = Date.now() - startTime;
|
|
139
|
+
expect(duration).toBeLessThan(1500);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
describe('Search Performance', () => {
|
|
143
|
+
beforeEach(async () => {
|
|
144
|
+
// Create a moderate-sized graph
|
|
145
|
+
const entities = Array.from({ length: 500 }, (_, i) => ({
|
|
146
|
+
name: `Entity${i}`,
|
|
147
|
+
entityType: i % 5 === 0 ? 'person' : 'project',
|
|
148
|
+
observations: [`This is observation ${i} with some searchable text`],
|
|
149
|
+
tags: i % 3 === 0 ? ['tagged', 'test'] : undefined,
|
|
150
|
+
importance: (i % 10) + 1,
|
|
151
|
+
}));
|
|
152
|
+
await entityManager.createEntities(entities);
|
|
153
|
+
});
|
|
154
|
+
it('should perform basic search in < 100ms', async () => {
|
|
155
|
+
const startTime = Date.now();
|
|
156
|
+
await basicSearch.searchNodes('Entity');
|
|
157
|
+
const duration = Date.now() - startTime;
|
|
158
|
+
expect(duration).toBeLessThan(100);
|
|
159
|
+
});
|
|
160
|
+
it('should perform ranked search in < 600ms', async () => {
|
|
161
|
+
const startTime = Date.now();
|
|
162
|
+
await rankedSearch.searchNodesRanked('searchable text');
|
|
163
|
+
const duration = Date.now() - startTime;
|
|
164
|
+
expect(duration).toBeLessThan(600);
|
|
165
|
+
});
|
|
166
|
+
it('should perform boolean search in < 150ms', async () => {
|
|
167
|
+
const startTime = Date.now();
|
|
168
|
+
await booleanSearch.booleanSearch('person AND observation');
|
|
169
|
+
const duration = Date.now() - startTime;
|
|
170
|
+
expect(duration).toBeLessThan(150);
|
|
171
|
+
});
|
|
172
|
+
it('should perform fuzzy search in < 200ms', async () => {
|
|
173
|
+
const startTime = Date.now();
|
|
174
|
+
await fuzzySearch.fuzzySearch('Entty', 0.7);
|
|
175
|
+
const duration = Date.now() - startTime;
|
|
176
|
+
expect(duration).toBeLessThan(200);
|
|
177
|
+
});
|
|
178
|
+
it('should search with filters in < 150ms', async () => {
|
|
179
|
+
const startTime = Date.now();
|
|
180
|
+
await basicSearch.searchNodes('Entity', ['tagged'], 5, 8);
|
|
181
|
+
const duration = Date.now() - startTime;
|
|
182
|
+
expect(duration).toBeLessThan(150);
|
|
183
|
+
});
|
|
184
|
+
it('should open 50 nodes in < 100ms', async () => {
|
|
185
|
+
const nodeNames = Array.from({ length: 50 }, (_, i) => `Entity${i}`);
|
|
186
|
+
const startTime = Date.now();
|
|
187
|
+
await basicSearch.openNodes(nodeNames);
|
|
188
|
+
const duration = Date.now() - startTime;
|
|
189
|
+
expect(duration).toBeLessThan(100);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
describe('Compression Performance', () => {
|
|
193
|
+
it('should detect duplicates in 100 entities in < 300ms', async () => {
|
|
194
|
+
// Create entities with some duplicates
|
|
195
|
+
const entities = Array.from({ length: 100 }, (_, i) => ({
|
|
196
|
+
name: `Entity${i}`,
|
|
197
|
+
entityType: 'person',
|
|
198
|
+
observations: [i % 10 === 0 ? 'Duplicate observation' : `Unique observation ${i}`],
|
|
199
|
+
}));
|
|
200
|
+
await entityManager.createEntities(entities);
|
|
201
|
+
const startTime = Date.now();
|
|
202
|
+
await compressionManager.findDuplicates(0.8);
|
|
203
|
+
const duration = Date.now() - startTime;
|
|
204
|
+
expect(duration).toBeLessThan(300);
|
|
205
|
+
});
|
|
206
|
+
it('should detect duplicates in 500 entities in < 1500ms', async () => {
|
|
207
|
+
// Create entities with some duplicates
|
|
208
|
+
const entities = Array.from({ length: 500 }, (_, i) => ({
|
|
209
|
+
name: `Entity${i}`,
|
|
210
|
+
entityType: 'person',
|
|
211
|
+
observations: [i % 20 === 0 ? 'Duplicate observation' : `Unique observation ${i}`],
|
|
212
|
+
}));
|
|
213
|
+
await entityManager.createEntities(entities);
|
|
214
|
+
const startTime = Date.now();
|
|
215
|
+
await compressionManager.findDuplicates(0.8);
|
|
216
|
+
const duration = Date.now() - startTime;
|
|
217
|
+
expect(duration).toBeLessThan(1500);
|
|
218
|
+
});
|
|
219
|
+
it('should compress duplicates in 100 entities in < 400ms', async () => {
|
|
220
|
+
// Create similar entities
|
|
221
|
+
const entities = Array.from({ length: 100 }, (_, i) => ({
|
|
222
|
+
name: `Entity${i}`,
|
|
223
|
+
entityType: 'person',
|
|
224
|
+
observations: [i % 10 === 0 ? 'Similar observation' : `Observation ${i}`],
|
|
225
|
+
}));
|
|
226
|
+
await entityManager.createEntities(entities);
|
|
227
|
+
const startTime = Date.now();
|
|
228
|
+
await compressionManager.compressGraph(0.8, false);
|
|
229
|
+
const duration = Date.now() - startTime;
|
|
230
|
+
expect(duration).toBeLessThan(400);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
describe('Graph Loading/Saving Performance', () => {
|
|
234
|
+
it('should load graph with 100 entities in < 100ms', async () => {
|
|
235
|
+
// Create entities
|
|
236
|
+
const entities = Array.from({ length: 100 }, (_, i) => ({
|
|
237
|
+
name: `Entity${i}`,
|
|
238
|
+
entityType: 'test',
|
|
239
|
+
observations: [`Observation ${i}`],
|
|
240
|
+
}));
|
|
241
|
+
await entityManager.createEntities(entities);
|
|
242
|
+
const startTime = Date.now();
|
|
243
|
+
await storage.loadGraph();
|
|
244
|
+
const duration = Date.now() - startTime;
|
|
245
|
+
expect(duration).toBeLessThan(100);
|
|
246
|
+
});
|
|
247
|
+
it('should load graph with 1000 entities in < 500ms', async () => {
|
|
248
|
+
// Create entities
|
|
249
|
+
const entities = Array.from({ length: 1000 }, (_, i) => ({
|
|
250
|
+
name: `Entity${i}`,
|
|
251
|
+
entityType: 'test',
|
|
252
|
+
observations: [`Observation ${i}`],
|
|
253
|
+
}));
|
|
254
|
+
await entityManager.createEntities(entities);
|
|
255
|
+
const startTime = Date.now();
|
|
256
|
+
await storage.loadGraph();
|
|
257
|
+
const duration = Date.now() - startTime;
|
|
258
|
+
expect(duration).toBeLessThan(500);
|
|
259
|
+
});
|
|
260
|
+
it('should save graph with 100 entities in < 150ms', async () => {
|
|
261
|
+
// Create entities
|
|
262
|
+
const entities = Array.from({ length: 100 }, (_, i) => ({
|
|
263
|
+
name: `Entity${i}`,
|
|
264
|
+
entityType: 'test',
|
|
265
|
+
observations: [`Observation ${i}`],
|
|
266
|
+
}));
|
|
267
|
+
const graph = await storage.loadGraph();
|
|
268
|
+
graph.entities = entities.map(e => ({
|
|
269
|
+
...e,
|
|
270
|
+
createdAt: new Date().toISOString(),
|
|
271
|
+
lastModified: new Date().toISOString(),
|
|
272
|
+
}));
|
|
273
|
+
const startTime = Date.now();
|
|
274
|
+
await storage.saveGraph(graph);
|
|
275
|
+
const duration = Date.now() - startTime;
|
|
276
|
+
expect(duration).toBeLessThan(150);
|
|
277
|
+
});
|
|
278
|
+
it('should save graph with 1000 entities in < 800ms', async () => {
|
|
279
|
+
// Create entities
|
|
280
|
+
const entities = Array.from({ length: 1000 }, (_, i) => ({
|
|
281
|
+
name: `Entity${i}`,
|
|
282
|
+
entityType: 'test',
|
|
283
|
+
observations: [`Observation ${i}`],
|
|
284
|
+
}));
|
|
285
|
+
const graph = await storage.loadGraph();
|
|
286
|
+
graph.entities = entities.map(e => ({
|
|
287
|
+
...e,
|
|
288
|
+
createdAt: new Date().toISOString(),
|
|
289
|
+
lastModified: new Date().toISOString(),
|
|
290
|
+
}));
|
|
291
|
+
const startTime = Date.now();
|
|
292
|
+
await storage.saveGraph(graph);
|
|
293
|
+
const duration = Date.now() - startTime;
|
|
294
|
+
expect(duration).toBeLessThan(800);
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
describe('Complex Workflow Performance', () => {
|
|
298
|
+
it('should complete full CRUD workflow in < 300ms', async () => {
|
|
299
|
+
const startTime = Date.now();
|
|
300
|
+
// Create
|
|
301
|
+
await entityManager.createEntities([
|
|
302
|
+
{ name: 'Entity1', entityType: 'test', observations: ['Test 1'] },
|
|
303
|
+
{ name: 'Entity2', entityType: 'test', observations: ['Test 2'] },
|
|
304
|
+
]);
|
|
305
|
+
// Read
|
|
306
|
+
await entityManager.getEntity('Entity1');
|
|
307
|
+
// Update
|
|
308
|
+
await entityManager.updateEntity('Entity1', { importance: 5 });
|
|
309
|
+
// Search
|
|
310
|
+
await basicSearch.searchNodes('Entity');
|
|
311
|
+
// Delete
|
|
312
|
+
await entityManager.deleteEntities(['Entity2']);
|
|
313
|
+
const duration = Date.now() - startTime;
|
|
314
|
+
expect(duration).toBeLessThan(300);
|
|
315
|
+
});
|
|
316
|
+
it('should handle bulk workflow (create, relate, search) in < 500ms', async () => {
|
|
317
|
+
const startTime = Date.now();
|
|
318
|
+
// Bulk create
|
|
319
|
+
const entities = Array.from({ length: 50 }, (_, i) => ({
|
|
320
|
+
name: `Entity${i}`,
|
|
321
|
+
entityType: 'test',
|
|
322
|
+
observations: [`Observation ${i}`],
|
|
323
|
+
}));
|
|
324
|
+
await entityManager.createEntities(entities);
|
|
325
|
+
// Bulk relate
|
|
326
|
+
const relations = Array.from({ length: 50 }, (_, i) => ({
|
|
327
|
+
from: `Entity${i}`,
|
|
328
|
+
to: `Entity${(i + 1) % 50}`,
|
|
329
|
+
relationType: 'connects',
|
|
330
|
+
}));
|
|
331
|
+
await relationManager.createRelations(relations);
|
|
332
|
+
// Search
|
|
333
|
+
await basicSearch.searchNodes('Entity');
|
|
334
|
+
const duration = Date.now() - startTime;
|
|
335
|
+
expect(duration).toBeLessThan(500);
|
|
336
|
+
});
|
|
337
|
+
it('should handle complex query workflow in < 400ms', async () => {
|
|
338
|
+
// Setup
|
|
339
|
+
const entities = Array.from({ length: 100 }, (_, i) => ({
|
|
340
|
+
name: `Entity${i}`,
|
|
341
|
+
entityType: i % 2 === 0 ? 'person' : 'project',
|
|
342
|
+
observations: [`Observation ${i}`],
|
|
343
|
+
tags: i % 3 === 0 ? ['important'] : undefined,
|
|
344
|
+
importance: (i % 10) + 1,
|
|
345
|
+
}));
|
|
346
|
+
await entityManager.createEntities(entities);
|
|
347
|
+
const startTime = Date.now();
|
|
348
|
+
// Multiple complex queries
|
|
349
|
+
await rankedSearch.searchNodesRanked('Observation', ['important'], 5);
|
|
350
|
+
await booleanSearch.booleanSearch('person AND (important OR project)');
|
|
351
|
+
await fuzzySearch.fuzzySearch('Observatn', 0.7);
|
|
352
|
+
const duration = Date.now() - startTime;
|
|
353
|
+
expect(duration).toBeLessThan(400);
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
describe('Memory Efficiency', () => {
|
|
357
|
+
it('should handle 2000 entities without excessive memory', async () => {
|
|
358
|
+
// Create in batches due to 1000 entity limit
|
|
359
|
+
const batch1 = Array.from({ length: 1000 }, (_, i) => ({
|
|
360
|
+
name: `Entity${i}`,
|
|
361
|
+
entityType: 'test',
|
|
362
|
+
observations: [`Observation ${i}`],
|
|
363
|
+
}));
|
|
364
|
+
const batch2 = Array.from({ length: 1000 }, (_, i) => ({
|
|
365
|
+
name: `Entity${i + 1000}`,
|
|
366
|
+
entityType: 'test',
|
|
367
|
+
observations: [`Observation ${i + 1000}`],
|
|
368
|
+
}));
|
|
369
|
+
await entityManager.createEntities(batch1);
|
|
370
|
+
await entityManager.createEntities(batch2);
|
|
371
|
+
const graph = await storage.loadGraph();
|
|
372
|
+
expect(graph.entities).toHaveLength(2000);
|
|
373
|
+
});
|
|
374
|
+
it('should handle graph with 5000 total elements (entities + relations)', async () => {
|
|
375
|
+
// Create 1000 entities
|
|
376
|
+
const entities = Array.from({ length: 1000 }, (_, i) => ({
|
|
377
|
+
name: `Entity${i}`,
|
|
378
|
+
entityType: 'test',
|
|
379
|
+
observations: [`Observation ${i}`],
|
|
380
|
+
}));
|
|
381
|
+
await entityManager.createEntities(entities);
|
|
382
|
+
// Create 4000 relations in batches due to 1000 relation limit
|
|
383
|
+
const batch1 = Array.from({ length: 1000 }, (_, i) => ({
|
|
384
|
+
from: `Entity${i % 1000}`,
|
|
385
|
+
to: `Entity${(i + 1) % 1000}`,
|
|
386
|
+
relationType: i % 2 === 0 ? 'connects' : 'relates',
|
|
387
|
+
}));
|
|
388
|
+
const batch2 = Array.from({ length: 1000 }, (_, i) => ({
|
|
389
|
+
from: `Entity${(i + 1) % 1000}`,
|
|
390
|
+
to: `Entity${(i + 2) % 1000}`,
|
|
391
|
+
relationType: i % 2 === 0 ? 'links' : 'relates_to',
|
|
392
|
+
}));
|
|
393
|
+
const batch3 = Array.from({ length: 1000 }, (_, i) => ({
|
|
394
|
+
from: `Entity${(i + 2) % 1000}`,
|
|
395
|
+
to: `Entity${(i + 3) % 1000}`,
|
|
396
|
+
relationType: i % 2 === 0 ? 'connects_to' : 'associates',
|
|
397
|
+
}));
|
|
398
|
+
const batch4 = Array.from({ length: 1000 }, (_, i) => ({
|
|
399
|
+
from: `Entity${(i + 3) % 1000}`,
|
|
400
|
+
to: `Entity${(i + 4) % 1000}`,
|
|
401
|
+
relationType: i % 2 === 0 ? 'joins' : 'interacts',
|
|
402
|
+
}));
|
|
403
|
+
await relationManager.createRelations(batch1);
|
|
404
|
+
await relationManager.createRelations(batch2);
|
|
405
|
+
await relationManager.createRelations(batch3);
|
|
406
|
+
await relationManager.createRelations(batch4);
|
|
407
|
+
const graph = await storage.loadGraph();
|
|
408
|
+
expect(graph.entities.length + graph.relations.length).toBe(5000);
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
});
|