@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,291 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const simple_git_1 = require("simple-git");
|
|
43
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
44
|
+
const uuid_1 = require("uuid");
|
|
45
|
+
describe('Project Directory Feature Tests', () => {
|
|
46
|
+
let tempProjectPath;
|
|
47
|
+
let tempDbPath;
|
|
48
|
+
let db;
|
|
49
|
+
beforeEach(async () => {
|
|
50
|
+
// Create a temporary project directory with git repo
|
|
51
|
+
tempProjectPath = path.join(os.tmpdir(), `test-project-${Date.now()}`);
|
|
52
|
+
fs.mkdirSync(tempProjectPath, { recursive: true });
|
|
53
|
+
// Initialize git repo in the temp project
|
|
54
|
+
const git = (0, simple_git_1.simpleGit)(tempProjectPath);
|
|
55
|
+
await git.init();
|
|
56
|
+
await git.addConfig('user.name', 'Test User');
|
|
57
|
+
await git.addConfig('user.email', 'test@example.com');
|
|
58
|
+
// Use repo-local hooks directory to prevent global hooks from interfering
|
|
59
|
+
const localHooksDir = path.join(tempProjectPath, '.git', 'hooks');
|
|
60
|
+
await git.addConfig('core.hooksPath', localHooksDir);
|
|
61
|
+
await git.addConfig('commit.gpgsign', 'false');
|
|
62
|
+
// Create initial commit
|
|
63
|
+
fs.writeFileSync(path.join(tempProjectPath, 'README.md'), '# Test Project');
|
|
64
|
+
await git.add('.');
|
|
65
|
+
await git.commit('Initial commit');
|
|
66
|
+
// Create test database
|
|
67
|
+
tempDbPath = path.join(os.tmpdir(), `test-db-${Date.now()}.db`);
|
|
68
|
+
db = new better_sqlite3_1.default(tempDbPath);
|
|
69
|
+
// Initialize database schema
|
|
70
|
+
db.exec(`
|
|
71
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
72
|
+
id TEXT PRIMARY KEY,
|
|
73
|
+
name TEXT,
|
|
74
|
+
description TEXT,
|
|
75
|
+
branch TEXT,
|
|
76
|
+
parent_id TEXT,
|
|
77
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
78
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
79
|
+
FOREIGN KEY (parent_id) REFERENCES sessions(id)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
CREATE TABLE IF NOT EXISTS context_items (
|
|
83
|
+
id TEXT PRIMARY KEY,
|
|
84
|
+
session_id TEXT NOT NULL,
|
|
85
|
+
key TEXT NOT NULL,
|
|
86
|
+
value TEXT NOT NULL,
|
|
87
|
+
category TEXT,
|
|
88
|
+
priority TEXT DEFAULT 'normal',
|
|
89
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
90
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id),
|
|
91
|
+
UNIQUE(session_id, key)
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
CREATE TABLE IF NOT EXISTS checkpoints (
|
|
95
|
+
id TEXT PRIMARY KEY,
|
|
96
|
+
session_id TEXT NOT NULL,
|
|
97
|
+
name TEXT NOT NULL,
|
|
98
|
+
description TEXT,
|
|
99
|
+
git_status TEXT,
|
|
100
|
+
git_branch TEXT,
|
|
101
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
102
|
+
FOREIGN KEY (session_id) REFERENCES sessions(id)
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
CREATE TABLE IF NOT EXISTS checkpoint_items (
|
|
106
|
+
id TEXT PRIMARY KEY,
|
|
107
|
+
checkpoint_id TEXT NOT NULL,
|
|
108
|
+
context_item_id TEXT NOT NULL,
|
|
109
|
+
FOREIGN KEY (checkpoint_id) REFERENCES checkpoints(id),
|
|
110
|
+
FOREIGN KEY (context_item_id) REFERENCES context_items(id)
|
|
111
|
+
);
|
|
112
|
+
`);
|
|
113
|
+
});
|
|
114
|
+
afterEach(() => {
|
|
115
|
+
// Clean up
|
|
116
|
+
db.close();
|
|
117
|
+
try {
|
|
118
|
+
fs.unlinkSync(tempDbPath);
|
|
119
|
+
fs.rmSync(tempProjectPath, { recursive: true, force: true });
|
|
120
|
+
}
|
|
121
|
+
catch (_e) {
|
|
122
|
+
// Ignore
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
describe('Git operations with project directory', () => {
|
|
126
|
+
it('should detect git repository in project directory', async () => {
|
|
127
|
+
const git = (0, simple_git_1.simpleGit)(tempProjectPath);
|
|
128
|
+
const status = await git.status();
|
|
129
|
+
const branch = await git.branch();
|
|
130
|
+
expect(branch.current).toBeTruthy();
|
|
131
|
+
expect(status.isClean()).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
it('should capture git status for checkpoint', async () => {
|
|
134
|
+
// Create session
|
|
135
|
+
const sessionId = (0, uuid_1.v4)();
|
|
136
|
+
db.prepare('INSERT INTO sessions (id, name, branch) VALUES (?, ?, ?)').run(sessionId, 'Test Session', 'master');
|
|
137
|
+
// Make changes in repo
|
|
138
|
+
fs.writeFileSync(path.join(tempProjectPath, 'new-file.txt'), 'new content');
|
|
139
|
+
// Get git status
|
|
140
|
+
const git = (0, simple_git_1.simpleGit)(tempProjectPath);
|
|
141
|
+
const status = await git.status();
|
|
142
|
+
const branch = await git.branch();
|
|
143
|
+
const gitStatus = JSON.stringify({
|
|
144
|
+
modified: status.modified,
|
|
145
|
+
created: status.created,
|
|
146
|
+
deleted: status.deleted,
|
|
147
|
+
staged: status.staged,
|
|
148
|
+
not_added: status.not_added,
|
|
149
|
+
ahead: status.ahead,
|
|
150
|
+
behind: status.behind,
|
|
151
|
+
});
|
|
152
|
+
// Create checkpoint with git status
|
|
153
|
+
const checkpointId = (0, uuid_1.v4)();
|
|
154
|
+
db.prepare(`
|
|
155
|
+
INSERT INTO checkpoints (id, session_id, name, git_status, git_branch)
|
|
156
|
+
VALUES (?, ?, ?, ?, ?)
|
|
157
|
+
`).run(checkpointId, sessionId, 'Test Checkpoint', gitStatus, branch.current);
|
|
158
|
+
// Verify checkpoint
|
|
159
|
+
const checkpoint = db
|
|
160
|
+
.prepare('SELECT * FROM checkpoints WHERE id = ?')
|
|
161
|
+
.get(checkpointId);
|
|
162
|
+
expect(checkpoint.git_branch).toBe(branch.current);
|
|
163
|
+
const savedStatus = JSON.parse(checkpoint.git_status);
|
|
164
|
+
expect(savedStatus.not_added).toContain('new-file.txt');
|
|
165
|
+
});
|
|
166
|
+
it('should handle git commit with context save', async () => {
|
|
167
|
+
const sessionId = (0, uuid_1.v4)();
|
|
168
|
+
db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run(sessionId, 'Test Session');
|
|
169
|
+
// Add context items
|
|
170
|
+
db.prepare(`
|
|
171
|
+
INSERT INTO context_items (id, session_id, key, value, category, priority)
|
|
172
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
173
|
+
`).run((0, uuid_1.v4)(), sessionId, 'current_task', 'Test git integration', 'task', 'high');
|
|
174
|
+
// Make changes and commit
|
|
175
|
+
fs.writeFileSync(path.join(tempProjectPath, 'test.txt'), 'test content');
|
|
176
|
+
const git = (0, simple_git_1.simpleGit)(tempProjectPath);
|
|
177
|
+
await git.add('.');
|
|
178
|
+
const _commitResult = await git.commit('Test commit');
|
|
179
|
+
// Save commit info as context
|
|
180
|
+
const timestamp = new Date().toISOString();
|
|
181
|
+
db.prepare(`
|
|
182
|
+
INSERT INTO context_items (id, session_id, key, value, category)
|
|
183
|
+
VALUES (?, ?, ?, ?, ?)
|
|
184
|
+
`).run((0, uuid_1.v4)(), sessionId, `commit_${timestamp}`, 'Test commit', 'git');
|
|
185
|
+
// Create checkpoint for the commit
|
|
186
|
+
const gitStatus = await git.status();
|
|
187
|
+
const gitBranch = await git.branch();
|
|
188
|
+
const checkpointId = (0, uuid_1.v4)();
|
|
189
|
+
db.prepare(`
|
|
190
|
+
INSERT INTO checkpoints (id, session_id, name, description, git_status, git_branch)
|
|
191
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
192
|
+
`).run(checkpointId, sessionId, `git-commit-${timestamp}`, `Git commit: Test commit`, JSON.stringify(gitStatus), gitBranch.current);
|
|
193
|
+
// Link context items to checkpoint
|
|
194
|
+
const contextItems = db
|
|
195
|
+
.prepare('SELECT id FROM context_items WHERE session_id = ?')
|
|
196
|
+
.all(sessionId);
|
|
197
|
+
contextItems.forEach((item) => {
|
|
198
|
+
db.prepare(`
|
|
199
|
+
INSERT INTO checkpoint_items (id, checkpoint_id, context_item_id)
|
|
200
|
+
VALUES (?, ?, ?)
|
|
201
|
+
`).run((0, uuid_1.v4)(), checkpointId, item.id);
|
|
202
|
+
});
|
|
203
|
+
// Verify
|
|
204
|
+
const checkpoint = db
|
|
205
|
+
.prepare('SELECT * FROM checkpoints WHERE id = ?')
|
|
206
|
+
.get(checkpointId);
|
|
207
|
+
expect(checkpoint.name).toContain('git-commit-');
|
|
208
|
+
expect(checkpoint.git_branch).toBeTruthy();
|
|
209
|
+
const linkedItems = db
|
|
210
|
+
.prepare(`
|
|
211
|
+
SELECT COUNT(*) as count FROM checkpoint_items WHERE checkpoint_id = ?
|
|
212
|
+
`)
|
|
213
|
+
.get(checkpointId);
|
|
214
|
+
expect(linkedItems.count).toBeGreaterThan(0);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
describe('Non-git directory handling', () => {
|
|
218
|
+
it('should handle directory without git gracefully', async () => {
|
|
219
|
+
const nonGitPath = path.join(os.tmpdir(), `non-git-${Date.now()}`);
|
|
220
|
+
fs.mkdirSync(nonGitPath, { recursive: true });
|
|
221
|
+
try {
|
|
222
|
+
const git = (0, simple_git_1.simpleGit)(nonGitPath);
|
|
223
|
+
let isGitRepo = true;
|
|
224
|
+
let gitError = '';
|
|
225
|
+
try {
|
|
226
|
+
await git.status();
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
isGitRepo = false;
|
|
230
|
+
gitError = error.message;
|
|
231
|
+
}
|
|
232
|
+
expect(isGitRepo).toBe(false);
|
|
233
|
+
expect(gitError).toContain('not a git repository');
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
fs.rmSync(nonGitPath, { recursive: true, force: true });
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
describe('Project directory with special characters', () => {
|
|
241
|
+
it('should handle paths with spaces', async () => {
|
|
242
|
+
const pathWithSpaces = path.join(os.tmpdir(), `test project ${Date.now()}`);
|
|
243
|
+
fs.mkdirSync(pathWithSpaces, { recursive: true });
|
|
244
|
+
const git = (0, simple_git_1.simpleGit)(pathWithSpaces);
|
|
245
|
+
await git.init();
|
|
246
|
+
// Configure git for this test to avoid CI failures
|
|
247
|
+
await git.addConfig('user.name', 'Test User');
|
|
248
|
+
await git.addConfig('user.email', 'test@example.com');
|
|
249
|
+
// Use repo-local hooks directory to prevent global hooks from interfering
|
|
250
|
+
const localHooksDir = path.join(pathWithSpaces, '.git', 'hooks');
|
|
251
|
+
await git.addConfig('core.hooksPath', localHooksDir);
|
|
252
|
+
await git.addConfig('commit.gpgsign', 'false');
|
|
253
|
+
try {
|
|
254
|
+
fs.writeFileSync(path.join(pathWithSpaces, 'test.txt'), 'content');
|
|
255
|
+
await git.add('.');
|
|
256
|
+
await git.commit('Initial commit');
|
|
257
|
+
const status = await git.status();
|
|
258
|
+
expect(status.isClean()).toBe(true);
|
|
259
|
+
}
|
|
260
|
+
finally {
|
|
261
|
+
fs.rmSync(pathWithSpaces, { recursive: true, force: true });
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
describe('Session and project directory integration', () => {
|
|
266
|
+
it('should store git branch with session', async () => {
|
|
267
|
+
const git = (0, simple_git_1.simpleGit)(tempProjectPath);
|
|
268
|
+
const branch = await git.branch();
|
|
269
|
+
const sessionId = (0, uuid_1.v4)();
|
|
270
|
+
db.prepare(`
|
|
271
|
+
INSERT INTO sessions (id, name, description, branch)
|
|
272
|
+
VALUES (?, ?, ?, ?)
|
|
273
|
+
`).run(sessionId, 'Feature Development', 'Working on new feature', branch.current);
|
|
274
|
+
const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(sessionId);
|
|
275
|
+
expect(session.branch).toBe(branch.current);
|
|
276
|
+
});
|
|
277
|
+
it('should update session when switching branches', async () => {
|
|
278
|
+
const git = (0, simple_git_1.simpleGit)(tempProjectPath);
|
|
279
|
+
// Create and switch to new branch
|
|
280
|
+
await git.checkoutLocalBranch('feature-branch');
|
|
281
|
+
const newBranch = await git.branch();
|
|
282
|
+
const sessionId = (0, uuid_1.v4)();
|
|
283
|
+
db.prepare(`
|
|
284
|
+
INSERT INTO sessions (id, name, branch)
|
|
285
|
+
VALUES (?, ?, ?)
|
|
286
|
+
`).run(sessionId, 'Feature Work', newBranch.current);
|
|
287
|
+
const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(sessionId);
|
|
288
|
+
expect(session.branch).toBe('feature-branch');
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
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 fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const database_js_1 = require("../../utils/database.js");
|
|
41
|
+
(0, globals_1.describe)('Resource Cleanup Tests', () => {
|
|
42
|
+
let tempDir;
|
|
43
|
+
(0, globals_1.beforeEach)(() => {
|
|
44
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mcp-test-'));
|
|
45
|
+
});
|
|
46
|
+
(0, globals_1.afterEach)(() => {
|
|
47
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
48
|
+
});
|
|
49
|
+
(0, globals_1.it)('should properly close database connections', () => {
|
|
50
|
+
const tempDbPath = path.join(tempDir, 'test.db');
|
|
51
|
+
const dbManager = new database_js_1.DatabaseManager({ filename: tempDbPath });
|
|
52
|
+
const db = dbManager.getDatabase();
|
|
53
|
+
// Verify database is open
|
|
54
|
+
(0, globals_1.expect)(() => {
|
|
55
|
+
db.prepare('SELECT 1').get();
|
|
56
|
+
}).not.toThrow();
|
|
57
|
+
// Close the database
|
|
58
|
+
dbManager.close();
|
|
59
|
+
// Verify database is closed
|
|
60
|
+
(0, globals_1.expect)(() => {
|
|
61
|
+
db.prepare('SELECT 1').get();
|
|
62
|
+
}).toThrow('The database connection is not open');
|
|
63
|
+
});
|
|
64
|
+
(0, globals_1.it)('should release file locks after closing', () => {
|
|
65
|
+
const tempDbPath = path.join(tempDir, 'test.db');
|
|
66
|
+
const dbManager = new database_js_1.DatabaseManager({ filename: tempDbPath });
|
|
67
|
+
// Database should be created
|
|
68
|
+
(0, globals_1.expect)(fs.existsSync(tempDbPath)).toBe(true);
|
|
69
|
+
// Close the database
|
|
70
|
+
dbManager.close();
|
|
71
|
+
// Should be able to delete the file (no locks)
|
|
72
|
+
(0, globals_1.expect)(() => {
|
|
73
|
+
fs.unlinkSync(tempDbPath);
|
|
74
|
+
}).not.toThrow();
|
|
75
|
+
});
|
|
76
|
+
(0, globals_1.it)('should handle errors during initialization gracefully', () => {
|
|
77
|
+
// Try to create database in non-existent directory
|
|
78
|
+
const invalidPath = path.join(tempDir, 'non-existent', 'test.db');
|
|
79
|
+
(0, globals_1.expect)(() => {
|
|
80
|
+
new database_js_1.DatabaseManager({ filename: invalidPath });
|
|
81
|
+
}).toThrow();
|
|
82
|
+
});
|
|
83
|
+
(0, globals_1.it)('should clean up WAL files on close', () => {
|
|
84
|
+
const tempDbPath = path.join(tempDir, 'test.db');
|
|
85
|
+
const dbManager = new database_js_1.DatabaseManager({ filename: tempDbPath });
|
|
86
|
+
const db = dbManager.getDatabase();
|
|
87
|
+
// Perform some operations to ensure WAL files are created
|
|
88
|
+
db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run('test', 'Test');
|
|
89
|
+
// WAL files should exist
|
|
90
|
+
(0, globals_1.expect)(fs.existsSync(tempDbPath + '-wal')).toBe(true);
|
|
91
|
+
(0, globals_1.expect)(fs.existsSync(tempDbPath + '-shm')).toBe(true);
|
|
92
|
+
// Close database
|
|
93
|
+
dbManager.close();
|
|
94
|
+
// WAL files should be cleaned up
|
|
95
|
+
// Note: They might still exist but should be minimal size
|
|
96
|
+
if (fs.existsSync(tempDbPath + '-wal')) {
|
|
97
|
+
const walSize = fs.statSync(tempDbPath + '-wal').size;
|
|
98
|
+
(0, globals_1.expect)(walSize).toBe(0);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
(0, globals_1.it)('should handle multiple close calls safely', () => {
|
|
102
|
+
const tempDbPath = path.join(tempDir, 'test.db');
|
|
103
|
+
const dbManager = new database_js_1.DatabaseManager({ filename: tempDbPath });
|
|
104
|
+
// First close
|
|
105
|
+
(0, globals_1.expect)(() => {
|
|
106
|
+
dbManager.close();
|
|
107
|
+
}).not.toThrow();
|
|
108
|
+
// Second close should not throw
|
|
109
|
+
(0, globals_1.expect)(() => {
|
|
110
|
+
dbManager.close();
|
|
111
|
+
}).not.toThrow();
|
|
112
|
+
});
|
|
113
|
+
(0, globals_1.it)('should release memory for large operations', () => {
|
|
114
|
+
const tempDbPath = path.join(tempDir, 'test.db');
|
|
115
|
+
const dbManager = new database_js_1.DatabaseManager({ filename: tempDbPath });
|
|
116
|
+
const db = dbManager.getDatabase();
|
|
117
|
+
// Create session
|
|
118
|
+
db.prepare('INSERT INTO sessions (id, name) VALUES (?, ?)').run('test', 'Test');
|
|
119
|
+
// Insert many items
|
|
120
|
+
const stmt = db.prepare(`
|
|
121
|
+
INSERT INTO context_items (id, session_id, key, value, category, priority)
|
|
122
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
123
|
+
`);
|
|
124
|
+
const largeValue = 'x'.repeat(1000); // 1KB per value
|
|
125
|
+
// Use transaction for better performance
|
|
126
|
+
const insertMany = db.transaction((count) => {
|
|
127
|
+
for (let i = 0; i < count; i++) {
|
|
128
|
+
stmt.run(`item-${i}`, 'test', `key-${i}`, largeValue, 'test', 'normal');
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
// Insert 1000 items (1MB of data)
|
|
132
|
+
insertMany(1000);
|
|
133
|
+
// Verify data was inserted
|
|
134
|
+
const count = db.prepare('SELECT COUNT(*) as count FROM context_items').get();
|
|
135
|
+
(0, globals_1.expect)(count.count).toBe(1000);
|
|
136
|
+
// Close and cleanup
|
|
137
|
+
dbManager.close();
|
|
138
|
+
// Memory should be released (hard to test directly, but no errors should occur)
|
|
139
|
+
});
|
|
140
|
+
(0, globals_1.it)('should handle database file corruption gracefully', () => {
|
|
141
|
+
const tempDbPath = path.join(tempDir, 'corrupt.db');
|
|
142
|
+
// Create a corrupted database file
|
|
143
|
+
fs.writeFileSync(tempDbPath, 'This is not a valid SQLite database');
|
|
144
|
+
// Should throw when trying to open corrupted database
|
|
145
|
+
(0, globals_1.expect)(() => {
|
|
146
|
+
new database_js_1.DatabaseManager({ filename: tempDbPath });
|
|
147
|
+
}).toThrow(/not a database|malformed/);
|
|
148
|
+
});
|
|
149
|
+
});
|