@smallironman/mcp-memory-keeper 0.12.2-fork1
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/CHANGELOG.md +542 -0
- package/LICENSE +21 -0
- package/README.md +1281 -0
- package/bin/mcp-memory-keeper +54 -0
- package/dist/__tests__/e2e/issue33-reproduce.test.js +234 -0
- package/dist/__tests__/e2e/server-e2e.test.js +341 -0
- package/dist/__tests__/helpers/database-test-helper.js +160 -0
- package/dist/__tests__/helpers/test-server.js +92 -0
- package/dist/__tests__/integration/advanced-features.test.js +614 -0
- package/dist/__tests__/integration/backward-compatibility.test.js +245 -0
- package/dist/__tests__/integration/batchOperationsE2E.test.js +396 -0
- package/dist/__tests__/integration/batchOperationsHandler.test.js +1230 -0
- package/dist/__tests__/integration/channelManagementHandler.test.js +1291 -0
- package/dist/__tests__/integration/channels.test.js +376 -0
- package/dist/__tests__/integration/checkpoint.test.js +251 -0
- package/dist/__tests__/integration/concurrent-access.test.js +190 -0
- package/dist/__tests__/integration/context-operations.test.js +243 -0
- package/dist/__tests__/integration/contextDiff.test.js +852 -0
- package/dist/__tests__/integration/contextDiffHandler.test.js +976 -0
- package/dist/__tests__/integration/contextExportHandler.test.js +510 -0
- package/dist/__tests__/integration/contextGetPaginationDefaults.test.js +298 -0
- package/dist/__tests__/integration/contextReassignChannelHandler.test.js +908 -0
- package/dist/__tests__/integration/contextRelationshipsHandler.test.js +1151 -0
- package/dist/__tests__/integration/contextSearch.test.js +1054 -0
- package/dist/__tests__/integration/contextSearchHandler.test.js +552 -0
- package/dist/__tests__/integration/contextWatchActual.test.js +165 -0
- package/dist/__tests__/integration/contextWatchHandler.test.js +1500 -0
- package/dist/__tests__/integration/database-initialization.test.js +134 -0
- package/dist/__tests__/integration/enhanced-context-operations.test.js +1082 -0
- package/dist/__tests__/integration/enhancedContextGetHandler.test.js +915 -0
- package/dist/__tests__/integration/enhancedContextTimelineHandler.test.js +716 -0
- package/dist/__tests__/integration/error-cases.test.js +411 -0
- package/dist/__tests__/integration/export-import.test.js +367 -0
- package/dist/__tests__/integration/feature-flags.test.js +542 -0
- package/dist/__tests__/integration/file-operations.test.js +264 -0
- package/dist/__tests__/integration/filterBySessionId.test.js +251 -0
- package/dist/__tests__/integration/git-integration.test.js +241 -0
- package/dist/__tests__/integration/index-tools.test.js +496 -0
- package/dist/__tests__/integration/issue11-actual-bug-demo.test.js +304 -0
- package/dist/__tests__/integration/issue11-search-filters-bug.test.js +561 -0
- package/dist/__tests__/integration/issue12-checkpoint-restore-behavior.test.js +621 -0
- package/dist/__tests__/integration/issue13-key-validation.test.js +433 -0
- package/dist/__tests__/integration/issue24-final-fix.test.js +241 -0
- package/dist/__tests__/integration/issue24-fix-validation.test.js +158 -0
- package/dist/__tests__/integration/issue24-reproduce.test.js +225 -0
- package/dist/__tests__/integration/issue24-token-limit.test.js +199 -0
- package/dist/__tests__/integration/issue33-array-items-schema.test.js +165 -0
- package/dist/__tests__/integration/knowledge-graph.test.js +338 -0
- package/dist/__tests__/integration/migrations.test.js +528 -0
- package/dist/__tests__/integration/multi-agent.test.js +546 -0
- package/dist/__tests__/integration/pagination-critical-fix.test.js +296 -0
- package/dist/__tests__/integration/paginationDefaultsHandler.test.js +600 -0
- package/dist/__tests__/integration/project-directory.test.js +291 -0
- package/dist/__tests__/integration/resource-cleanup.test.js +149 -0
- package/dist/__tests__/integration/retention.test.js +513 -0
- package/dist/__tests__/integration/search.test.js +333 -0
- package/dist/__tests__/integration/semantic-search.test.js +266 -0
- package/dist/__tests__/integration/server-initialization.test.js +305 -0
- package/dist/__tests__/integration/session-management.test.js +219 -0
- package/dist/__tests__/integration/simplified-sharing.test.js +346 -0
- package/dist/__tests__/integration/smart-compaction.test.js +230 -0
- package/dist/__tests__/integration/summarization.test.js +308 -0
- package/dist/__tests__/integration/tokenLimitEnforcement.test.js +134 -0
- package/dist/__tests__/integration/tool-profiles-integration.test.js +150 -0
- package/dist/__tests__/integration/watcher-migration-validation.test.js +544 -0
- package/dist/__tests__/security/input-validation.test.js +115 -0
- package/dist/__tests__/utils/agents.test.js +473 -0
- package/dist/__tests__/utils/database.test.js +177 -0
- package/dist/__tests__/utils/git.test.js +122 -0
- package/dist/__tests__/utils/knowledge-graph.test.js +297 -0
- package/dist/__tests__/utils/migrationHealthCheck.test.js +302 -0
- package/dist/__tests__/utils/project-directory-messages.test.js +192 -0
- package/dist/__tests__/utils/timezone-safe-dates.js +119 -0
- package/dist/__tests__/utils/token-limits.test.js +225 -0
- package/dist/__tests__/utils/tool-profiles.test.js +374 -0
- package/dist/__tests__/utils/validation.test.js +200 -0
- package/dist/__tests__/utils/vector-store.test.js +231 -0
- package/dist/handlers/contextWatchHandlers.js +206 -0
- package/dist/index.js +4425 -0
- package/dist/migrations/003_add_channels.js +174 -0
- package/dist/migrations/004_add_context_watch.js +151 -0
- package/dist/migrations/005_add_context_watch.js +98 -0
- package/dist/migrations/simplify-sharing.js +117 -0
- package/dist/repositories/BaseRepository.js +30 -0
- package/dist/repositories/CheckpointRepository.js +140 -0
- package/dist/repositories/ContextRepository.js +2017 -0
- package/dist/repositories/FileRepository.js +104 -0
- package/dist/repositories/RepositoryManager.js +62 -0
- package/dist/repositories/SessionRepository.js +66 -0
- package/dist/repositories/WatcherRepository.js +252 -0
- package/dist/repositories/index.js +15 -0
- package/dist/test-helpers/database-helper.js +128 -0
- package/dist/types/entities.js +3 -0
- package/dist/utils/agents.js +791 -0
- package/dist/utils/channels.js +150 -0
- package/dist/utils/database.js +780 -0
- package/dist/utils/feature-flags.js +476 -0
- package/dist/utils/git.js +145 -0
- package/dist/utils/knowledge-graph.js +264 -0
- package/dist/utils/migrationHealthCheck.js +373 -0
- package/dist/utils/migrations.js +452 -0
- package/dist/utils/retention.js +460 -0
- package/dist/utils/timestamps.js +112 -0
- package/dist/utils/token-limits.js +350 -0
- package/dist/utils/tool-profiles.js +242 -0
- package/dist/utils/validation.js +296 -0
- package/dist/utils/vector-store.js +247 -0
- package/examples/config.json +31 -0
- package/examples/project-directory-setup.md +114 -0
- package/package.json +85 -0
|
@@ -0,0 +1,552 @@
|
|
|
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)('Context Search Handler Integration Tests', () => {
|
|
44
|
+
let dbManager;
|
|
45
|
+
let tempDbPath;
|
|
46
|
+
let db;
|
|
47
|
+
let contextRepo;
|
|
48
|
+
let testSessionId;
|
|
49
|
+
(0, globals_1.beforeEach)(() => {
|
|
50
|
+
tempDbPath = path.join(os.tmpdir(), `test-context-search-handler-${Date.now()}.db`);
|
|
51
|
+
dbManager = new database_1.DatabaseManager({
|
|
52
|
+
filename: tempDbPath,
|
|
53
|
+
maxSize: 10 * 1024 * 1024,
|
|
54
|
+
walMode: true,
|
|
55
|
+
});
|
|
56
|
+
db = dbManager.getDatabase();
|
|
57
|
+
contextRepo = new ContextRepository_1.ContextRepository(dbManager);
|
|
58
|
+
// Create test session
|
|
59
|
+
testSessionId = (0, uuid_1.v4)();
|
|
60
|
+
db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(testSessionId, 'Test Session');
|
|
61
|
+
});
|
|
62
|
+
(0, globals_1.afterEach)(() => {
|
|
63
|
+
dbManager.close();
|
|
64
|
+
try {
|
|
65
|
+
fs.unlinkSync(tempDbPath);
|
|
66
|
+
fs.unlinkSync(`${tempDbPath}-wal`);
|
|
67
|
+
fs.unlinkSync(`${tempDbPath}-shm`);
|
|
68
|
+
}
|
|
69
|
+
catch (_e) {
|
|
70
|
+
// Ignore
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
(0, globals_1.describe)('Enhanced Search Method', () => {
|
|
74
|
+
(0, globals_1.beforeEach)(() => {
|
|
75
|
+
// Create comprehensive test data
|
|
76
|
+
const now = new Date();
|
|
77
|
+
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
|
78
|
+
const lastWeek = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
79
|
+
const items = [
|
|
80
|
+
{
|
|
81
|
+
id: (0, uuid_1.v4)(),
|
|
82
|
+
session_id: testSessionId,
|
|
83
|
+
key: 'auth_config',
|
|
84
|
+
value: 'Authentication configuration for the app',
|
|
85
|
+
category: 'config',
|
|
86
|
+
priority: 'high',
|
|
87
|
+
channel: 'main',
|
|
88
|
+
created_at: now.toISOString(),
|
|
89
|
+
metadata: JSON.stringify({ tags: ['auth', 'config'] }),
|
|
90
|
+
size: 40,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: (0, uuid_1.v4)(),
|
|
94
|
+
session_id: testSessionId,
|
|
95
|
+
key: 'db_auth_connection',
|
|
96
|
+
value: 'Database connection string with auth params',
|
|
97
|
+
category: 'config',
|
|
98
|
+
priority: 'normal',
|
|
99
|
+
channel: 'feature/auth',
|
|
100
|
+
created_at: yesterday.toISOString(),
|
|
101
|
+
metadata: JSON.stringify({ tags: ['db', 'auth'] }),
|
|
102
|
+
size: 45,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: (0, uuid_1.v4)(),
|
|
106
|
+
session_id: testSessionId,
|
|
107
|
+
key: 'user_model',
|
|
108
|
+
value: 'User model with authentication methods',
|
|
109
|
+
category: 'code',
|
|
110
|
+
priority: 'high',
|
|
111
|
+
channel: 'main',
|
|
112
|
+
created_at: lastWeek.toISOString(),
|
|
113
|
+
metadata: null,
|
|
114
|
+
size: 38,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: (0, uuid_1.v4)(),
|
|
118
|
+
session_id: testSessionId,
|
|
119
|
+
key: 'api_endpoints',
|
|
120
|
+
value: 'API endpoints documentation',
|
|
121
|
+
category: 'docs',
|
|
122
|
+
priority: 'normal',
|
|
123
|
+
channel: 'main',
|
|
124
|
+
created_at: now.toISOString(),
|
|
125
|
+
is_private: 1,
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
// Insert test data
|
|
129
|
+
const stmt = db.prepare(`
|
|
130
|
+
INSERT INTO context_items (
|
|
131
|
+
id, session_id, key, value, category, priority, channel,
|
|
132
|
+
created_at, metadata, size, is_private
|
|
133
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
134
|
+
`);
|
|
135
|
+
items.forEach(item => {
|
|
136
|
+
stmt.run(item.id, item.session_id, item.key, item.value, item.category || null, item.priority || 'normal', item.channel || null, item.created_at || new Date().toISOString(), item.metadata || null, item.size || Buffer.byteLength(item.value, 'utf8'), item.is_private || 0);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
(0, globals_1.it)('should search with basic query maintaining backward compatibility', () => {
|
|
140
|
+
// Test the existing search method
|
|
141
|
+
const results = contextRepo.search('auth', testSessionId, true);
|
|
142
|
+
(0, globals_1.expect)(results.length).toBeGreaterThanOrEqual(3); // auth_config, db_auth_connection, user_model
|
|
143
|
+
(0, globals_1.expect)(results.every(r => r.key.includes('auth') || r.value.includes('auth') || r.value.includes('Auth'))).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
(0, globals_1.it)('should handle enhanced search with time filtering', () => {
|
|
146
|
+
const twoDaysAgo = new Date();
|
|
147
|
+
twoDaysAgo.setDate(twoDaysAgo.getDate() - 2);
|
|
148
|
+
// Simulate enhanced search parameters
|
|
149
|
+
const searchParams = {
|
|
150
|
+
query: 'auth',
|
|
151
|
+
sessionId: testSessionId,
|
|
152
|
+
createdAfter: twoDaysAgo.toISOString(),
|
|
153
|
+
searchIn: ['key', 'value'],
|
|
154
|
+
};
|
|
155
|
+
// Build query similar to how enhanced search would work
|
|
156
|
+
let sql = `
|
|
157
|
+
SELECT * FROM context_items
|
|
158
|
+
WHERE session_id = ?
|
|
159
|
+
AND (key LIKE ? OR value LIKE ?)
|
|
160
|
+
AND created_at > ?
|
|
161
|
+
ORDER BY priority DESC, created_at DESC
|
|
162
|
+
`;
|
|
163
|
+
const results = db
|
|
164
|
+
.prepare(sql)
|
|
165
|
+
.all(searchParams.sessionId, `%${searchParams.query}%`, `%${searchParams.query}%`, searchParams.createdAfter);
|
|
166
|
+
(0, globals_1.expect)(results.length).toBe(2); // auth_config and db_auth_connection (not user_model which is older)
|
|
167
|
+
});
|
|
168
|
+
(0, globals_1.it)('should handle enhanced search with channel filtering', () => {
|
|
169
|
+
const searchParams = {
|
|
170
|
+
query: 'auth',
|
|
171
|
+
sessionId: testSessionId,
|
|
172
|
+
channel: 'feature/auth',
|
|
173
|
+
};
|
|
174
|
+
let sql = `
|
|
175
|
+
SELECT * FROM context_items
|
|
176
|
+
WHERE session_id = ?
|
|
177
|
+
AND (key LIKE ? OR value LIKE ?)
|
|
178
|
+
AND channel = ?
|
|
179
|
+
ORDER BY priority DESC, created_at DESC
|
|
180
|
+
`;
|
|
181
|
+
const results = db
|
|
182
|
+
.prepare(sql)
|
|
183
|
+
.all(searchParams.sessionId, `%${searchParams.query}%`, `%${searchParams.query}%`, searchParams.channel);
|
|
184
|
+
(0, globals_1.expect)(results.length).toBe(1);
|
|
185
|
+
(0, globals_1.expect)(results[0].key).toBe('db_auth_connection');
|
|
186
|
+
});
|
|
187
|
+
(0, globals_1.it)('should handle enhanced search with multiple channels', () => {
|
|
188
|
+
const searchParams = {
|
|
189
|
+
query: 'auth',
|
|
190
|
+
sessionId: testSessionId,
|
|
191
|
+
channels: ['main', 'feature/auth'],
|
|
192
|
+
};
|
|
193
|
+
const placeholders = searchParams.channels.map(() => '?').join(',');
|
|
194
|
+
let sql = `
|
|
195
|
+
SELECT * FROM context_items
|
|
196
|
+
WHERE session_id = ?
|
|
197
|
+
AND (key LIKE ? OR value LIKE ?)
|
|
198
|
+
AND channel IN (${placeholders})
|
|
199
|
+
ORDER BY priority DESC, created_at DESC
|
|
200
|
+
`;
|
|
201
|
+
const results = db
|
|
202
|
+
.prepare(sql)
|
|
203
|
+
.all(searchParams.sessionId, `%${searchParams.query}%`, `%${searchParams.query}%`, ...searchParams.channels);
|
|
204
|
+
(0, globals_1.expect)(results.length).toBe(3); // All items with 'auth' in main or feature/auth channels
|
|
205
|
+
});
|
|
206
|
+
(0, globals_1.it)('should handle sort parameter correctly', () => {
|
|
207
|
+
const searchParams = {
|
|
208
|
+
query: 'auth',
|
|
209
|
+
sessionId: testSessionId,
|
|
210
|
+
sort: 'key_asc',
|
|
211
|
+
};
|
|
212
|
+
let sql = `
|
|
213
|
+
SELECT * FROM context_items
|
|
214
|
+
WHERE session_id = ?
|
|
215
|
+
AND (key LIKE ? OR value LIKE ?)
|
|
216
|
+
ORDER BY key ASC
|
|
217
|
+
`;
|
|
218
|
+
const results = db
|
|
219
|
+
.prepare(sql)
|
|
220
|
+
.all(searchParams.sessionId, `%${searchParams.query}%`, `%${searchParams.query}%`);
|
|
221
|
+
(0, globals_1.expect)(results[0].key).toBe('auth_config');
|
|
222
|
+
(0, globals_1.expect)(results[1].key).toBe('db_auth_connection');
|
|
223
|
+
});
|
|
224
|
+
(0, globals_1.it)('should include metadata when requested', () => {
|
|
225
|
+
const searchParams = {
|
|
226
|
+
query: 'auth',
|
|
227
|
+
sessionId: testSessionId,
|
|
228
|
+
includeMetadata: true,
|
|
229
|
+
};
|
|
230
|
+
const results = db
|
|
231
|
+
.prepare(`
|
|
232
|
+
SELECT *, size FROM context_items
|
|
233
|
+
WHERE session_id = ?
|
|
234
|
+
AND (key LIKE ? OR value LIKE ?)
|
|
235
|
+
ORDER BY created_at DESC
|
|
236
|
+
`)
|
|
237
|
+
.all(searchParams.sessionId, `%${searchParams.query}%`, `%${searchParams.query}%`);
|
|
238
|
+
results.forEach((item) => {
|
|
239
|
+
if (searchParams.includeMetadata) {
|
|
240
|
+
// Verify metadata structure
|
|
241
|
+
if (item.metadata) {
|
|
242
|
+
const parsed = JSON.parse(item.metadata);
|
|
243
|
+
(0, globals_1.expect)(parsed).toHaveProperty('tags');
|
|
244
|
+
}
|
|
245
|
+
// Verify size is included
|
|
246
|
+
(0, globals_1.expect)(item.size).toBeGreaterThan(0);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
(0, globals_1.it)('should handle pagination correctly', () => {
|
|
251
|
+
const searchParams = {
|
|
252
|
+
query: 'auth',
|
|
253
|
+
sessionId: testSessionId,
|
|
254
|
+
limit: 2,
|
|
255
|
+
offset: 1,
|
|
256
|
+
};
|
|
257
|
+
// Get total count first
|
|
258
|
+
const countResult = db
|
|
259
|
+
.prepare(`
|
|
260
|
+
SELECT COUNT(*) as count FROM context_items
|
|
261
|
+
WHERE session_id = ?
|
|
262
|
+
AND (key LIKE ? OR value LIKE ?)
|
|
263
|
+
`)
|
|
264
|
+
.get(searchParams.sessionId, `%${searchParams.query}%`, `%${searchParams.query}%`);
|
|
265
|
+
const totalCount = countResult.count;
|
|
266
|
+
// Get paginated results
|
|
267
|
+
const results = db
|
|
268
|
+
.prepare(`
|
|
269
|
+
SELECT * FROM context_items
|
|
270
|
+
WHERE session_id = ?
|
|
271
|
+
AND (key LIKE ? OR value LIKE ?)
|
|
272
|
+
ORDER BY created_at DESC
|
|
273
|
+
LIMIT ? OFFSET ?
|
|
274
|
+
`)
|
|
275
|
+
.all(searchParams.sessionId, `%${searchParams.query}%`, `%${searchParams.query}%`, searchParams.limit, searchParams.offset);
|
|
276
|
+
(0, globals_1.expect)(results.length).toBeLessThanOrEqual(searchParams.limit);
|
|
277
|
+
(0, globals_1.expect)(totalCount).toBeGreaterThanOrEqual(3);
|
|
278
|
+
});
|
|
279
|
+
(0, globals_1.it)('should handle keyPattern for regex-like matching', () => {
|
|
280
|
+
const searchParams = {
|
|
281
|
+
query: 'config', // Search in value
|
|
282
|
+
sessionId: testSessionId,
|
|
283
|
+
keyPattern: '*_config', // GLOB pattern for keys ending with _config
|
|
284
|
+
};
|
|
285
|
+
let sql = `
|
|
286
|
+
SELECT * FROM context_items
|
|
287
|
+
WHERE session_id = ?
|
|
288
|
+
AND (key LIKE ? OR value LIKE ?)
|
|
289
|
+
AND key GLOB ?
|
|
290
|
+
ORDER BY created_at DESC
|
|
291
|
+
`;
|
|
292
|
+
const results = db
|
|
293
|
+
.prepare(sql)
|
|
294
|
+
.all(searchParams.sessionId, `%${searchParams.query}%`, `%${searchParams.query}%`, searchParams.keyPattern);
|
|
295
|
+
(0, globals_1.expect)(results.length).toBe(1);
|
|
296
|
+
(0, globals_1.expect)(results[0].key).toBe('auth_config');
|
|
297
|
+
});
|
|
298
|
+
(0, globals_1.it)('should filter by priorities', () => {
|
|
299
|
+
const searchParams = {
|
|
300
|
+
query: 'auth',
|
|
301
|
+
sessionId: testSessionId,
|
|
302
|
+
priorities: ['high'],
|
|
303
|
+
};
|
|
304
|
+
const placeholders = searchParams.priorities.map(() => '?').join(',');
|
|
305
|
+
let sql = `
|
|
306
|
+
SELECT * FROM context_items
|
|
307
|
+
WHERE session_id = ?
|
|
308
|
+
AND (key LIKE ? OR value LIKE ?)
|
|
309
|
+
AND priority IN (${placeholders})
|
|
310
|
+
ORDER BY created_at DESC
|
|
311
|
+
`;
|
|
312
|
+
const results = db
|
|
313
|
+
.prepare(sql)
|
|
314
|
+
.all(searchParams.sessionId, `%${searchParams.query}%`, `%${searchParams.query}%`, ...searchParams.priorities);
|
|
315
|
+
(0, globals_1.expect)(results.length).toBe(2); // auth_config and user_model
|
|
316
|
+
(0, globals_1.expect)(results.every((r) => r.priority === 'high')).toBe(true);
|
|
317
|
+
});
|
|
318
|
+
(0, globals_1.it)('should respect privacy settings', () => {
|
|
319
|
+
// Search without session (should not see private items)
|
|
320
|
+
const publicResults = db
|
|
321
|
+
.prepare(`
|
|
322
|
+
SELECT * FROM context_items
|
|
323
|
+
WHERE is_private = 0
|
|
324
|
+
ORDER BY created_at DESC
|
|
325
|
+
`)
|
|
326
|
+
.all();
|
|
327
|
+
(0, globals_1.expect)(publicResults.some((r) => r.key === 'api_endpoints')).toBe(false);
|
|
328
|
+
// Search with session (should see own private items)
|
|
329
|
+
const sessionResults = db
|
|
330
|
+
.prepare(`
|
|
331
|
+
SELECT * FROM context_items
|
|
332
|
+
WHERE (is_private = 0 OR session_id = ?)
|
|
333
|
+
ORDER BY created_at DESC
|
|
334
|
+
`)
|
|
335
|
+
.all(testSessionId);
|
|
336
|
+
(0, globals_1.expect)(sessionResults.some((r) => r.key === 'api_endpoints')).toBe(true);
|
|
337
|
+
});
|
|
338
|
+
(0, globals_1.it)('should handle relative time parsing', () => {
|
|
339
|
+
// Add a recent item
|
|
340
|
+
const oneHourAgo = new Date();
|
|
341
|
+
oneHourAgo.setHours(oneHourAgo.getHours() - 1);
|
|
342
|
+
db.prepare('INSERT INTO context_items (id, session_id, key, value, created_at) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, 'recent_auth_task', 'Recent authentication task', oneHourAgo.toISOString());
|
|
343
|
+
// Simulate relative time parsing
|
|
344
|
+
const relativeTime = '2 hours ago';
|
|
345
|
+
const match = relativeTime.match(/^(\d+) hours? ago$/);
|
|
346
|
+
(0, globals_1.expect)(match).toBeTruthy();
|
|
347
|
+
const hours = parseInt(match[1]);
|
|
348
|
+
const cutoffTime = new Date();
|
|
349
|
+
cutoffTime.setHours(cutoffTime.getHours() - hours);
|
|
350
|
+
const results = db
|
|
351
|
+
.prepare(`
|
|
352
|
+
SELECT * FROM context_items
|
|
353
|
+
WHERE session_id = ?
|
|
354
|
+
AND (key LIKE ? OR value LIKE ?)
|
|
355
|
+
AND created_at > ?
|
|
356
|
+
ORDER BY created_at DESC
|
|
357
|
+
`)
|
|
358
|
+
.all(testSessionId, '%auth%', '%auth%', cutoffTime.toISOString());
|
|
359
|
+
(0, globals_1.expect)(results.some((r) => r.key === 'recent_auth_task')).toBe(true);
|
|
360
|
+
(0, globals_1.expect)(results.some((r) => r.key === 'auth_config')).toBe(true);
|
|
361
|
+
});
|
|
362
|
+
(0, globals_1.it)('should handle complex combined filters', () => {
|
|
363
|
+
const now = new Date();
|
|
364
|
+
const twoDaysAgo = new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000);
|
|
365
|
+
const searchParams = {
|
|
366
|
+
query: 'auth',
|
|
367
|
+
sessionId: testSessionId,
|
|
368
|
+
channels: ['main', 'feature/auth'],
|
|
369
|
+
priorities: ['high', 'normal'],
|
|
370
|
+
createdAfter: twoDaysAgo.toISOString(),
|
|
371
|
+
sort: 'created_at_desc',
|
|
372
|
+
limit: 10,
|
|
373
|
+
includeMetadata: true,
|
|
374
|
+
};
|
|
375
|
+
// Build complex query
|
|
376
|
+
const channelPlaceholders = searchParams.channels.map(() => '?').join(',');
|
|
377
|
+
const priorityPlaceholders = searchParams.priorities.map(() => '?').join(',');
|
|
378
|
+
let sql = `
|
|
379
|
+
SELECT *, size FROM context_items
|
|
380
|
+
WHERE session_id = ?
|
|
381
|
+
AND (key LIKE ? OR value LIKE ?)
|
|
382
|
+
AND channel IN (${channelPlaceholders})
|
|
383
|
+
AND priority IN (${priorityPlaceholders})
|
|
384
|
+
AND created_at > ?
|
|
385
|
+
ORDER BY created_at DESC
|
|
386
|
+
LIMIT ?
|
|
387
|
+
`;
|
|
388
|
+
const results = db
|
|
389
|
+
.prepare(sql)
|
|
390
|
+
.all(searchParams.sessionId, `%${searchParams.query}%`, `%${searchParams.query}%`, ...searchParams.channels, ...searchParams.priorities, searchParams.createdAfter, searchParams.limit);
|
|
391
|
+
// Should get auth_config and db_auth_connection (not user_model which is older)
|
|
392
|
+
(0, globals_1.expect)(results.length).toBe(2);
|
|
393
|
+
(0, globals_1.expect)(results[0].key).toBe('auth_config'); // Most recent
|
|
394
|
+
(0, globals_1.expect)(results[1].key).toBe('db_auth_connection');
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
(0, globals_1.describe)('searchIn Parameter Handling', () => {
|
|
398
|
+
(0, globals_1.beforeEach)(() => {
|
|
399
|
+
// Add specific test data
|
|
400
|
+
db.prepare('INSERT INTO context_items (id, session_id, key, value) VALUES (?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, 'authentication_service', 'Service for user login');
|
|
401
|
+
db.prepare('INSERT INTO context_items (id, session_id, key, value) VALUES (?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, 'user_service', 'Service for authentication');
|
|
402
|
+
});
|
|
403
|
+
(0, globals_1.it)('should search in both key and value when searchIn includes both', () => {
|
|
404
|
+
const searchParams = {
|
|
405
|
+
query: 'authentication',
|
|
406
|
+
sessionId: testSessionId,
|
|
407
|
+
searchIn: ['key', 'value'],
|
|
408
|
+
};
|
|
409
|
+
const results = db
|
|
410
|
+
.prepare(`
|
|
411
|
+
SELECT * FROM context_items
|
|
412
|
+
WHERE session_id = ?
|
|
413
|
+
AND (key LIKE ? OR value LIKE ?)
|
|
414
|
+
ORDER BY created_at DESC
|
|
415
|
+
`)
|
|
416
|
+
.all(searchParams.sessionId, `%${searchParams.query}%`, `%${searchParams.query}%`);
|
|
417
|
+
(0, globals_1.expect)(results.length).toBe(2);
|
|
418
|
+
});
|
|
419
|
+
(0, globals_1.it)('should search only in keys when searchIn is ["key"]', () => {
|
|
420
|
+
const searchParams = {
|
|
421
|
+
query: 'authentication',
|
|
422
|
+
sessionId: testSessionId,
|
|
423
|
+
searchIn: ['key'],
|
|
424
|
+
};
|
|
425
|
+
const results = db
|
|
426
|
+
.prepare(`
|
|
427
|
+
SELECT * FROM context_items
|
|
428
|
+
WHERE session_id = ?
|
|
429
|
+
AND key LIKE ?
|
|
430
|
+
ORDER BY created_at DESC
|
|
431
|
+
`)
|
|
432
|
+
.all(searchParams.sessionId, `%${searchParams.query}%`);
|
|
433
|
+
(0, globals_1.expect)(results.length).toBe(1);
|
|
434
|
+
(0, globals_1.expect)(results[0].key).toBe('authentication_service');
|
|
435
|
+
});
|
|
436
|
+
(0, globals_1.it)('should search only in values when searchIn is ["value"]', () => {
|
|
437
|
+
const searchParams = {
|
|
438
|
+
query: 'authentication',
|
|
439
|
+
sessionId: testSessionId,
|
|
440
|
+
searchIn: ['value'],
|
|
441
|
+
};
|
|
442
|
+
const results = db
|
|
443
|
+
.prepare(`
|
|
444
|
+
SELECT * FROM context_items
|
|
445
|
+
WHERE session_id = ?
|
|
446
|
+
AND value LIKE ?
|
|
447
|
+
ORDER BY created_at DESC
|
|
448
|
+
`)
|
|
449
|
+
.all(searchParams.sessionId, `%${searchParams.query}%`);
|
|
450
|
+
(0, globals_1.expect)(results.length).toBe(1);
|
|
451
|
+
(0, globals_1.expect)(results[0].key).toBe('user_service');
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
(0, globals_1.describe)('Response Format', () => {
|
|
455
|
+
(0, globals_1.beforeEach)(() => {
|
|
456
|
+
// Add test data
|
|
457
|
+
for (let i = 0; i < 5; i++) {
|
|
458
|
+
db.prepare('INSERT INTO context_items (id, session_id, key, value, priority, size) VALUES (?, ?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, `test_item_${i}`, `Test value containing auth keyword ${i}`, i % 2 === 0 ? 'high' : 'normal', 50 + i * 10);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
(0, globals_1.it)('should format response without metadata', () => {
|
|
462
|
+
const results = db
|
|
463
|
+
.prepare(`
|
|
464
|
+
SELECT * FROM context_items
|
|
465
|
+
WHERE session_id = ?
|
|
466
|
+
AND value LIKE ?
|
|
467
|
+
ORDER BY created_at DESC
|
|
468
|
+
`)
|
|
469
|
+
.all(testSessionId, '%auth%');
|
|
470
|
+
// Simulate handler response formatting
|
|
471
|
+
const formatted = results.map((r) => ({
|
|
472
|
+
key: r.key,
|
|
473
|
+
value: r.value,
|
|
474
|
+
category: r.category,
|
|
475
|
+
priority: r.priority,
|
|
476
|
+
}));
|
|
477
|
+
(0, globals_1.expect)(formatted.length).toBe(5);
|
|
478
|
+
formatted.forEach((item) => {
|
|
479
|
+
(0, globals_1.expect)(item).toHaveProperty('key');
|
|
480
|
+
(0, globals_1.expect)(item).toHaveProperty('value');
|
|
481
|
+
(0, globals_1.expect)(item).toHaveProperty('priority');
|
|
482
|
+
(0, globals_1.expect)(item).not.toHaveProperty('size');
|
|
483
|
+
(0, globals_1.expect)(item).not.toHaveProperty('created_at');
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
(0, globals_1.it)('should format response with metadata when requested', () => {
|
|
487
|
+
const results = db
|
|
488
|
+
.prepare(`
|
|
489
|
+
SELECT *, size FROM context_items
|
|
490
|
+
WHERE session_id = ?
|
|
491
|
+
AND value LIKE ?
|
|
492
|
+
ORDER BY created_at DESC
|
|
493
|
+
LIMIT 3
|
|
494
|
+
`)
|
|
495
|
+
.all(testSessionId, '%auth%');
|
|
496
|
+
// Get total count
|
|
497
|
+
const countResult = db
|
|
498
|
+
.prepare(`
|
|
499
|
+
SELECT COUNT(*) as count FROM context_items
|
|
500
|
+
WHERE session_id = ?
|
|
501
|
+
AND value LIKE ?
|
|
502
|
+
`)
|
|
503
|
+
.get(testSessionId, '%auth%');
|
|
504
|
+
// Simulate handler response with metadata
|
|
505
|
+
const formattedWithMetadata = {
|
|
506
|
+
items: results.map((item) => ({
|
|
507
|
+
key: item.key,
|
|
508
|
+
value: item.value,
|
|
509
|
+
category: item.category,
|
|
510
|
+
priority: item.priority,
|
|
511
|
+
channel: item.channel,
|
|
512
|
+
metadata: item.metadata ? JSON.parse(item.metadata) : null,
|
|
513
|
+
size: item.size,
|
|
514
|
+
created_at: item.created_at,
|
|
515
|
+
updated_at: item.updated_at,
|
|
516
|
+
})),
|
|
517
|
+
totalCount: countResult.count,
|
|
518
|
+
page: 1,
|
|
519
|
+
pageSize: 3,
|
|
520
|
+
};
|
|
521
|
+
(0, globals_1.expect)(formattedWithMetadata.items.length).toBe(3);
|
|
522
|
+
(0, globals_1.expect)(formattedWithMetadata.totalCount).toBe(5);
|
|
523
|
+
(0, globals_1.expect)(formattedWithMetadata.page).toBe(1);
|
|
524
|
+
(0, globals_1.expect)(formattedWithMetadata.pageSize).toBe(3);
|
|
525
|
+
formattedWithMetadata.items.forEach((item) => {
|
|
526
|
+
(0, globals_1.expect)(item).toHaveProperty('size');
|
|
527
|
+
(0, globals_1.expect)(item).toHaveProperty('created_at');
|
|
528
|
+
(0, globals_1.expect)(item).toHaveProperty('updated_at');
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
(0, globals_1.describe)('Error Handling', () => {
|
|
533
|
+
(0, globals_1.it)('should handle empty search query gracefully', () => {
|
|
534
|
+
const results = contextRepo.search('', testSessionId, true);
|
|
535
|
+
(0, globals_1.expect)(results.length).toBe(0);
|
|
536
|
+
});
|
|
537
|
+
(0, globals_1.it)('should handle non-existent session gracefully', () => {
|
|
538
|
+
const results = contextRepo.search('test', 'non-existent-session', true);
|
|
539
|
+
(0, globals_1.expect)(results.length).toBe(0);
|
|
540
|
+
});
|
|
541
|
+
(0, globals_1.it)('should handle SQL injection attempts safely', () => {
|
|
542
|
+
const maliciousQuery = "'; DROP TABLE context_items; --";
|
|
543
|
+
// This should not throw and should not damage the database
|
|
544
|
+
const _results = contextRepo.search(maliciousQuery, testSessionId, true);
|
|
545
|
+
// Verify table still exists
|
|
546
|
+
const tableExists = db
|
|
547
|
+
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='context_items'")
|
|
548
|
+
.get();
|
|
549
|
+
(0, globals_1.expect)(tableExists).toBeTruthy();
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
});
|