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.
- package/CHANGELOG.md +433 -0
- package/LICENSE +21 -0
- package/README.md +1051 -0
- package/bin/mcp-memory-keeper +52 -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 +938 -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/cross-session-sharing.test.js +302 -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 +407 -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/git-integration.test.js +237 -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/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 +283 -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 +307 -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/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 +188 -0
- package/dist/__tests__/utils/timezone-safe-dates.js +119 -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 +4310 -0
- package/dist/index.phase1.backup.js +410 -0
- package/dist/index.phase2.backup.js +704 -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 +1873 -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/server.js +384 -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 +731 -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/validation.js +296 -0
- package/dist/utils/vector-store.js +247 -0
- package/package.json +84 -0
|
@@ -0,0 +1,307 @@
|
|
|
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 child_process_1 = require("child_process");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const os = __importStar(require("os"));
|
|
41
|
+
(0, globals_1.describe)('Server Initialization Tests', () => {
|
|
42
|
+
let tempDir;
|
|
43
|
+
let serverProcess = null;
|
|
44
|
+
(0, globals_1.beforeEach)(() => {
|
|
45
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mcp-test-'));
|
|
46
|
+
});
|
|
47
|
+
(0, globals_1.afterEach)(async () => {
|
|
48
|
+
if (serverProcess && !serverProcess.killed) {
|
|
49
|
+
// First try graceful shutdown
|
|
50
|
+
serverProcess.kill('SIGTERM');
|
|
51
|
+
// Wait for process to actually exit
|
|
52
|
+
await new Promise(resolve => {
|
|
53
|
+
const timeout = setTimeout(() => {
|
|
54
|
+
// Force kill if still running
|
|
55
|
+
if (serverProcess && !serverProcess.killed) {
|
|
56
|
+
serverProcess.kill('SIGKILL');
|
|
57
|
+
}
|
|
58
|
+
resolve();
|
|
59
|
+
}, 5000);
|
|
60
|
+
serverProcess?.on('exit', () => {
|
|
61
|
+
clearTimeout(timeout);
|
|
62
|
+
resolve();
|
|
63
|
+
});
|
|
64
|
+
serverProcess?.on('error', () => {
|
|
65
|
+
clearTimeout(timeout);
|
|
66
|
+
resolve();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
// Remove event listeners to prevent leaks
|
|
70
|
+
serverProcess?.removeAllListeners();
|
|
71
|
+
// Track for global cleanup
|
|
72
|
+
if (global.testProcesses) {
|
|
73
|
+
const index = global.testProcesses.indexOf(serverProcess);
|
|
74
|
+
if (index > -1) {
|
|
75
|
+
global.testProcesses.splice(index, 1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
serverProcess = null;
|
|
80
|
+
// Clean up temp directory
|
|
81
|
+
try {
|
|
82
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
console.warn('Error cleaning up temp directory:', error);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
(0, globals_1.it)('should start server and respond to initialize request', done => {
|
|
89
|
+
const dbPath = path.join(tempDir, 'test.db');
|
|
90
|
+
// Start the server
|
|
91
|
+
serverProcess = (0, child_process_1.spawn)('node', [path.join(__dirname, '../../../dist/index.js')], {
|
|
92
|
+
env: {
|
|
93
|
+
...process.env,
|
|
94
|
+
MCP_DB_PATH: dbPath,
|
|
95
|
+
},
|
|
96
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
97
|
+
});
|
|
98
|
+
// Track for global cleanup
|
|
99
|
+
if (!global.testProcesses) {
|
|
100
|
+
global.testProcesses = [];
|
|
101
|
+
}
|
|
102
|
+
global.testProcesses.push(serverProcess);
|
|
103
|
+
// Track for global cleanup
|
|
104
|
+
if (!global.testProcesses) {
|
|
105
|
+
global.testProcesses = [];
|
|
106
|
+
}
|
|
107
|
+
global.testProcesses.push(serverProcess);
|
|
108
|
+
let output = '';
|
|
109
|
+
let initialized = false;
|
|
110
|
+
serverProcess.stdout?.on('data', _data => {
|
|
111
|
+
output += _data.toString();
|
|
112
|
+
// Look for initialization response
|
|
113
|
+
const lines = output.split('\n').filter(line => line.trim());
|
|
114
|
+
for (const line of lines) {
|
|
115
|
+
try {
|
|
116
|
+
const msg = JSON.parse(line);
|
|
117
|
+
if (msg.id === 1 && msg.result) {
|
|
118
|
+
initialized = true;
|
|
119
|
+
(0, globals_1.expect)(msg.result).toHaveProperty('protocolVersion');
|
|
120
|
+
(0, globals_1.expect)(msg.result).toHaveProperty('capabilities');
|
|
121
|
+
done();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (_e) {
|
|
125
|
+
// Not JSON, continue
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
serverProcess.stderr?.on('data', _data => {
|
|
130
|
+
console.error('Server error:', _data.toString());
|
|
131
|
+
});
|
|
132
|
+
// Send initialization request
|
|
133
|
+
setTimeout(() => {
|
|
134
|
+
serverProcess?.stdin?.write(JSON.stringify({
|
|
135
|
+
jsonrpc: '2.0',
|
|
136
|
+
method: 'initialize',
|
|
137
|
+
params: {
|
|
138
|
+
protocolVersion: '2024-11-05',
|
|
139
|
+
capabilities: {},
|
|
140
|
+
clientInfo: {
|
|
141
|
+
name: 'test-client',
|
|
142
|
+
version: '1.0.0',
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
id: 1,
|
|
146
|
+
}) + '\n');
|
|
147
|
+
}, 500);
|
|
148
|
+
// Timeout after 5 seconds
|
|
149
|
+
setTimeout(() => {
|
|
150
|
+
if (!initialized) {
|
|
151
|
+
done(new Error('Server did not initialize within timeout'));
|
|
152
|
+
}
|
|
153
|
+
}, 5000);
|
|
154
|
+
});
|
|
155
|
+
(0, globals_1.it)('should create database file on startup', done => {
|
|
156
|
+
const dbPath = path.join(tempDir, 'context.db');
|
|
157
|
+
// Change to temp directory
|
|
158
|
+
const originalCwd = process.cwd();
|
|
159
|
+
try {
|
|
160
|
+
process.chdir(tempDir);
|
|
161
|
+
// Start the server
|
|
162
|
+
serverProcess = (0, child_process_1.spawn)('node', [path.join(__dirname, '../../../dist/index.js')], {
|
|
163
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
164
|
+
});
|
|
165
|
+
// Track for global cleanup
|
|
166
|
+
if (!global.testProcesses) {
|
|
167
|
+
global.testProcesses = [];
|
|
168
|
+
}
|
|
169
|
+
global.testProcesses.push(serverProcess);
|
|
170
|
+
// Wait for server to start
|
|
171
|
+
setTimeout(() => {
|
|
172
|
+
try {
|
|
173
|
+
// Check if database file was created
|
|
174
|
+
(0, globals_1.expect)(fs.existsSync(dbPath)).toBe(true);
|
|
175
|
+
// Check if it's a valid SQLite database
|
|
176
|
+
const header = fs.readFileSync(dbPath).slice(0, 16).toString();
|
|
177
|
+
(0, globals_1.expect)(header).toBe('SQLite format 3\x00');
|
|
178
|
+
process.chdir(originalCwd);
|
|
179
|
+
done();
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
process.chdir(originalCwd);
|
|
183
|
+
done(error);
|
|
184
|
+
}
|
|
185
|
+
}, 1000);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
process.chdir(originalCwd);
|
|
189
|
+
done(error);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
(0, globals_1.it)('should handle invalid requests gracefully', done => {
|
|
193
|
+
const dbPath = path.join(tempDir, 'test.db');
|
|
194
|
+
serverProcess = (0, child_process_1.spawn)('node', [path.join(__dirname, '../../../dist/index.js')], {
|
|
195
|
+
env: {
|
|
196
|
+
...process.env,
|
|
197
|
+
MCP_DB_PATH: dbPath,
|
|
198
|
+
},
|
|
199
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
200
|
+
});
|
|
201
|
+
// Track for global cleanup
|
|
202
|
+
if (!global.testProcesses) {
|
|
203
|
+
global.testProcesses = [];
|
|
204
|
+
}
|
|
205
|
+
global.testProcesses.push(serverProcess);
|
|
206
|
+
let output = '';
|
|
207
|
+
let errorReceived = false;
|
|
208
|
+
serverProcess.stdout?.on('data', _data => {
|
|
209
|
+
output += _data.toString();
|
|
210
|
+
const lines = output.split('\n').filter(line => line.trim());
|
|
211
|
+
for (const line of lines) {
|
|
212
|
+
try {
|
|
213
|
+
const msg = JSON.parse(line);
|
|
214
|
+
if (msg.id === 2 && msg.error) {
|
|
215
|
+
errorReceived = true;
|
|
216
|
+
(0, globals_1.expect)(msg.error).toHaveProperty('code');
|
|
217
|
+
(0, globals_1.expect)(msg.error).toHaveProperty('message');
|
|
218
|
+
done();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch (_e) {
|
|
222
|
+
// Not JSON, continue
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
// Send invalid request after initialization
|
|
227
|
+
setTimeout(() => {
|
|
228
|
+
// Initialize first
|
|
229
|
+
serverProcess?.stdin?.write(JSON.stringify({
|
|
230
|
+
jsonrpc: '2.0',
|
|
231
|
+
method: 'initialize',
|
|
232
|
+
params: {
|
|
233
|
+
protocolVersion: '2024-11-05',
|
|
234
|
+
capabilities: {},
|
|
235
|
+
},
|
|
236
|
+
id: 1,
|
|
237
|
+
}) + '\n');
|
|
238
|
+
// Then send invalid request
|
|
239
|
+
setTimeout(() => {
|
|
240
|
+
serverProcess?.stdin?.write(JSON.stringify({
|
|
241
|
+
jsonrpc: '2.0',
|
|
242
|
+
method: 'tools/call',
|
|
243
|
+
params: {
|
|
244
|
+
name: 'non_existent_tool',
|
|
245
|
+
arguments: {},
|
|
246
|
+
},
|
|
247
|
+
id: 2,
|
|
248
|
+
}) + '\n');
|
|
249
|
+
}, 500);
|
|
250
|
+
}, 500);
|
|
251
|
+
// Timeout
|
|
252
|
+
setTimeout(() => {
|
|
253
|
+
if (!errorReceived) {
|
|
254
|
+
done(new Error('Server did not return error for invalid request'));
|
|
255
|
+
}
|
|
256
|
+
}, 5000);
|
|
257
|
+
});
|
|
258
|
+
(0, globals_1.it)('should handle server shutdown gracefully', done => {
|
|
259
|
+
const dbPath = path.join(tempDir, 'test.db');
|
|
260
|
+
serverProcess = (0, child_process_1.spawn)('node', [path.join(__dirname, '../../../dist/index.js')], {
|
|
261
|
+
env: {
|
|
262
|
+
...process.env,
|
|
263
|
+
MCP_DB_PATH: dbPath,
|
|
264
|
+
},
|
|
265
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
266
|
+
});
|
|
267
|
+
// Track for global cleanup
|
|
268
|
+
if (!global.testProcesses) {
|
|
269
|
+
global.testProcesses = [];
|
|
270
|
+
}
|
|
271
|
+
global.testProcesses.push(serverProcess);
|
|
272
|
+
let serverStarted = false;
|
|
273
|
+
let timeout;
|
|
274
|
+
const cleanup = () => {
|
|
275
|
+
if (timeout)
|
|
276
|
+
clearTimeout(timeout);
|
|
277
|
+
};
|
|
278
|
+
serverProcess.stdout?.on('data', _data => {
|
|
279
|
+
serverStarted = true;
|
|
280
|
+
// Server is ready, wait a bit then terminate
|
|
281
|
+
setTimeout(() => {
|
|
282
|
+
serverProcess?.kill('SIGTERM');
|
|
283
|
+
}, 100);
|
|
284
|
+
});
|
|
285
|
+
serverProcess.stderr?.on('data', _data => {
|
|
286
|
+
// Any stderr output means server is working
|
|
287
|
+
serverStarted = true;
|
|
288
|
+
});
|
|
289
|
+
serverProcess.on('exit', code => {
|
|
290
|
+
cleanup();
|
|
291
|
+
(0, globals_1.expect)(serverStarted).toBe(true);
|
|
292
|
+
(0, globals_1.expect)(code).toBe(null); // Killed by signal, not error
|
|
293
|
+
done();
|
|
294
|
+
});
|
|
295
|
+
serverProcess.on('error', err => {
|
|
296
|
+
cleanup();
|
|
297
|
+
done(err);
|
|
298
|
+
});
|
|
299
|
+
// Fallback: if server doesn't start in 3 seconds, kill it anyway
|
|
300
|
+
timeout = setTimeout(() => {
|
|
301
|
+
if (!serverStarted) {
|
|
302
|
+
serverStarted = true; // Assume it started but didn't output yet
|
|
303
|
+
}
|
|
304
|
+
serverProcess?.kill('SIGTERM');
|
|
305
|
+
}, 3000);
|
|
306
|
+
}, 15000); // Increase test timeout to 15 seconds
|
|
307
|
+
});
|
|
@@ -0,0 +1,219 @@
|
|
|
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 database_1 = require("../../utils/database");
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const uuid_1 = require("uuid");
|
|
41
|
+
describe('Session Management Integration Tests', () => {
|
|
42
|
+
let dbManager;
|
|
43
|
+
let tempDbPath;
|
|
44
|
+
let db;
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
tempDbPath = path.join(os.tmpdir(), `test-session-${Date.now()}.db`);
|
|
47
|
+
dbManager = new database_1.DatabaseManager({
|
|
48
|
+
filename: tempDbPath,
|
|
49
|
+
maxSize: 10 * 1024 * 1024,
|
|
50
|
+
walMode: true,
|
|
51
|
+
});
|
|
52
|
+
db = dbManager.getDatabase();
|
|
53
|
+
});
|
|
54
|
+
afterEach(() => {
|
|
55
|
+
dbManager.close();
|
|
56
|
+
try {
|
|
57
|
+
fs.unlinkSync(tempDbPath);
|
|
58
|
+
fs.unlinkSync(`${tempDbPath}-wal`);
|
|
59
|
+
fs.unlinkSync(`${tempDbPath}-shm`);
|
|
60
|
+
}
|
|
61
|
+
catch (_e) {
|
|
62
|
+
// Ignore
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
describe('Session lifecycle', () => {
|
|
66
|
+
it('should create a new session', () => {
|
|
67
|
+
const sessionId = (0, uuid_1.v4)();
|
|
68
|
+
const result = db
|
|
69
|
+
.prepare('INSERT INTO sessions (id, name, description, branch) VALUES (?, ?, ?, ?)')
|
|
70
|
+
.run(sessionId, 'Test Session', 'Test Description', 'main');
|
|
71
|
+
expect(result.changes).toBe(1);
|
|
72
|
+
const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(sessionId);
|
|
73
|
+
expect(session).toBeDefined();
|
|
74
|
+
expect(session.name).toBe('Test Session');
|
|
75
|
+
expect(session.description).toBe('Test Description');
|
|
76
|
+
expect(session.branch).toBe('main');
|
|
77
|
+
});
|
|
78
|
+
it('should list sessions in order', () => {
|
|
79
|
+
// Create multiple sessions
|
|
80
|
+
const sessions = [
|
|
81
|
+
{ id: (0, uuid_1.v4)(), name: 'Session 1' },
|
|
82
|
+
{ id: (0, uuid_1.v4)(), name: 'Session 2' },
|
|
83
|
+
{ id: (0, uuid_1.v4)(), name: 'Session 3' },
|
|
84
|
+
];
|
|
85
|
+
sessions.forEach((s, index) => {
|
|
86
|
+
// Add delay to ensure different timestamps
|
|
87
|
+
const date = new Date();
|
|
88
|
+
date.setSeconds(date.getSeconds() - (sessions.length - index));
|
|
89
|
+
db.prepare('INSERT INTO sessions (id, name, created_at) VALUES (?, ?, ?)').run(s.id, s.name, date.toISOString());
|
|
90
|
+
});
|
|
91
|
+
const results = db
|
|
92
|
+
.prepare('SELECT * FROM sessions ORDER BY created_at DESC LIMIT 10')
|
|
93
|
+
.all();
|
|
94
|
+
expect(results).toHaveLength(3);
|
|
95
|
+
expect(results[0].name).toBe('Session 3');
|
|
96
|
+
expect(results[2].name).toBe('Session 1');
|
|
97
|
+
});
|
|
98
|
+
it('should copy context when continuing from previous session', () => {
|
|
99
|
+
// Create source session
|
|
100
|
+
const sourceSessionId = (0, uuid_1.v4)();
|
|
101
|
+
db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(sourceSessionId, 'Source Session');
|
|
102
|
+
// Add context items
|
|
103
|
+
const items = [
|
|
104
|
+
{ key: 'key1', value: 'value1', category: 'task', priority: 'high' },
|
|
105
|
+
{ key: 'key2', value: 'value2', category: 'decision', priority: 'normal' },
|
|
106
|
+
];
|
|
107
|
+
items.forEach(item => {
|
|
108
|
+
db.prepare('INSERT INTO context_items (id, session_id, key, value, category, priority) VALUES (?, ?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), sourceSessionId, item.key, item.value, item.category, item.priority);
|
|
109
|
+
});
|
|
110
|
+
// Create new session and copy items
|
|
111
|
+
const newSessionId = (0, uuid_1.v4)();
|
|
112
|
+
db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(newSessionId, 'New Session');
|
|
113
|
+
dbManager.transaction(() => {
|
|
114
|
+
const sourceItems = db
|
|
115
|
+
.prepare('SELECT * FROM context_items WHERE session_id = ?')
|
|
116
|
+
.all(sourceSessionId);
|
|
117
|
+
sourceItems.forEach((item) => {
|
|
118
|
+
db.prepare('INSERT INTO context_items (id, session_id, key, value, category, priority) VALUES (?, ?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), newSessionId, item.key, item.value, item.category, item.priority);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
// Verify items were copied
|
|
122
|
+
const copiedItems = db
|
|
123
|
+
.prepare('SELECT * FROM context_items WHERE session_id = ? ORDER BY key')
|
|
124
|
+
.all(newSessionId);
|
|
125
|
+
expect(copiedItems).toHaveLength(2);
|
|
126
|
+
expect(copiedItems[0].key).toBe('key1');
|
|
127
|
+
expect(copiedItems[0].value).toBe('value1');
|
|
128
|
+
expect(copiedItems[1].key).toBe('key2');
|
|
129
|
+
expect(copiedItems[1].value).toBe('value2');
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
describe('Context storage', () => {
|
|
133
|
+
it('should save and retrieve context items', () => {
|
|
134
|
+
const sessionId = (0, uuid_1.v4)();
|
|
135
|
+
db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(sessionId, 'Test Session');
|
|
136
|
+
// Save context item
|
|
137
|
+
const itemId = (0, uuid_1.v4)();
|
|
138
|
+
db.prepare('INSERT INTO context_items (id, session_id, key, value, category, priority) VALUES (?, ?, ?, ?, ?, ?)').run(itemId, sessionId, 'test_key', 'test_value', 'task', 'high');
|
|
139
|
+
// Retrieve item
|
|
140
|
+
const item = db
|
|
141
|
+
.prepare('SELECT * FROM context_items WHERE session_id = ? AND key = ?')
|
|
142
|
+
.get(sessionId, 'test_key');
|
|
143
|
+
expect(item).toBeDefined();
|
|
144
|
+
expect(item.value).toBe('test_value');
|
|
145
|
+
expect(item.category).toBe('task');
|
|
146
|
+
expect(item.priority).toBe('high');
|
|
147
|
+
});
|
|
148
|
+
it('should handle unique key constraint per session', () => {
|
|
149
|
+
const sessionId = (0, uuid_1.v4)();
|
|
150
|
+
db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(sessionId, 'Test Session');
|
|
151
|
+
// Insert first item
|
|
152
|
+
db.prepare('INSERT INTO context_items (id, session_id, key, value) VALUES (?, ?, ?, ?)').run((0, uuid_1.v4)(), sessionId, 'unique_key', 'value1');
|
|
153
|
+
// Try to insert duplicate key - should replace
|
|
154
|
+
db.prepare('INSERT OR REPLACE INTO context_items (id, session_id, key, value) VALUES (?, ?, ?, ?)').run((0, uuid_1.v4)(), sessionId, 'unique_key', 'value2');
|
|
155
|
+
const items = db
|
|
156
|
+
.prepare('SELECT * FROM context_items WHERE session_id = ? AND key = ?')
|
|
157
|
+
.all(sessionId, 'unique_key');
|
|
158
|
+
expect(items).toHaveLength(1);
|
|
159
|
+
expect(items[0].value).toBe('value2');
|
|
160
|
+
});
|
|
161
|
+
it('should filter by category and priority', () => {
|
|
162
|
+
const sessionId = (0, uuid_1.v4)();
|
|
163
|
+
db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(sessionId, 'Test Session');
|
|
164
|
+
// Insert items with different categories and priorities
|
|
165
|
+
const items = [
|
|
166
|
+
{ key: 'task1', category: 'task', priority: 'high' },
|
|
167
|
+
{ key: 'task2', category: 'task', priority: 'low' },
|
|
168
|
+
{ key: 'decision1', category: 'decision', priority: 'high' },
|
|
169
|
+
{ key: 'note1', category: 'note', priority: 'normal' },
|
|
170
|
+
];
|
|
171
|
+
items.forEach(item => {
|
|
172
|
+
db.prepare('INSERT INTO context_items (id, session_id, key, value, category, priority) VALUES (?, ?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), sessionId, item.key, 'value', item.category, item.priority);
|
|
173
|
+
});
|
|
174
|
+
// Filter by category
|
|
175
|
+
const tasks = db
|
|
176
|
+
.prepare('SELECT * FROM context_items WHERE session_id = ? AND category = ?')
|
|
177
|
+
.all(sessionId, 'task');
|
|
178
|
+
expect(tasks).toHaveLength(2);
|
|
179
|
+
// Filter by priority
|
|
180
|
+
const highPriority = db
|
|
181
|
+
.prepare('SELECT * FROM context_items WHERE session_id = ? AND priority = ?')
|
|
182
|
+
.all(sessionId, 'high');
|
|
183
|
+
expect(highPriority).toHaveLength(2);
|
|
184
|
+
expect(highPriority.map((i) => i.key).sort()).toEqual(['decision1', 'task1']);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
describe('File caching', () => {
|
|
188
|
+
it('should cache file content with hash', () => {
|
|
189
|
+
const sessionId = (0, uuid_1.v4)();
|
|
190
|
+
db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(sessionId, 'Test Session');
|
|
191
|
+
const content = 'This is file content';
|
|
192
|
+
const hash = require('crypto').createHash('sha256').update(content).digest('hex');
|
|
193
|
+
db.prepare('INSERT INTO file_cache (id, session_id, file_path, content, hash) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), sessionId, '/test/file.txt', content, hash);
|
|
194
|
+
const cached = db
|
|
195
|
+
.prepare('SELECT * FROM file_cache WHERE session_id = ? AND file_path = ?')
|
|
196
|
+
.get(sessionId, '/test/file.txt');
|
|
197
|
+
expect(cached).toBeDefined();
|
|
198
|
+
expect(cached.content).toBe(content);
|
|
199
|
+
expect(cached.hash).toBe(hash);
|
|
200
|
+
});
|
|
201
|
+
it('should detect file changes', () => {
|
|
202
|
+
const sessionId = (0, uuid_1.v4)();
|
|
203
|
+
db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(sessionId, 'Test Session');
|
|
204
|
+
const originalContent = 'Original content';
|
|
205
|
+
const originalHash = require('crypto')
|
|
206
|
+
.createHash('sha256')
|
|
207
|
+
.update(originalContent)
|
|
208
|
+
.digest('hex');
|
|
209
|
+
db.prepare('INSERT INTO file_cache (id, session_id, file_path, content, hash) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), sessionId, '/test/file.txt', originalContent, originalHash);
|
|
210
|
+
const newContent = 'Modified content';
|
|
211
|
+
const newHash = require('crypto').createHash('sha256').update(newContent).digest('hex');
|
|
212
|
+
const cached = db
|
|
213
|
+
.prepare('SELECT * FROM file_cache WHERE session_id = ? AND file_path = ?')
|
|
214
|
+
.get(sessionId, '/test/file.txt');
|
|
215
|
+
expect(cached.hash).not.toBe(newHash);
|
|
216
|
+
expect(originalHash).not.toBe(newHash);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
});
|