mcp-memory-keeper 0.10.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.
Files changed (98) hide show
  1. package/CHANGELOG.md +433 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1051 -0
  4. package/bin/mcp-memory-keeper +52 -0
  5. package/dist/__tests__/helpers/database-test-helper.js +160 -0
  6. package/dist/__tests__/helpers/test-server.js +92 -0
  7. package/dist/__tests__/integration/advanced-features.test.js +614 -0
  8. package/dist/__tests__/integration/backward-compatibility.test.js +245 -0
  9. package/dist/__tests__/integration/batchOperationsE2E.test.js +396 -0
  10. package/dist/__tests__/integration/batchOperationsHandler.test.js +1230 -0
  11. package/dist/__tests__/integration/channelManagementHandler.test.js +1291 -0
  12. package/dist/__tests__/integration/channels.test.js +376 -0
  13. package/dist/__tests__/integration/checkpoint.test.js +251 -0
  14. package/dist/__tests__/integration/concurrent-access.test.js +190 -0
  15. package/dist/__tests__/integration/context-operations.test.js +243 -0
  16. package/dist/__tests__/integration/contextDiff.test.js +852 -0
  17. package/dist/__tests__/integration/contextDiffHandler.test.js +976 -0
  18. package/dist/__tests__/integration/contextExportHandler.test.js +510 -0
  19. package/dist/__tests__/integration/contextGetPaginationDefaults.test.js +298 -0
  20. package/dist/__tests__/integration/contextReassignChannelHandler.test.js +908 -0
  21. package/dist/__tests__/integration/contextRelationshipsHandler.test.js +1151 -0
  22. package/dist/__tests__/integration/contextSearch.test.js +938 -0
  23. package/dist/__tests__/integration/contextSearchHandler.test.js +552 -0
  24. package/dist/__tests__/integration/contextWatchActual.test.js +165 -0
  25. package/dist/__tests__/integration/contextWatchHandler.test.js +1500 -0
  26. package/dist/__tests__/integration/cross-session-sharing.test.js +302 -0
  27. package/dist/__tests__/integration/database-initialization.test.js +134 -0
  28. package/dist/__tests__/integration/enhanced-context-operations.test.js +1082 -0
  29. package/dist/__tests__/integration/enhancedContextGetHandler.test.js +915 -0
  30. package/dist/__tests__/integration/enhancedContextTimelineHandler.test.js +716 -0
  31. package/dist/__tests__/integration/error-cases.test.js +407 -0
  32. package/dist/__tests__/integration/export-import.test.js +367 -0
  33. package/dist/__tests__/integration/feature-flags.test.js +542 -0
  34. package/dist/__tests__/integration/file-operations.test.js +264 -0
  35. package/dist/__tests__/integration/git-integration.test.js +237 -0
  36. package/dist/__tests__/integration/index-tools.test.js +496 -0
  37. package/dist/__tests__/integration/issue11-actual-bug-demo.test.js +304 -0
  38. package/dist/__tests__/integration/issue11-search-filters-bug.test.js +561 -0
  39. package/dist/__tests__/integration/issue12-checkpoint-restore-behavior.test.js +621 -0
  40. package/dist/__tests__/integration/issue13-key-validation.test.js +433 -0
  41. package/dist/__tests__/integration/knowledge-graph.test.js +338 -0
  42. package/dist/__tests__/integration/migrations.test.js +528 -0
  43. package/dist/__tests__/integration/multi-agent.test.js +546 -0
  44. package/dist/__tests__/integration/pagination-critical-fix.test.js +296 -0
  45. package/dist/__tests__/integration/paginationDefaultsHandler.test.js +600 -0
  46. package/dist/__tests__/integration/project-directory.test.js +283 -0
  47. package/dist/__tests__/integration/resource-cleanup.test.js +149 -0
  48. package/dist/__tests__/integration/retention.test.js +513 -0
  49. package/dist/__tests__/integration/search.test.js +333 -0
  50. package/dist/__tests__/integration/semantic-search.test.js +266 -0
  51. package/dist/__tests__/integration/server-initialization.test.js +307 -0
  52. package/dist/__tests__/integration/session-management.test.js +219 -0
  53. package/dist/__tests__/integration/simplified-sharing.test.js +346 -0
  54. package/dist/__tests__/integration/smart-compaction.test.js +230 -0
  55. package/dist/__tests__/integration/summarization.test.js +308 -0
  56. package/dist/__tests__/integration/watcher-migration-validation.test.js +544 -0
  57. package/dist/__tests__/security/input-validation.test.js +115 -0
  58. package/dist/__tests__/utils/agents.test.js +473 -0
  59. package/dist/__tests__/utils/database.test.js +177 -0
  60. package/dist/__tests__/utils/git.test.js +122 -0
  61. package/dist/__tests__/utils/knowledge-graph.test.js +297 -0
  62. package/dist/__tests__/utils/migrationHealthCheck.test.js +302 -0
  63. package/dist/__tests__/utils/project-directory-messages.test.js +188 -0
  64. package/dist/__tests__/utils/timezone-safe-dates.js +119 -0
  65. package/dist/__tests__/utils/validation.test.js +200 -0
  66. package/dist/__tests__/utils/vector-store.test.js +231 -0
  67. package/dist/handlers/contextWatchHandlers.js +206 -0
  68. package/dist/index.js +4310 -0
  69. package/dist/index.phase1.backup.js +410 -0
  70. package/dist/index.phase2.backup.js +704 -0
  71. package/dist/migrations/003_add_channels.js +174 -0
  72. package/dist/migrations/004_add_context_watch.js +151 -0
  73. package/dist/migrations/005_add_context_watch.js +98 -0
  74. package/dist/migrations/simplify-sharing.js +117 -0
  75. package/dist/repositories/BaseRepository.js +30 -0
  76. package/dist/repositories/CheckpointRepository.js +140 -0
  77. package/dist/repositories/ContextRepository.js +1873 -0
  78. package/dist/repositories/FileRepository.js +104 -0
  79. package/dist/repositories/RepositoryManager.js +62 -0
  80. package/dist/repositories/SessionRepository.js +66 -0
  81. package/dist/repositories/WatcherRepository.js +252 -0
  82. package/dist/repositories/index.js +15 -0
  83. package/dist/server.js +384 -0
  84. package/dist/test-helpers/database-helper.js +128 -0
  85. package/dist/types/entities.js +3 -0
  86. package/dist/utils/agents.js +791 -0
  87. package/dist/utils/channels.js +150 -0
  88. package/dist/utils/database.js +731 -0
  89. package/dist/utils/feature-flags.js +476 -0
  90. package/dist/utils/git.js +145 -0
  91. package/dist/utils/knowledge-graph.js +264 -0
  92. package/dist/utils/migrationHealthCheck.js +373 -0
  93. package/dist/utils/migrations.js +452 -0
  94. package/dist/utils/retention.js +460 -0
  95. package/dist/utils/timestamps.js +112 -0
  96. package/dist/utils/validation.js +296 -0
  97. package/dist/utils/vector-store.js +247 -0
  98. package/package.json +84 -0
@@ -0,0 +1,915 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const globals_1 = require("@jest/globals");
37
+ const database_1 = require("../../utils/database");
38
+ const ContextRepository_1 = require("../../repositories/ContextRepository");
39
+ const os = __importStar(require("os"));
40
+ const path = __importStar(require("path"));
41
+ const fs = __importStar(require("fs"));
42
+ const uuid_1 = require("uuid");
43
+ (0, globals_1.describe)('Enhanced Context Get Handler Integration Tests', () => {
44
+ let dbManager;
45
+ let tempDbPath;
46
+ let db;
47
+ let contextRepo;
48
+ let testSessionId;
49
+ let secondSessionId;
50
+ (0, globals_1.beforeEach)(() => {
51
+ tempDbPath = path.join(os.tmpdir(), `test-enhanced-context-get-${Date.now()}.db`);
52
+ dbManager = new database_1.DatabaseManager({
53
+ filename: tempDbPath,
54
+ maxSize: 10 * 1024 * 1024,
55
+ walMode: true,
56
+ });
57
+ db = dbManager.getDatabase();
58
+ contextRepo = new ContextRepository_1.ContextRepository(dbManager);
59
+ // Create test sessions
60
+ testSessionId = (0, uuid_1.v4)();
61
+ secondSessionId = (0, uuid_1.v4)();
62
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(testSessionId, 'Test Session');
63
+ db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(secondSessionId, 'Second Session');
64
+ });
65
+ (0, globals_1.afterEach)(() => {
66
+ dbManager.close();
67
+ try {
68
+ fs.unlinkSync(tempDbPath);
69
+ fs.unlinkSync(`${tempDbPath}-wal`);
70
+ fs.unlinkSync(`${tempDbPath}-shm`);
71
+ }
72
+ catch (_e) {
73
+ // Ignore
74
+ }
75
+ });
76
+ function createTestData() {
77
+ const now = new Date();
78
+ const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
79
+ const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
80
+ const twoDaysAgo = new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000);
81
+ const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
82
+ // Create a timestamp that's definitely yesterday
83
+ const yesterday = new Date();
84
+ yesterday.setDate(yesterday.getDate() - 1);
85
+ yesterday.setHours(12, 0, 0, 0); // Noon yesterday
86
+ const items = [
87
+ {
88
+ id: (0, uuid_1.v4)(),
89
+ session_id: testSessionId,
90
+ key: 'config.database.url',
91
+ value: 'postgresql://localhost:5432/myapp',
92
+ category: 'config',
93
+ priority: 'high',
94
+ channel: 'main',
95
+ created_at: now.toISOString(),
96
+ updated_at: now.toISOString(),
97
+ metadata: JSON.stringify({ environment: 'production' }),
98
+ size: 35,
99
+ },
100
+ // Add an item from yesterday
101
+ {
102
+ id: (0, uuid_1.v4)(),
103
+ session_id: testSessionId,
104
+ key: 'config.yesterday.item',
105
+ value: 'Created yesterday',
106
+ category: 'config',
107
+ priority: 'normal',
108
+ channel: 'main',
109
+ created_at: yesterday.toISOString(),
110
+ updated_at: yesterday.toISOString(),
111
+ size: 17,
112
+ },
113
+ {
114
+ id: (0, uuid_1.v4)(),
115
+ session_id: testSessionId,
116
+ key: 'config.cache.ttl',
117
+ value: '3600',
118
+ category: 'config',
119
+ priority: 'normal',
120
+ channel: 'main',
121
+ created_at: oneHourAgo.toISOString(),
122
+ updated_at: oneHourAgo.toISOString(),
123
+ metadata: JSON.stringify({ unit: 'seconds' }),
124
+ size: 4,
125
+ },
126
+ {
127
+ id: (0, uuid_1.v4)(),
128
+ session_id: testSessionId,
129
+ key: 'task.deploy.status',
130
+ value: 'completed',
131
+ category: 'task',
132
+ priority: 'high',
133
+ channel: 'deployment',
134
+ created_at: oneDayAgo.toISOString(),
135
+ updated_at: oneHourAgo.toISOString(),
136
+ size: 9,
137
+ },
138
+ {
139
+ id: (0, uuid_1.v4)(),
140
+ session_id: testSessionId,
141
+ key: 'task.backup.status',
142
+ value: 'pending',
143
+ category: 'task',
144
+ priority: 'low',
145
+ channel: 'maintenance',
146
+ created_at: twoDaysAgo.toISOString(),
147
+ updated_at: twoDaysAgo.toISOString(),
148
+ size: 7,
149
+ },
150
+ {
151
+ id: (0, uuid_1.v4)(),
152
+ session_id: testSessionId,
153
+ key: 'note.architecture',
154
+ value: 'The system uses a microservices architecture with Docker containers',
155
+ category: 'note',
156
+ priority: 'normal',
157
+ channel: 'documentation',
158
+ created_at: oneWeekAgo.toISOString(),
159
+ updated_at: oneWeekAgo.toISOString(),
160
+ size: 67,
161
+ },
162
+ // Private item
163
+ {
164
+ id: (0, uuid_1.v4)(),
165
+ session_id: testSessionId,
166
+ key: 'secret.api.key',
167
+ value: 'sk-1234567890abcdef',
168
+ category: 'config',
169
+ priority: 'high',
170
+ channel: 'secure',
171
+ created_at: now.toISOString(),
172
+ updated_at: now.toISOString(),
173
+ is_private: 1,
174
+ size: 18,
175
+ },
176
+ // Item from another session (public)
177
+ {
178
+ id: (0, uuid_1.v4)(),
179
+ session_id: secondSessionId,
180
+ key: 'shared.resource',
181
+ value: 'This is a shared resource',
182
+ category: 'shared',
183
+ priority: 'normal',
184
+ channel: 'public',
185
+ created_at: oneDayAgo.toISOString(),
186
+ is_private: 0,
187
+ size: 25,
188
+ },
189
+ ];
190
+ // Insert test data
191
+ const stmt = db.prepare(`
192
+ INSERT INTO context_items (
193
+ id, session_id, key, value, category, priority, channel,
194
+ created_at, updated_at, metadata, size, is_private
195
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
196
+ `);
197
+ items.forEach(item => {
198
+ stmt.run(item.id, item.session_id, item.key, item.value, item.category || null, item.priority || 'normal', item.channel || 'general', item.created_at || new Date().toISOString(), item.updated_at || item.created_at || new Date().toISOString(), item.metadata || null, item.size || Buffer.byteLength(item.value, 'utf8'), item.is_private || 0);
199
+ });
200
+ }
201
+ (0, globals_1.describe)('Backward Compatibility Tests', () => {
202
+ (0, globals_1.beforeEach)(() => {
203
+ createTestData();
204
+ });
205
+ (0, globals_1.it)('should return single value when requesting by key (backward compatible)', () => {
206
+ const result = contextRepo.getByKey(testSessionId, 'config.database.url');
207
+ (0, globals_1.expect)(result).toBeTruthy();
208
+ (0, globals_1.expect)(result.value).toBe('postgresql://localhost:5432/myapp');
209
+ // Handler should return just the value for single key requests
210
+ // This simulates the handler behavior
211
+ const handlerResponse = {
212
+ content: [{ type: 'text', text: result.value }],
213
+ };
214
+ (0, globals_1.expect)(handlerResponse.content[0].text).toBe('postgresql://localhost:5432/myapp');
215
+ });
216
+ (0, globals_1.it)('should return text format for multiple items by default', () => {
217
+ const items = contextRepo.getByCategory(testSessionId, 'config');
218
+ (0, globals_1.expect)(items.length).toBeGreaterThan(1);
219
+ // Simulate handler formatting for multiple items
220
+ const formattedItems = items
221
+ .filter(item => !item.is_private || item.session_id === testSessionId)
222
+ .map(r => `• [${r.priority}] ${r.key}: ${r.value.substring(0, 100)}${r.value.length > 100 ? '...' : ''}`)
223
+ .join('\n');
224
+ const handlerResponse = {
225
+ content: [
226
+ {
227
+ type: 'text',
228
+ text: `Found ${items.length} context items:\n\n${formattedItems}`,
229
+ },
230
+ ],
231
+ };
232
+ (0, globals_1.expect)(handlerResponse.content[0].text).toContain('Found');
233
+ (0, globals_1.expect)(handlerResponse.content[0].text).toContain('config.database.url');
234
+ (0, globals_1.expect)(handlerResponse.content[0].text).toContain('[high]');
235
+ });
236
+ (0, globals_1.it)('should handle category parameter (existing functionality)', () => {
237
+ const items = contextRepo.getByCategory(testSessionId, 'task');
238
+ (0, globals_1.expect)(items.length).toBe(2);
239
+ (0, globals_1.expect)(items.every(item => item.category === 'task')).toBe(true);
240
+ });
241
+ (0, globals_1.it)('should respect session isolation by default', () => {
242
+ const items = contextRepo.getBySessionId(testSessionId);
243
+ (0, globals_1.expect)(items.every(item => item.session_id === testSessionId)).toBe(true);
244
+ (0, globals_1.expect)(items.some(item => item.key === 'shared.resource')).toBe(false);
245
+ });
246
+ });
247
+ (0, globals_1.describe)('Metadata Inclusion Tests', () => {
248
+ (0, globals_1.beforeEach)(() => {
249
+ createTestData();
250
+ });
251
+ (0, globals_1.it)('should include metadata when includeMetadata is true', () => {
252
+ const options = {
253
+ sessionId: testSessionId,
254
+ key: 'config.database.url',
255
+ includeMetadata: true,
256
+ };
257
+ const result = contextRepo.queryEnhanced(options);
258
+ (0, globals_1.expect)(result.items.length).toBe(1);
259
+ const item = result.items[0];
260
+ // Verify all metadata fields are present
261
+ (0, globals_1.expect)(item).toHaveProperty('size');
262
+ (0, globals_1.expect)(item).toHaveProperty('created_at');
263
+ (0, globals_1.expect)(item).toHaveProperty('updated_at');
264
+ (0, globals_1.expect)(item).toHaveProperty('metadata');
265
+ (0, globals_1.expect)(item.size).toBe(35);
266
+ // Verify metadata can be parsed
267
+ if (item.metadata) {
268
+ const parsed = JSON.parse(item.metadata);
269
+ (0, globals_1.expect)(parsed).toHaveProperty('environment', 'production');
270
+ }
271
+ });
272
+ (0, globals_1.it)('should return metadata structure for multiple items', () => {
273
+ const options = {
274
+ sessionId: testSessionId,
275
+ category: 'config',
276
+ includeMetadata: true,
277
+ };
278
+ const result = contextRepo.queryEnhanced(options);
279
+ // Should return object with items array and totalCount
280
+ (0, globals_1.expect)(result).toHaveProperty('items');
281
+ (0, globals_1.expect)(result).toHaveProperty('totalCount');
282
+ (0, globals_1.expect)(result.totalCount).toBeGreaterThanOrEqual(result.items.length);
283
+ result.items.forEach(item => {
284
+ (0, globals_1.expect)(item).toHaveProperty('size');
285
+ (0, globals_1.expect)(item).toHaveProperty('created_at');
286
+ (0, globals_1.expect)(item).toHaveProperty('updated_at');
287
+ });
288
+ });
289
+ });
290
+ (0, globals_1.describe)('Sorting Tests', () => {
291
+ (0, globals_1.beforeEach)(() => {
292
+ createTestData();
293
+ });
294
+ (0, globals_1.it)('should sort by created_at descending (default)', () => {
295
+ const result = contextRepo.queryEnhanced({
296
+ sessionId: testSessionId,
297
+ sort: 'created_desc',
298
+ });
299
+ for (let i = 1; i < result.items.length; i++) {
300
+ const prevDate = new Date(result.items[i - 1].created_at);
301
+ const currDate = new Date(result.items[i].created_at);
302
+ (0, globals_1.expect)(prevDate.getTime()).toBeGreaterThanOrEqual(currDate.getTime());
303
+ }
304
+ });
305
+ (0, globals_1.it)('should sort by created_at ascending', () => {
306
+ const result = contextRepo.queryEnhanced({
307
+ sessionId: testSessionId,
308
+ sort: 'created_asc',
309
+ });
310
+ for (let i = 1; i < result.items.length; i++) {
311
+ const prevDate = new Date(result.items[i - 1].created_at);
312
+ const currDate = new Date(result.items[i].created_at);
313
+ (0, globals_1.expect)(prevDate.getTime()).toBeLessThanOrEqual(currDate.getTime());
314
+ }
315
+ });
316
+ (0, globals_1.it)('should sort by updated_at descending', () => {
317
+ const result = contextRepo.queryEnhanced({
318
+ sessionId: testSessionId,
319
+ sort: 'updated_desc',
320
+ });
321
+ // Verify items are sorted by updated_at in descending order
322
+ for (let i = 1; i < result.items.length; i++) {
323
+ const prevDate = new Date(result.items[i - 1].updated_at);
324
+ const currDate = new Date(result.items[i].updated_at);
325
+ (0, globals_1.expect)(prevDate.getTime()).toBeGreaterThanOrEqual(currDate.getTime());
326
+ }
327
+ // The most recently updated items should be first
328
+ // config.database.url and secret.api.key both have updated_at = now
329
+ (0, globals_1.expect)(['config.database.url', 'secret.api.key']).toContain(result.items[0].key);
330
+ });
331
+ (0, globals_1.it)('should sort by key ascending', () => {
332
+ const result = contextRepo.queryEnhanced({
333
+ sessionId: testSessionId,
334
+ sort: 'key_asc',
335
+ });
336
+ for (let i = 1; i < result.items.length; i++) {
337
+ (0, globals_1.expect)(result.items[i - 1].key.localeCompare(result.items[i].key)).toBeLessThanOrEqual(0);
338
+ }
339
+ });
340
+ (0, globals_1.it)('should sort by key descending', () => {
341
+ const result = contextRepo.queryEnhanced({
342
+ sessionId: testSessionId,
343
+ sort: 'key_desc',
344
+ });
345
+ for (let i = 1; i < result.items.length; i++) {
346
+ (0, globals_1.expect)(result.items[i - 1].key.localeCompare(result.items[i].key)).toBeGreaterThanOrEqual(0);
347
+ }
348
+ });
349
+ (0, globals_1.it)('should handle priority-based sorting when no sort specified', () => {
350
+ const result = contextRepo.queryEnhanced({
351
+ sessionId: testSessionId,
352
+ category: 'task',
353
+ });
354
+ // High priority items should come first
355
+ (0, globals_1.expect)(result.items[0].priority).toBe('high');
356
+ });
357
+ });
358
+ (0, globals_1.describe)('Pagination Tests', () => {
359
+ (0, globals_1.beforeEach)(() => {
360
+ createTestData();
361
+ // Add more items for pagination testing
362
+ for (let i = 0; i < 10; i++) {
363
+ db.prepare(`
364
+ INSERT INTO context_items (id, session_id, key, value, category, priority)
365
+ VALUES (?, ?, ?, ?, ?, ?)
366
+ `).run((0, uuid_1.v4)(), testSessionId, `test.item.${i}`, `Test value ${i}`, 'test', 'normal');
367
+ }
368
+ });
369
+ (0, globals_1.it)('should limit results correctly', () => {
370
+ const result = contextRepo.queryEnhanced({
371
+ sessionId: testSessionId,
372
+ limit: 5,
373
+ });
374
+ (0, globals_1.expect)(result.items.length).toBe(5);
375
+ (0, globals_1.expect)(result.totalCount).toBeGreaterThan(5);
376
+ });
377
+ (0, globals_1.it)('should handle offset correctly', () => {
378
+ const resultPage1 = contextRepo.queryEnhanced({
379
+ sessionId: testSessionId,
380
+ limit: 5,
381
+ offset: 0,
382
+ });
383
+ const resultPage2 = contextRepo.queryEnhanced({
384
+ sessionId: testSessionId,
385
+ limit: 5,
386
+ offset: 5,
387
+ });
388
+ // Ensure no overlap between pages
389
+ const page1Keys = new Set(resultPage1.items.map(item => item.key));
390
+ const page2Keys = new Set(resultPage2.items.map(item => item.key));
391
+ page2Keys.forEach(key => {
392
+ (0, globals_1.expect)(page1Keys.has(key)).toBe(false);
393
+ });
394
+ });
395
+ (0, globals_1.it)('should calculate correct pagination metadata', () => {
396
+ const pageSize = 3;
397
+ const page = 2;
398
+ const offset = (page - 1) * pageSize;
399
+ const result = contextRepo.queryEnhanced({
400
+ sessionId: testSessionId,
401
+ limit: pageSize,
402
+ offset: offset,
403
+ });
404
+ // Handler would calculate these values
405
+ const totalPages = Math.ceil(result.totalCount / pageSize);
406
+ const hasNextPage = page < totalPages;
407
+ const hasPreviousPage = page > 1;
408
+ (0, globals_1.expect)(result.items.length).toBeLessThanOrEqual(pageSize);
409
+ (0, globals_1.expect)(hasNextPage).toBe(true);
410
+ (0, globals_1.expect)(hasPreviousPage).toBe(true);
411
+ });
412
+ });
413
+ (0, globals_1.describe)('Time Filtering Tests', () => {
414
+ (0, globals_1.beforeEach)(() => {
415
+ createTestData();
416
+ });
417
+ (0, globals_1.it)('should filter by createdAfter with ISO date', () => {
418
+ const twoDaysAgo = new Date();
419
+ twoDaysAgo.setDate(twoDaysAgo.getDate() - 2);
420
+ const result = contextRepo.queryEnhanced({
421
+ sessionId: testSessionId,
422
+ createdAfter: twoDaysAgo.toISOString(),
423
+ });
424
+ result.items.forEach(item => {
425
+ (0, globals_1.expect)(new Date(item.created_at).getTime()).toBeGreaterThan(twoDaysAgo.getTime());
426
+ });
427
+ // Should include recent items but not older ones
428
+ (0, globals_1.expect)(result.items.some(item => item.key === 'config.database.url')).toBe(true);
429
+ (0, globals_1.expect)(result.items.some(item => item.key === 'note.architecture')).toBe(false);
430
+ });
431
+ (0, globals_1.it)('should filter by createdBefore with ISO date', () => {
432
+ const oneDayAgo = new Date();
433
+ oneDayAgo.setDate(oneDayAgo.getDate() - 1);
434
+ const result = contextRepo.queryEnhanced({
435
+ sessionId: testSessionId,
436
+ createdBefore: oneDayAgo.toISOString(),
437
+ });
438
+ result.items.forEach(item => {
439
+ (0, globals_1.expect)(new Date(item.created_at).getTime()).toBeLessThan(oneDayAgo.getTime());
440
+ });
441
+ // Should include older items but not recent ones
442
+ (0, globals_1.expect)(result.items.some(item => item.key === 'note.architecture')).toBe(true);
443
+ (0, globals_1.expect)(result.items.some(item => item.key === 'config.database.url')).toBe(false);
444
+ });
445
+ (0, globals_1.it)('should handle relative time for createdAfter', () => {
446
+ // Test "2 hours ago"
447
+ const result = contextRepo.queryEnhanced({
448
+ sessionId: testSessionId,
449
+ createdAfter: '2 hours ago',
450
+ });
451
+ // Should include items created within last 2 hours
452
+ (0, globals_1.expect)(result.items.some(item => item.key === 'config.database.url')).toBe(true);
453
+ (0, globals_1.expect)(result.items.some(item => item.key === 'config.cache.ttl')).toBe(true);
454
+ (0, globals_1.expect)(result.items.some(item => item.key === 'task.deploy.status')).toBe(false);
455
+ });
456
+ (0, globals_1.it)('should handle "today" relative time', () => {
457
+ const result = contextRepo.queryEnhanced({
458
+ sessionId: testSessionId,
459
+ createdAfter: 'today',
460
+ });
461
+ const today = new Date();
462
+ today.setHours(0, 0, 0, 0);
463
+ result.items.forEach(item => {
464
+ const itemDate = new Date(item.created_at);
465
+ (0, globals_1.expect)(itemDate.getTime()).toBeGreaterThanOrEqual(today.getTime());
466
+ });
467
+ });
468
+ (0, globals_1.it)('should handle "yesterday" relative time', () => {
469
+ const result = contextRepo.queryEnhanced({
470
+ sessionId: testSessionId,
471
+ createdAfter: 'yesterday',
472
+ createdBefore: 'today',
473
+ });
474
+ const today = new Date();
475
+ today.setHours(0, 0, 0, 0);
476
+ const yesterday = new Date(today);
477
+ yesterday.setDate(yesterday.getDate() - 1);
478
+ result.items.forEach(item => {
479
+ const itemDate = new Date(item.created_at);
480
+ (0, globals_1.expect)(itemDate.getTime()).toBeGreaterThanOrEqual(yesterday.getTime());
481
+ (0, globals_1.expect)(itemDate.getTime()).toBeLessThan(today.getTime());
482
+ });
483
+ });
484
+ (0, globals_1.it)('should handle combined date range', () => {
485
+ // Use a slightly larger range to account for timing
486
+ const result = contextRepo.queryEnhanced({
487
+ sessionId: testSessionId,
488
+ createdAfter: '8 days ago',
489
+ createdBefore: '1 hour ago',
490
+ });
491
+ // Should include items from the past week but not the most recent items
492
+ (0, globals_1.expect)(result.items.some(item => item.key === 'note.architecture')).toBe(true);
493
+ (0, globals_1.expect)(result.items.some(item => item.key === 'task.backup.status')).toBe(true);
494
+ (0, globals_1.expect)(result.items.some(item => item.key === 'task.deploy.status')).toBe(true);
495
+ (0, globals_1.expect)(result.items.some(item => item.key === 'config.database.url')).toBe(false); // created "now"
496
+ (0, globals_1.expect)(result.items.some(item => item.key === 'secret.api.key')).toBe(false); // created "now"
497
+ });
498
+ });
499
+ (0, globals_1.describe)('Priority Filtering Tests', () => {
500
+ (0, globals_1.beforeEach)(() => {
501
+ createTestData();
502
+ });
503
+ (0, globals_1.it)('should filter by single priority', () => {
504
+ const result = contextRepo.queryEnhanced({
505
+ sessionId: testSessionId,
506
+ priorities: ['high'],
507
+ });
508
+ (0, globals_1.expect)(result.items.every(item => item.priority === 'high')).toBe(true);
509
+ (0, globals_1.expect)(result.items.some(item => item.key === 'config.database.url')).toBe(true);
510
+ (0, globals_1.expect)(result.items.some(item => item.key === 'task.deploy.status')).toBe(true);
511
+ });
512
+ (0, globals_1.it)('should filter by multiple priorities', () => {
513
+ const result = contextRepo.queryEnhanced({
514
+ sessionId: testSessionId,
515
+ priorities: ['high', 'low'],
516
+ });
517
+ (0, globals_1.expect)(result.items.every(item => item.priority === 'high' || item.priority === 'low')).toBe(true);
518
+ (0, globals_1.expect)(result.items.some(item => item.priority === 'normal')).toBe(false);
519
+ });
520
+ (0, globals_1.it)('should handle empty priorities array', () => {
521
+ const result = contextRepo.queryEnhanced({
522
+ sessionId: testSessionId,
523
+ priorities: [],
524
+ });
525
+ // Should return all items when no priority filter
526
+ (0, globals_1.expect)(result.items.length).toBeGreaterThan(0);
527
+ });
528
+ });
529
+ (0, globals_1.describe)('Pattern Matching Tests', () => {
530
+ (0, globals_1.beforeEach)(() => {
531
+ createTestData();
532
+ });
533
+ (0, globals_1.it)('should match keys with simple pattern', () => {
534
+ const result = contextRepo.queryEnhanced({
535
+ sessionId: testSessionId,
536
+ keyPattern: 'config.*',
537
+ });
538
+ (0, globals_1.expect)(result.items.every(item => item.key.startsWith('config.'))).toBe(true);
539
+ (0, globals_1.expect)(result.items.length).toBe(3); // config.database.url, config.cache.ttl, and config.yesterday.item
540
+ });
541
+ (0, globals_1.it)('should match keys ending with pattern', () => {
542
+ const result = contextRepo.queryEnhanced({
543
+ sessionId: testSessionId,
544
+ keyPattern: '*.status',
545
+ });
546
+ (0, globals_1.expect)(result.items.every(item => item.key.endsWith('.status'))).toBe(true);
547
+ (0, globals_1.expect)(result.items.length).toBe(2); // task.deploy.status and task.backup.status
548
+ });
549
+ (0, globals_1.it)('should match keys with middle wildcard', () => {
550
+ const result = contextRepo.queryEnhanced({
551
+ sessionId: testSessionId,
552
+ keyPattern: 'task.*.status',
553
+ });
554
+ (0, globals_1.expect)(result.items.every(item => item.key.startsWith('task.') && item.key.endsWith('.status'))).toBe(true);
555
+ });
556
+ (0, globals_1.it)('should handle regex special characters in pattern', () => {
557
+ // Add item with special characters
558
+ db.prepare(`
559
+ INSERT INTO context_items (id, session_id, key, value)
560
+ VALUES (?, ?, ?, ?)
561
+ `).run((0, uuid_1.v4)(), testSessionId, 'config.db[prod].host', 'localhost');
562
+ // Pattern should handle [ ] characters
563
+ // In GLOB, we need to match literal brackets and any content between them
564
+ const result = contextRepo.queryEnhanced({
565
+ sessionId: testSessionId,
566
+ keyPattern: 'config.db*prod*.host',
567
+ });
568
+ (0, globals_1.expect)(result.items.length).toBe(1);
569
+ (0, globals_1.expect)(result.items[0].key).toBe('config.db[prod].host');
570
+ });
571
+ });
572
+ (0, globals_1.describe)('Channel Filtering Tests', () => {
573
+ (0, globals_1.beforeEach)(() => {
574
+ createTestData();
575
+ });
576
+ (0, globals_1.it)('should filter by single channel', () => {
577
+ const result = contextRepo.queryEnhanced({
578
+ sessionId: testSessionId,
579
+ channel: 'main',
580
+ });
581
+ (0, globals_1.expect)(result.items.every(item => item.channel === 'main')).toBe(true);
582
+ (0, globals_1.expect)(result.items.length).toBe(3); // includes config.yesterday.item
583
+ });
584
+ (0, globals_1.it)('should filter by multiple channels', () => {
585
+ const result = contextRepo.queryEnhanced({
586
+ sessionId: testSessionId,
587
+ channels: ['main', 'deployment'],
588
+ });
589
+ (0, globals_1.expect)(result.items.every(item => item.channel === 'main' || item.channel === 'deployment')).toBe(true);
590
+ (0, globals_1.expect)(result.items.length).toBe(4); // 3 from main + 1 from deployment
591
+ });
592
+ (0, globals_1.it)('should handle both channel and channels parameters', () => {
593
+ // When both are provided, channels array should take precedence
594
+ const result = contextRepo.queryEnhanced({
595
+ sessionId: testSessionId,
596
+ channel: 'main',
597
+ channels: ['deployment', 'maintenance'],
598
+ });
599
+ // Should only return items from channels array
600
+ (0, globals_1.expect)(result.items.every(item => item.channel === 'deployment' || item.channel === 'maintenance')).toBe(true);
601
+ (0, globals_1.expect)(result.items.some(item => item.channel === 'main')).toBe(false);
602
+ });
603
+ });
604
+ (0, globals_1.describe)('Combined Parameter Tests', () => {
605
+ (0, globals_1.beforeEach)(() => {
606
+ createTestData();
607
+ });
608
+ (0, globals_1.it)('should handle multiple filters together', () => {
609
+ const result = contextRepo.queryEnhanced({
610
+ sessionId: testSessionId,
611
+ category: 'task',
612
+ priorities: ['high', 'normal'],
613
+ createdAfter: '3 days ago',
614
+ sort: 'created_desc',
615
+ limit: 10,
616
+ });
617
+ (0, globals_1.expect)(result.items.every(item => item.category === 'task' && (item.priority === 'high' || item.priority === 'normal'))).toBe(true);
618
+ // Should include task.deploy.status but not task.backup.status (low priority)
619
+ (0, globals_1.expect)(result.items.some(item => item.key === 'task.deploy.status')).toBe(true);
620
+ (0, globals_1.expect)(result.items.some(item => item.key === 'task.backup.status')).toBe(false);
621
+ });
622
+ (0, globals_1.it)('should handle pattern with other filters', () => {
623
+ const result = contextRepo.queryEnhanced({
624
+ sessionId: testSessionId,
625
+ keyPattern: '*.status',
626
+ priorities: ['high'],
627
+ includeMetadata: true,
628
+ });
629
+ (0, globals_1.expect)(result.items.length).toBe(1);
630
+ (0, globals_1.expect)(result.items[0].key).toBe('task.deploy.status');
631
+ (0, globals_1.expect)(result.items[0]).toHaveProperty('size');
632
+ (0, globals_1.expect)(result.items[0]).toHaveProperty('created_at');
633
+ });
634
+ (0, globals_1.it)('should handle time range with channels and pagination', () => {
635
+ const result = contextRepo.queryEnhanced({
636
+ sessionId: testSessionId,
637
+ channels: ['main', 'deployment', 'maintenance'],
638
+ createdAfter: '7 days ago',
639
+ createdBefore: 'today',
640
+ sort: 'key_asc',
641
+ limit: 2,
642
+ offset: 0,
643
+ });
644
+ (0, globals_1.expect)(result.items.length).toBeLessThanOrEqual(2);
645
+ (0, globals_1.expect)(result.totalCount).toBeGreaterThanOrEqual(result.items.length);
646
+ // Verify sorting
647
+ if (result.items.length > 1) {
648
+ (0, globals_1.expect)(result.items[0].key.localeCompare(result.items[1].key)).toBeLessThanOrEqual(0);
649
+ }
650
+ });
651
+ });
652
+ (0, globals_1.describe)('Privacy and Access Control Tests', () => {
653
+ (0, globals_1.beforeEach)(() => {
654
+ createTestData();
655
+ });
656
+ (0, globals_1.it)('should include own private items', () => {
657
+ const result = contextRepo.queryEnhanced({
658
+ sessionId: testSessionId,
659
+ category: 'config',
660
+ });
661
+ // Should include private secret.api.key since it's from the same session
662
+ (0, globals_1.expect)(result.items.some(item => item.key === 'secret.api.key')).toBe(true);
663
+ });
664
+ (0, globals_1.it)('should include public items from other sessions', () => {
665
+ const result = contextRepo.getAccessibleItems(testSessionId);
666
+ // Should include shared.resource from another session
667
+ (0, globals_1.expect)(result.some(item => item.key === 'shared.resource')).toBe(true);
668
+ });
669
+ (0, globals_1.it)('should not include private items from other sessions', () => {
670
+ // Add private item in second session
671
+ db.prepare(`
672
+ INSERT INTO context_items (id, session_id, key, value, is_private)
673
+ VALUES (?, ?, ?, ?, ?)
674
+ `).run((0, uuid_1.v4)(), secondSessionId, 'other.private', 'Private data', 1);
675
+ const result = contextRepo.getAccessibleItems(testSessionId);
676
+ // Should not include other session's private item
677
+ (0, globals_1.expect)(result.some(item => item.key === 'other.private')).toBe(false);
678
+ });
679
+ });
680
+ (0, globals_1.describe)('Error Handling Tests', () => {
681
+ (0, globals_1.beforeEach)(() => {
682
+ createTestData();
683
+ });
684
+ (0, globals_1.it)('should handle non-existent session gracefully', () => {
685
+ const result = contextRepo.queryEnhanced({
686
+ sessionId: 'non-existent-session',
687
+ });
688
+ // When session doesn't exist, it returns public items from other sessions
689
+ (0, globals_1.expect)(result.items.length).toBeGreaterThan(0);
690
+ (0, globals_1.expect)(result.items.every(item => item.is_private === 0)).toBe(true);
691
+ (0, globals_1.expect)(result.items.some(item => item.key === 'shared.resource')).toBe(true);
692
+ });
693
+ (0, globals_1.it)('should handle invalid sort parameter', () => {
694
+ const result = contextRepo.queryEnhanced({
695
+ sessionId: testSessionId,
696
+ sort: 'invalid_sort',
697
+ });
698
+ // Should fall back to default sort
699
+ (0, globals_1.expect)(result.items.length).toBeGreaterThan(0);
700
+ });
701
+ (0, globals_1.it)('should handle invalid date formats', () => {
702
+ const result = contextRepo.queryEnhanced({
703
+ sessionId: testSessionId,
704
+ createdAfter: 'invalid date',
705
+ });
706
+ // Should treat as literal string and likely return no results
707
+ (0, globals_1.expect)(result.items).toEqual([]);
708
+ });
709
+ (0, globals_1.it)('should handle negative offset', () => {
710
+ const result = contextRepo.queryEnhanced({
711
+ sessionId: testSessionId,
712
+ limit: 5,
713
+ offset: -1,
714
+ });
715
+ // Should treat negative offset as 0
716
+ (0, globals_1.expect)(result.items.length).toBeGreaterThan(0);
717
+ });
718
+ (0, globals_1.it)('should handle very large limit', () => {
719
+ const result = contextRepo.queryEnhanced({
720
+ sessionId: testSessionId,
721
+ limit: 999999,
722
+ });
723
+ // Should return all available items
724
+ (0, globals_1.expect)(result.items.length).toBe(result.totalCount);
725
+ });
726
+ (0, globals_1.it)('should handle SQL injection attempts in keyPattern', () => {
727
+ const maliciousPattern = "'; DROP TABLE context_items; --";
728
+ // Should not throw and should not damage database
729
+ const result = contextRepo.queryEnhanced({
730
+ sessionId: testSessionId,
731
+ keyPattern: maliciousPattern,
732
+ });
733
+ // Verify table still exists
734
+ const tableExists = db
735
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='context_items'")
736
+ .get();
737
+ (0, globals_1.expect)(tableExists).toBeTruthy();
738
+ (0, globals_1.expect)(result.items).toEqual([]);
739
+ });
740
+ });
741
+ (0, globals_1.describe)('Handler Response Format Tests', () => {
742
+ (0, globals_1.beforeEach)(() => {
743
+ createTestData();
744
+ });
745
+ (0, globals_1.it)('should format single item response correctly', () => {
746
+ const item = contextRepo.getByKey(testSessionId, 'config.database.url');
747
+ // Handler returns just the value for single key request
748
+ const handlerResponse = {
749
+ content: [
750
+ {
751
+ type: 'text',
752
+ text: item.value,
753
+ },
754
+ ],
755
+ };
756
+ (0, globals_1.expect)(handlerResponse.content[0].text).toBe('postgresql://localhost:5432/myapp');
757
+ });
758
+ (0, globals_1.it)('should format multiple items without metadata', () => {
759
+ const result = contextRepo.queryEnhanced({
760
+ sessionId: testSessionId,
761
+ category: 'config',
762
+ });
763
+ // Handler formats as text list
764
+ const formattedItems = result.items
765
+ .map(r => `• [${r.priority}] ${r.key}: ${r.value.substring(0, 100)}${r.value.length > 100 ? '...' : ''}`)
766
+ .join('\n');
767
+ const handlerResponse = {
768
+ content: [
769
+ {
770
+ type: 'text',
771
+ text: `Found ${result.items.length} context items:\n\n${formattedItems}`,
772
+ },
773
+ ],
774
+ };
775
+ (0, globals_1.expect)(handlerResponse.content[0].text).toContain('Found');
776
+ (0, globals_1.expect)(handlerResponse.content[0].text).toContain('• [');
777
+ (0, globals_1.expect)(handlerResponse.content[0].text.split('\n').length).toBeGreaterThan(2);
778
+ });
779
+ (0, globals_1.it)('should format response with metadata as JSON', () => {
780
+ const result = contextRepo.queryEnhanced({
781
+ sessionId: testSessionId,
782
+ category: 'config',
783
+ includeMetadata: true,
784
+ limit: 10,
785
+ offset: 0,
786
+ });
787
+ // Handler returns structured data with metadata
788
+ const handlerResponse = {
789
+ content: [
790
+ {
791
+ type: 'text',
792
+ text: JSON.stringify({
793
+ items: result.items.map(item => ({
794
+ key: item.key,
795
+ value: item.value,
796
+ category: item.category,
797
+ priority: item.priority,
798
+ channel: item.channel,
799
+ metadata: item.metadata ? JSON.parse(item.metadata) : null,
800
+ size: item.size,
801
+ created_at: item.created_at,
802
+ updated_at: item.updated_at,
803
+ })),
804
+ pagination: {
805
+ totalCount: result.totalCount,
806
+ page: 1,
807
+ pageSize: 10,
808
+ totalPages: Math.ceil(result.totalCount / 10),
809
+ hasNextPage: result.totalCount > 10,
810
+ hasPreviousPage: false,
811
+ },
812
+ }, null, 2),
813
+ },
814
+ ],
815
+ };
816
+ const parsed = JSON.parse(handlerResponse.content[0].text);
817
+ (0, globals_1.expect)(parsed).toHaveProperty('items');
818
+ (0, globals_1.expect)(parsed).toHaveProperty('pagination');
819
+ (0, globals_1.expect)(parsed.pagination).toHaveProperty('totalCount');
820
+ (0, globals_1.expect)(parsed.items[0]).toHaveProperty('size');
821
+ (0, globals_1.expect)(parsed.items[0]).toHaveProperty('created_at');
822
+ });
823
+ (0, globals_1.it)('should handle empty results gracefully', () => {
824
+ const _result = contextRepo.queryEnhanced({
825
+ sessionId: testSessionId,
826
+ key: 'non-existent-key',
827
+ });
828
+ const handlerResponse = {
829
+ content: [
830
+ {
831
+ type: 'text',
832
+ text: 'No matching context found',
833
+ },
834
+ ],
835
+ };
836
+ (0, globals_1.expect)(handlerResponse.content[0].text).toBe('No matching context found');
837
+ });
838
+ });
839
+ (0, globals_1.describe)('Performance and Edge Cases', () => {
840
+ (0, globals_1.it)('should handle large number of items efficiently', () => {
841
+ // Add 100 items
842
+ for (let i = 0; i < 100; i++) {
843
+ db.prepare(`
844
+ INSERT INTO context_items (id, session_id, key, value, priority)
845
+ VALUES (?, ?, ?, ?, ?)
846
+ `).run((0, uuid_1.v4)(), testSessionId, `perf.test.${i.toString().padStart(3, '0')}`, `Performance test value ${i}`, i % 3 === 0 ? 'high' : 'normal');
847
+ }
848
+ const startTime = Date.now();
849
+ const result = contextRepo.queryEnhanced({
850
+ sessionId: testSessionId,
851
+ keyPattern: 'perf.test.*',
852
+ priorities: ['high'],
853
+ sort: 'key_desc',
854
+ limit: 20,
855
+ });
856
+ const endTime = Date.now();
857
+ (0, globals_1.expect)(result.items.length).toBe(20);
858
+ (0, globals_1.expect)(result.totalCount).toBeGreaterThan(20);
859
+ (0, globals_1.expect)(endTime - startTime).toBeLessThan(100); // Should complete within 100ms
860
+ });
861
+ (0, globals_1.it)('should handle unicode and special characters in values', () => {
862
+ const unicodeKey = 'unicode.test';
863
+ const unicodeValue = '🚀 Unicode test with émojis and spëcial çharacters';
864
+ db.prepare(`
865
+ INSERT INTO context_items (id, session_id, key, value)
866
+ VALUES (?, ?, ?, ?)
867
+ `).run((0, uuid_1.v4)(), testSessionId, unicodeKey, unicodeValue);
868
+ const result = contextRepo.queryEnhanced({
869
+ sessionId: testSessionId,
870
+ key: unicodeKey,
871
+ });
872
+ (0, globals_1.expect)(result.items.length).toBe(1);
873
+ (0, globals_1.expect)(result.items[0].value).toBe(unicodeValue);
874
+ });
875
+ (0, globals_1.it)('should handle very long values', () => {
876
+ const longValue = 'A'.repeat(10000);
877
+ db.prepare(`
878
+ INSERT INTO context_items (id, session_id, key, value, size)
879
+ VALUES (?, ?, ?, ?, ?)
880
+ `).run((0, uuid_1.v4)(), testSessionId, 'long.value', longValue, Buffer.byteLength(longValue, 'utf8'));
881
+ const result = contextRepo.queryEnhanced({
882
+ sessionId: testSessionId,
883
+ key: 'long.value',
884
+ includeMetadata: true,
885
+ });
886
+ (0, globals_1.expect)(result.items.length).toBe(1);
887
+ (0, globals_1.expect)(result.items[0].value.length).toBe(10000);
888
+ (0, globals_1.expect)(result.items[0].size).toBe(10000);
889
+ });
890
+ (0, globals_1.it)('should handle all parameters at once', () => {
891
+ const result = contextRepo.queryEnhanced({
892
+ sessionId: testSessionId,
893
+ key: undefined,
894
+ category: 'config',
895
+ channel: undefined,
896
+ channels: ['main', 'secure'],
897
+ sort: 'created_desc',
898
+ limit: 5,
899
+ offset: 0,
900
+ createdAfter: '7 days ago',
901
+ createdBefore: 'now',
902
+ keyPattern: 'config.*',
903
+ priorities: ['high', 'normal'],
904
+ includeMetadata: true,
905
+ });
906
+ // Should apply all filters correctly
907
+ (0, globals_1.expect)(result.items.every(item => item.category === 'config' &&
908
+ item.key.startsWith('config.') &&
909
+ (item.channel === 'main' || item.channel === 'secure') &&
910
+ (item.priority === 'high' || item.priority === 'normal'))).toBe(true);
911
+ (0, globals_1.expect)(result).toHaveProperty('totalCount');
912
+ (0, globals_1.expect)(result.items.length).toBeLessThanOrEqual(5);
913
+ });
914
+ });
915
+ });