@asifkibria/claude-code-toolkit 1.0.2 → 1.2.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/README.md +165 -214
- package/dist/CLAUDE.md +7 -0
- package/dist/__tests__/dashboard.test.d.ts +2 -0
- package/dist/__tests__/dashboard.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard.test.js +606 -0
- package/dist/__tests__/dashboard.test.js.map +1 -0
- package/dist/__tests__/mcp-validator.test.d.ts +2 -0
- package/dist/__tests__/mcp-validator.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-validator.test.js +217 -0
- package/dist/__tests__/mcp-validator.test.js.map +1 -0
- package/dist/__tests__/scanner.test.js +350 -1
- package/dist/__tests__/scanner.test.js.map +1 -1
- package/dist/__tests__/security.test.d.ts +2 -0
- package/dist/__tests__/security.test.d.ts.map +1 -0
- package/dist/__tests__/security.test.js +375 -0
- package/dist/__tests__/security.test.js.map +1 -0
- package/dist/__tests__/session-recovery.test.d.ts +2 -0
- package/dist/__tests__/session-recovery.test.d.ts.map +1 -0
- package/dist/__tests__/session-recovery.test.js +230 -0
- package/dist/__tests__/session-recovery.test.js.map +1 -0
- package/dist/__tests__/storage.test.d.ts +2 -0
- package/dist/__tests__/storage.test.d.ts.map +1 -0
- package/dist/__tests__/storage.test.js +241 -0
- package/dist/__tests__/storage.test.js.map +1 -0
- package/dist/__tests__/trace.test.d.ts +2 -0
- package/dist/__tests__/trace.test.d.ts.map +1 -0
- package/dist/__tests__/trace.test.js +376 -0
- package/dist/__tests__/trace.test.js.map +1 -0
- package/dist/cli.js +501 -20
- package/dist/cli.js.map +1 -1
- package/dist/index.js +950 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/dashboard-ui.d.ts +2 -0
- package/dist/lib/dashboard-ui.d.ts.map +1 -0
- package/dist/lib/dashboard-ui.js +2075 -0
- package/dist/lib/dashboard-ui.js.map +1 -0
- package/dist/lib/dashboard.d.ts +15 -0
- package/dist/lib/dashboard.d.ts.map +1 -0
- package/dist/lib/dashboard.js +1422 -0
- package/dist/lib/dashboard.js.map +1 -0
- package/dist/lib/logs.d.ts +42 -0
- package/dist/lib/logs.d.ts.map +1 -0
- package/dist/lib/logs.js +166 -0
- package/dist/lib/logs.js.map +1 -0
- package/dist/lib/mcp-validator.d.ts +86 -0
- package/dist/lib/mcp-validator.d.ts.map +1 -0
- package/dist/lib/mcp-validator.js +463 -0
- package/dist/lib/mcp-validator.js.map +1 -0
- package/dist/lib/scanner.d.ts +187 -2
- package/dist/lib/scanner.d.ts.map +1 -1
- package/dist/lib/scanner.js +1224 -14
- package/dist/lib/scanner.js.map +1 -1
- package/dist/lib/security.d.ts +57 -0
- package/dist/lib/security.d.ts.map +1 -0
- package/dist/lib/security.js +423 -0
- package/dist/lib/security.js.map +1 -0
- package/dist/lib/session-recovery.d.ts +60 -0
- package/dist/lib/session-recovery.d.ts.map +1 -0
- package/dist/lib/session-recovery.js +433 -0
- package/dist/lib/session-recovery.js.map +1 -0
- package/dist/lib/storage.d.ts +68 -0
- package/dist/lib/storage.d.ts.map +1 -0
- package/dist/lib/storage.js +500 -0
- package/dist/lib/storage.js.map +1 -0
- package/dist/lib/trace.d.ts +119 -0
- package/dist/lib/trace.d.ts.map +1 -0
- package/dist/lib/trace.js +649 -0
- package/dist/lib/trace.js.map +1 -0
- package/package.json +11 -3
|
@@ -0,0 +1,606 @@
|
|
|
1
|
+
import { describe, it, expect, afterAll } from "vitest";
|
|
2
|
+
import * as http from "http";
|
|
3
|
+
import { createDashboardServer } from "../lib/dashboard.js";
|
|
4
|
+
import { generateDashboardHTML } from "../lib/dashboard-ui.js";
|
|
5
|
+
function request(server, path) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const addr = server.address();
|
|
8
|
+
if (!addr || typeof addr === "string")
|
|
9
|
+
return reject(new Error("Server not listening"));
|
|
10
|
+
const req = http.get({ hostname: "127.0.0.1", port: addr.port, path }, (res) => {
|
|
11
|
+
let body = "";
|
|
12
|
+
res.on("data", (chunk) => { body += chunk.toString(); });
|
|
13
|
+
res.on("end", () => resolve({ status: res.statusCode || 0, body, headers: res.headers }));
|
|
14
|
+
});
|
|
15
|
+
req.on("error", reject);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function postRequest(server, path, data = {}) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const addr = server.address();
|
|
21
|
+
if (!addr || typeof addr === "string")
|
|
22
|
+
return reject(new Error("Server not listening"));
|
|
23
|
+
const payload = JSON.stringify(data);
|
|
24
|
+
const req = http.request({
|
|
25
|
+
hostname: "127.0.0.1",
|
|
26
|
+
port: addr.port,
|
|
27
|
+
path,
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(payload) },
|
|
30
|
+
}, (res) => {
|
|
31
|
+
let body = "";
|
|
32
|
+
res.on("data", (chunk) => { body += chunk.toString(); });
|
|
33
|
+
res.on("end", () => resolve({ status: res.statusCode || 0, body, headers: res.headers }));
|
|
34
|
+
});
|
|
35
|
+
req.on("error", reject);
|
|
36
|
+
req.write(payload);
|
|
37
|
+
req.end();
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
describe("Dashboard", () => {
|
|
41
|
+
let server;
|
|
42
|
+
let port;
|
|
43
|
+
const startServer = () => {
|
|
44
|
+
server = createDashboardServer();
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
server.listen(0, "127.0.0.1", () => {
|
|
47
|
+
const addr = server.address();
|
|
48
|
+
if (addr && typeof addr !== "string")
|
|
49
|
+
port = addr.port;
|
|
50
|
+
resolve();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
afterAll(() => {
|
|
55
|
+
if (server)
|
|
56
|
+
server.close();
|
|
57
|
+
});
|
|
58
|
+
describe("generateDashboardHTML", () => {
|
|
59
|
+
it("should return valid HTML", () => {
|
|
60
|
+
const html = generateDashboardHTML();
|
|
61
|
+
expect(html).toContain("<!DOCTYPE html>");
|
|
62
|
+
expect(html).toContain("<title>Claude Code Toolkit</title>");
|
|
63
|
+
expect(html).toContain("</html>");
|
|
64
|
+
});
|
|
65
|
+
it("should include all 10 navigation tabs", () => {
|
|
66
|
+
const html = generateDashboardHTML();
|
|
67
|
+
expect(html).toContain('data-tab="overview"');
|
|
68
|
+
expect(html).toContain('data-tab="storage"');
|
|
69
|
+
expect(html).toContain('data-tab="sessions"');
|
|
70
|
+
expect(html).toContain('data-tab="security"');
|
|
71
|
+
expect(html).toContain('data-tab="traces"');
|
|
72
|
+
expect(html).toContain('data-tab="mcp"');
|
|
73
|
+
expect(html).toContain('data-tab="analytics"');
|
|
74
|
+
expect(html).toContain('data-tab="backups"');
|
|
75
|
+
expect(html).toContain('data-tab="context"');
|
|
76
|
+
expect(html).toContain('data-tab="maintenance"');
|
|
77
|
+
});
|
|
78
|
+
it("should include all 10 section containers", () => {
|
|
79
|
+
const html = generateDashboardHTML();
|
|
80
|
+
expect(html).toContain('id="sec-overview"');
|
|
81
|
+
expect(html).toContain('id="sec-storage"');
|
|
82
|
+
expect(html).toContain('id="sec-sessions"');
|
|
83
|
+
expect(html).toContain('id="sec-security"');
|
|
84
|
+
expect(html).toContain('id="sec-traces"');
|
|
85
|
+
expect(html).toContain('id="sec-mcp"');
|
|
86
|
+
expect(html).toContain('id="sec-analytics"');
|
|
87
|
+
expect(html).toContain('id="sec-backups"');
|
|
88
|
+
expect(html).toContain('id="sec-context"');
|
|
89
|
+
expect(html).toContain('id="sec-maintenance"');
|
|
90
|
+
});
|
|
91
|
+
it("should include dark and light theme support", () => {
|
|
92
|
+
const html = generateDashboardHTML();
|
|
93
|
+
expect(html).toContain("prefers-color-scheme: light");
|
|
94
|
+
});
|
|
95
|
+
it("should include the esc() sanitization function", () => {
|
|
96
|
+
const html = generateDashboardHTML();
|
|
97
|
+
expect(html).toContain("function esc(s)");
|
|
98
|
+
expect(html).toContain("&");
|
|
99
|
+
expect(html).toContain("<");
|
|
100
|
+
});
|
|
101
|
+
it("should include auto-refresh toggle", () => {
|
|
102
|
+
const html = generateDashboardHTML();
|
|
103
|
+
expect(html).toContain("auto-refresh");
|
|
104
|
+
});
|
|
105
|
+
it("should include toast notification system", () => {
|
|
106
|
+
const html = generateDashboardHTML();
|
|
107
|
+
expect(html).toContain("function toast(");
|
|
108
|
+
});
|
|
109
|
+
it("should include confirmation modal", () => {
|
|
110
|
+
const html = generateDashboardHTML();
|
|
111
|
+
expect(html).toContain("function showModal(");
|
|
112
|
+
});
|
|
113
|
+
it("should include all 10 loader functions", () => {
|
|
114
|
+
const html = generateDashboardHTML();
|
|
115
|
+
expect(html).toContain("function loadOverview()");
|
|
116
|
+
expect(html).toContain("function loadStorage()");
|
|
117
|
+
expect(html).toContain("function loadSessions()");
|
|
118
|
+
expect(html).toContain("function loadSecurity()");
|
|
119
|
+
expect(html).toContain("function loadTraces()");
|
|
120
|
+
expect(html).toContain("function loadMcp()");
|
|
121
|
+
expect(html).toContain("function loadAnalytics()");
|
|
122
|
+
expect(html).toContain("function loadBackups()");
|
|
123
|
+
expect(html).toContain("function loadContext()");
|
|
124
|
+
expect(html).toContain("function loadMaintenance()");
|
|
125
|
+
});
|
|
126
|
+
it("should include redact action functions", () => {
|
|
127
|
+
const html = generateDashboardHTML();
|
|
128
|
+
expect(html).toContain("function doRedact(");
|
|
129
|
+
expect(html).toContain("function doRedactAll()");
|
|
130
|
+
expect(html).toContain("function doPreviewFinding(");
|
|
131
|
+
});
|
|
132
|
+
it("should include audit action function", () => {
|
|
133
|
+
const html = generateDashboardHTML();
|
|
134
|
+
expect(html).toContain("function doAudit(");
|
|
135
|
+
});
|
|
136
|
+
it("should include trace wipe action function", () => {
|
|
137
|
+
const html = generateDashboardHTML();
|
|
138
|
+
expect(html).toContain("function doWipeTraces()");
|
|
139
|
+
});
|
|
140
|
+
it("should include MCP test action function", () => {
|
|
141
|
+
const html = generateDashboardHTML();
|
|
142
|
+
expect(html).toContain("function doTestMcp()");
|
|
143
|
+
});
|
|
144
|
+
it("should include backup action functions", () => {
|
|
145
|
+
const html = generateDashboardHTML();
|
|
146
|
+
expect(html).toContain("function doDeleteBackups()");
|
|
147
|
+
expect(html).toContain("function doRestore(");
|
|
148
|
+
});
|
|
149
|
+
it("should include maintenance and archive action functions", () => {
|
|
150
|
+
const html = generateDashboardHTML();
|
|
151
|
+
expect(html).toContain("function doMaintenance()");
|
|
152
|
+
expect(html).toContain("function doArchive()");
|
|
153
|
+
});
|
|
154
|
+
it("should include switchTab function", () => {
|
|
155
|
+
const html = generateDashboardHTML();
|
|
156
|
+
expect(html).toContain("function switchTab(");
|
|
157
|
+
});
|
|
158
|
+
it("should include fmtK number formatting", () => {
|
|
159
|
+
const html = generateDashboardHTML();
|
|
160
|
+
expect(html).toContain("function fmtK(");
|
|
161
|
+
});
|
|
162
|
+
it("should include ago time formatting", () => {
|
|
163
|
+
const html = generateDashboardHTML();
|
|
164
|
+
expect(html).toContain("function ago(");
|
|
165
|
+
});
|
|
166
|
+
it("should include modal HTML support", () => {
|
|
167
|
+
const html = generateDashboardHTML();
|
|
168
|
+
expect(html).toContain('id="mBodyWrap"');
|
|
169
|
+
expect(html).toContain("isHtml");
|
|
170
|
+
});
|
|
171
|
+
it("should include btn-warn CSS class", () => {
|
|
172
|
+
const html = generateDashboardHTML();
|
|
173
|
+
expect(html).toContain(".btn-warn");
|
|
174
|
+
});
|
|
175
|
+
it("should include modal pre style for previews", () => {
|
|
176
|
+
const html = generateDashboardHTML();
|
|
177
|
+
expect(html).toContain(".modal pre");
|
|
178
|
+
});
|
|
179
|
+
it("should include progress bar element", () => {
|
|
180
|
+
const html = generateDashboardHTML();
|
|
181
|
+
expect(html).toContain('id="progressBar"');
|
|
182
|
+
expect(html).toContain(".progress-bar");
|
|
183
|
+
expect(html).toContain("progressPulse");
|
|
184
|
+
});
|
|
185
|
+
it("should include action overlay element", () => {
|
|
186
|
+
const html = generateDashboardHTML();
|
|
187
|
+
expect(html).toContain('id="actionOverlay"');
|
|
188
|
+
expect(html).toContain('id="actionLabel"');
|
|
189
|
+
expect(html).toContain(".action-overlay");
|
|
190
|
+
expect(html).toContain(".action-card");
|
|
191
|
+
});
|
|
192
|
+
it("should include progress helper functions", () => {
|
|
193
|
+
const html = generateDashboardHTML();
|
|
194
|
+
expect(html).toContain("function showProgress(");
|
|
195
|
+
expect(html).toContain("function hideProgress()");
|
|
196
|
+
expect(html).toContain("function showResult(");
|
|
197
|
+
expect(html).toContain("function reloadCurrent()");
|
|
198
|
+
});
|
|
199
|
+
it("should include result banner CSS", () => {
|
|
200
|
+
const html = generateDashboardHTML();
|
|
201
|
+
expect(html).toContain(".result-banner");
|
|
202
|
+
expect(html).toContain(".result-icon");
|
|
203
|
+
expect(html).toContain(".result-title");
|
|
204
|
+
expect(html).toContain(".result-details");
|
|
205
|
+
expect(html).toContain(".result-close");
|
|
206
|
+
});
|
|
207
|
+
it("should include nav badge styles", () => {
|
|
208
|
+
const html = generateDashboardHTML();
|
|
209
|
+
expect(html).toContain(".nav-badge");
|
|
210
|
+
expect(html).toContain("nav-badge-red");
|
|
211
|
+
expect(html).toContain("nav-badge-yellow");
|
|
212
|
+
});
|
|
213
|
+
it("should include header health dot", () => {
|
|
214
|
+
const html = generateDashboardHTML();
|
|
215
|
+
expect(html).toContain('id="headerDot"');
|
|
216
|
+
expect(html).toContain(".header-dot");
|
|
217
|
+
});
|
|
218
|
+
it("should include keyboard shortcut handler", () => {
|
|
219
|
+
const html = generateDashboardHTML();
|
|
220
|
+
expect(html).toContain("addEventListener('keydown'");
|
|
221
|
+
expect(html).toContain("tabOrder");
|
|
222
|
+
});
|
|
223
|
+
it("should include sortable session columns", () => {
|
|
224
|
+
const html = generateDashboardHTML();
|
|
225
|
+
expect(html).toContain("function sortSessions(");
|
|
226
|
+
expect(html).toContain("function renderSessionTable(");
|
|
227
|
+
expect(html).toContain(".sort-header");
|
|
228
|
+
});
|
|
229
|
+
it("should include sparkline tooltip", () => {
|
|
230
|
+
const html = generateDashboardHTML();
|
|
231
|
+
expect(html).toContain('id="sparkTip"');
|
|
232
|
+
expect(html).toContain(".spark-tooltip");
|
|
233
|
+
});
|
|
234
|
+
it("should include staggered card animation", () => {
|
|
235
|
+
const html = generateDashboardHTML();
|
|
236
|
+
expect(html).toContain("function staggerCards(");
|
|
237
|
+
expect(html).toContain("cardIn");
|
|
238
|
+
expect(html).toContain(".card.stagger");
|
|
239
|
+
});
|
|
240
|
+
it("should include empty state helper", () => {
|
|
241
|
+
const html = generateDashboardHTML();
|
|
242
|
+
expect(html).toContain("function emptyState(");
|
|
243
|
+
expect(html).toContain(".empty-state");
|
|
244
|
+
expect(html).toContain(".es-icon");
|
|
245
|
+
});
|
|
246
|
+
it("should include updateNavBadges function", () => {
|
|
247
|
+
const html = generateDashboardHTML();
|
|
248
|
+
expect(html).toContain("function updateNavBadges(");
|
|
249
|
+
});
|
|
250
|
+
it("should include clickable overview cards", () => {
|
|
251
|
+
const html = generateDashboardHTML();
|
|
252
|
+
expect(html).toContain(".clickable-card");
|
|
253
|
+
});
|
|
254
|
+
it("should include modal close button and click-outside-to-close", () => {
|
|
255
|
+
const html = generateDashboardHTML();
|
|
256
|
+
expect(html).toContain("modal-close");
|
|
257
|
+
expect(html).toContain("if(event.target===this)closeModal()");
|
|
258
|
+
expect(html).toContain('id="mCancel"');
|
|
259
|
+
});
|
|
260
|
+
it("should include keyboard help overlay", () => {
|
|
261
|
+
const html = generateDashboardHTML();
|
|
262
|
+
expect(html).toContain('id="kbdHelp"');
|
|
263
|
+
expect(html).toContain("kbd-help-card");
|
|
264
|
+
expect(html).toContain("Keyboard Shortcuts");
|
|
265
|
+
expect(html).toContain("kbd-key");
|
|
266
|
+
});
|
|
267
|
+
it("should include enhanced modal glassmorphism styles", () => {
|
|
268
|
+
const html = generateDashboardHTML();
|
|
269
|
+
expect(html).toContain("saturate(180%)");
|
|
270
|
+
expect(html).toContain("blur(8px)");
|
|
271
|
+
});
|
|
272
|
+
it("should include mobile responsive styles", () => {
|
|
273
|
+
const html = generateDashboardHTML();
|
|
274
|
+
expect(html).toContain("@media (max-width: 768px)");
|
|
275
|
+
expect(html).toContain("@media (max-width: 480px)");
|
|
276
|
+
});
|
|
277
|
+
it("should include button focus-visible styles", () => {
|
|
278
|
+
const html = generateDashboardHTML();
|
|
279
|
+
expect(html).toContain(".btn:focus-visible");
|
|
280
|
+
expect(html).toContain("outline-offset");
|
|
281
|
+
});
|
|
282
|
+
it("should include severity-based pulse animations", () => {
|
|
283
|
+
const html = generateDashboardHTML();
|
|
284
|
+
expect(html).toContain("dotPulseGreen");
|
|
285
|
+
expect(html).toContain("dotPulseYellow");
|
|
286
|
+
expect(html).toContain("dotPulseRed");
|
|
287
|
+
});
|
|
288
|
+
it("should show audit results in modal popup", () => {
|
|
289
|
+
const html = generateDashboardHTML();
|
|
290
|
+
expect(html).toContain("showModal('Session Audit:");
|
|
291
|
+
});
|
|
292
|
+
it("should show extract results in modal popup", () => {
|
|
293
|
+
const html = generateDashboardHTML();
|
|
294
|
+
expect(html).toContain("showModal('Session Extract:");
|
|
295
|
+
});
|
|
296
|
+
it("should handle audit errors gracefully", () => {
|
|
297
|
+
const html = generateDashboardHTML();
|
|
298
|
+
expect(html).toContain("No Activity Recorded");
|
|
299
|
+
expect(html).toContain("Audit Failed");
|
|
300
|
+
});
|
|
301
|
+
it("should handle extract errors gracefully", () => {
|
|
302
|
+
const html = generateDashboardHTML();
|
|
303
|
+
expect(html).toContain("Empty Session");
|
|
304
|
+
expect(html).toContain("Extract Failed");
|
|
305
|
+
});
|
|
306
|
+
it("should include enhanced table row hover styles", () => {
|
|
307
|
+
const html = generateDashboardHTML();
|
|
308
|
+
expect(html).toContain("table tbody tr:hover");
|
|
309
|
+
expect(html).toContain("border-left-color: var(--accent)");
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
describe("HTTP Server - GET Endpoints", () => {
|
|
313
|
+
it("should serve HTML at root", async () => {
|
|
314
|
+
await startServer();
|
|
315
|
+
const res = await request(server, "/");
|
|
316
|
+
expect(res.status).toBe(200);
|
|
317
|
+
expect(res.headers["content-type"]).toContain("text/html");
|
|
318
|
+
expect(res.body).toContain("<!DOCTYPE html>");
|
|
319
|
+
});
|
|
320
|
+
it("should return 404 for unknown paths", async () => {
|
|
321
|
+
const res = await request(server, "/nonexistent");
|
|
322
|
+
expect(res.status).toBe(404);
|
|
323
|
+
});
|
|
324
|
+
it("should serve overview API", async () => {
|
|
325
|
+
const res = await request(server, "/api/overview");
|
|
326
|
+
expect(res.status).toBe(200);
|
|
327
|
+
expect(res.headers["content-type"]).toContain("application/json");
|
|
328
|
+
const data = JSON.parse(res.body);
|
|
329
|
+
expect(data).toHaveProperty("totalConversations");
|
|
330
|
+
expect(data).toHaveProperty("issueCount");
|
|
331
|
+
expect(data).toHaveProperty("backupCount");
|
|
332
|
+
expect(data).toHaveProperty("backupSize");
|
|
333
|
+
expect(data).toHaveProperty("archiveCandidates");
|
|
334
|
+
expect(data).toHaveProperty("maintenanceActions");
|
|
335
|
+
}, 15000);
|
|
336
|
+
it("should serve storage API", async () => {
|
|
337
|
+
const res = await request(server, "/api/storage");
|
|
338
|
+
expect(res.status).toBe(200);
|
|
339
|
+
const data = JSON.parse(res.body);
|
|
340
|
+
expect(data).toHaveProperty("totalSize");
|
|
341
|
+
expect(data).toHaveProperty("categories");
|
|
342
|
+
});
|
|
343
|
+
it("should serve sessions API", async () => {
|
|
344
|
+
const res = await request(server, "/api/sessions");
|
|
345
|
+
expect(res.status).toBe(200);
|
|
346
|
+
const data = JSON.parse(res.body);
|
|
347
|
+
expect(Array.isArray(data)).toBe(true);
|
|
348
|
+
});
|
|
349
|
+
it("should serve security API", async () => {
|
|
350
|
+
const res = await request(server, "/api/security");
|
|
351
|
+
expect(res.status).toBe(200);
|
|
352
|
+
const data = JSON.parse(res.body);
|
|
353
|
+
expect(data).toHaveProperty("filesScanned");
|
|
354
|
+
expect(data).toHaveProperty("totalFindings");
|
|
355
|
+
});
|
|
356
|
+
it("should serve compliance API", async () => {
|
|
357
|
+
const res = await request(server, "/api/compliance");
|
|
358
|
+
expect(res.status).toBe(200);
|
|
359
|
+
const data = JSON.parse(res.body);
|
|
360
|
+
expect(data).toHaveProperty("secretsScan");
|
|
361
|
+
expect(data).toHaveProperty("sessionCount");
|
|
362
|
+
expect(data).toHaveProperty("retentionStatus");
|
|
363
|
+
expect(data).toHaveProperty("generatedAt");
|
|
364
|
+
});
|
|
365
|
+
it("should serve traces API", async () => {
|
|
366
|
+
const res = await request(server, "/api/traces");
|
|
367
|
+
expect(res.status).toBe(200);
|
|
368
|
+
const data = JSON.parse(res.body);
|
|
369
|
+
expect(data).toHaveProperty("totalSize");
|
|
370
|
+
expect(data).toHaveProperty("totalFiles");
|
|
371
|
+
expect(data).toHaveProperty("categories");
|
|
372
|
+
});
|
|
373
|
+
it("should serve analytics API", async () => {
|
|
374
|
+
const res = await request(server, "/api/analytics");
|
|
375
|
+
expect(res.status).toBe(200);
|
|
376
|
+
const data = JSON.parse(res.body);
|
|
377
|
+
expect(data).toHaveProperty("totalSessions");
|
|
378
|
+
expect(data).toHaveProperty("toolUsage");
|
|
379
|
+
expect(data).toHaveProperty("contextTokens");
|
|
380
|
+
expect(data).toHaveProperty("tokenWarnings");
|
|
381
|
+
expect(data).toHaveProperty("avgTokensPerSession");
|
|
382
|
+
});
|
|
383
|
+
it("should serve duplicates API", async () => {
|
|
384
|
+
const res = await request(server, "/api/duplicates");
|
|
385
|
+
expect(res.status).toBe(200);
|
|
386
|
+
const data = JSON.parse(res.body);
|
|
387
|
+
expect(data).toHaveProperty("totalDuplicates");
|
|
388
|
+
expect(data).toHaveProperty("totalWastedSize");
|
|
389
|
+
expect(data).toHaveProperty("groups");
|
|
390
|
+
});
|
|
391
|
+
it("should serve context API", async () => {
|
|
392
|
+
const res = await request(server, "/api/context");
|
|
393
|
+
expect(res.status).toBe(200);
|
|
394
|
+
const data = JSON.parse(res.body);
|
|
395
|
+
expect(data).toHaveProperty("totalTokens");
|
|
396
|
+
expect(data).toHaveProperty("totalFiles");
|
|
397
|
+
expect(data).toHaveProperty("warnings");
|
|
398
|
+
expect(data).toHaveProperty("estimates");
|
|
399
|
+
expect(Array.isArray(data.estimates)).toBe(true);
|
|
400
|
+
});
|
|
401
|
+
it("should serve backups API", async () => {
|
|
402
|
+
const res = await request(server, "/api/backups");
|
|
403
|
+
expect(res.status).toBe(200);
|
|
404
|
+
const data = JSON.parse(res.body);
|
|
405
|
+
expect(data).toHaveProperty("totalBackups");
|
|
406
|
+
expect(data).toHaveProperty("totalSize");
|
|
407
|
+
expect(data).toHaveProperty("backups");
|
|
408
|
+
expect(Array.isArray(data.backups)).toBe(true);
|
|
409
|
+
});
|
|
410
|
+
it("should serve stats API", async () => {
|
|
411
|
+
const res = await request(server, "/api/stats");
|
|
412
|
+
expect(res.status).toBe(200);
|
|
413
|
+
const data = JSON.parse(res.body);
|
|
414
|
+
expect(data).toHaveProperty("totalFiles");
|
|
415
|
+
expect(data).toHaveProperty("totalMessages");
|
|
416
|
+
expect(data).toHaveProperty("totalImages");
|
|
417
|
+
expect(data).toHaveProperty("totalSize");
|
|
418
|
+
expect(data).toHaveProperty("stats");
|
|
419
|
+
});
|
|
420
|
+
it("should serve archive candidates API", async () => {
|
|
421
|
+
const res = await request(server, "/api/archive/candidates");
|
|
422
|
+
expect(res.status).toBe(200);
|
|
423
|
+
const data = JSON.parse(res.body);
|
|
424
|
+
expect(data).toHaveProperty("totalCandidates");
|
|
425
|
+
expect(data).toHaveProperty("totalSize");
|
|
426
|
+
expect(data).toHaveProperty("candidates");
|
|
427
|
+
expect(Array.isArray(data.candidates)).toBe(true);
|
|
428
|
+
});
|
|
429
|
+
it("should serve maintenance API", async () => {
|
|
430
|
+
const res = await request(server, "/api/maintenance");
|
|
431
|
+
expect(res.status).toBe(200);
|
|
432
|
+
const data = JSON.parse(res.body);
|
|
433
|
+
expect(data).toHaveProperty("status");
|
|
434
|
+
expect(data).toHaveProperty("actions");
|
|
435
|
+
expect(data).toHaveProperty("totalActions");
|
|
436
|
+
expect(data).toHaveProperty("estimatedSpace");
|
|
437
|
+
});
|
|
438
|
+
it("should serve scan API", async () => {
|
|
439
|
+
const res = await request(server, "/api/scan");
|
|
440
|
+
expect(res.status).toBe(200);
|
|
441
|
+
const data = JSON.parse(res.body);
|
|
442
|
+
expect(data).toHaveProperty("totalFiles");
|
|
443
|
+
expect(data).toHaveProperty("totalIssues");
|
|
444
|
+
expect(data).toHaveProperty("filesWithIssues");
|
|
445
|
+
expect(data).toHaveProperty("results");
|
|
446
|
+
});
|
|
447
|
+
it("should set no-cache header on API responses", async () => {
|
|
448
|
+
const res = await request(server, "/api/overview");
|
|
449
|
+
expect(res.headers["cache-control"]).toBe("no-cache");
|
|
450
|
+
});
|
|
451
|
+
it("should return 404 for unknown API endpoints", async () => {
|
|
452
|
+
const res = await request(server, "/api/nonexistent");
|
|
453
|
+
expect(res.status).toBe(404);
|
|
454
|
+
});
|
|
455
|
+
it("should return 404 for nonexistent session detail", async () => {
|
|
456
|
+
const res = await request(server, "/api/session/nonexistent-id-12345");
|
|
457
|
+
expect(res.status).toBe(404);
|
|
458
|
+
const data = JSON.parse(res.body);
|
|
459
|
+
expect(data).toHaveProperty("error");
|
|
460
|
+
});
|
|
461
|
+
it("should return 404 for nonexistent session audit", async () => {
|
|
462
|
+
const res = await request(server, "/api/session/nonexistent-id-12345/audit");
|
|
463
|
+
expect(res.status).toBe(404);
|
|
464
|
+
const data = JSON.parse(res.body);
|
|
465
|
+
expect(data).toHaveProperty("error");
|
|
466
|
+
});
|
|
467
|
+
it("should return 404 for nonexistent security finding", async () => {
|
|
468
|
+
const res = await request(server, "/api/security/finding/nonexistent-file/999");
|
|
469
|
+
expect(res.status).toBe(404);
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
describe("POST Action Endpoints", () => {
|
|
473
|
+
it("should handle clean action with dry run", async () => {
|
|
474
|
+
const res = await postRequest(server, "/api/action/clean", { dryRun: true });
|
|
475
|
+
expect(res.status).toBe(200);
|
|
476
|
+
const data = JSON.parse(res.body);
|
|
477
|
+
expect(data).toHaveProperty("success");
|
|
478
|
+
expect(data.dryRun).toBe(true);
|
|
479
|
+
});
|
|
480
|
+
it("should handle fix-all action", async () => {
|
|
481
|
+
const res = await postRequest(server, "/api/action/fix-all");
|
|
482
|
+
expect(res.status).toBe(200);
|
|
483
|
+
const data = JSON.parse(res.body);
|
|
484
|
+
expect(data).toHaveProperty("success");
|
|
485
|
+
expect(data).toHaveProperty("fixed");
|
|
486
|
+
expect(data).toHaveProperty("total");
|
|
487
|
+
});
|
|
488
|
+
it("should handle fix action with missing file", async () => {
|
|
489
|
+
const res = await postRequest(server, "/api/action/fix", {});
|
|
490
|
+
expect(res.status).toBe(200);
|
|
491
|
+
const data = JSON.parse(res.body);
|
|
492
|
+
expect(data.success).toBe(false);
|
|
493
|
+
expect(data.error).toContain("file");
|
|
494
|
+
});
|
|
495
|
+
it("should handle repair action with missing sessionId", async () => {
|
|
496
|
+
const res = await postRequest(server, "/api/action/repair", {});
|
|
497
|
+
expect(res.status).toBe(200);
|
|
498
|
+
const data = JSON.parse(res.body);
|
|
499
|
+
expect(data.success).toBe(false);
|
|
500
|
+
expect(data.error).toContain("sessionId");
|
|
501
|
+
});
|
|
502
|
+
it("should handle extract action with missing sessionId", async () => {
|
|
503
|
+
const res = await postRequest(server, "/api/action/extract", {});
|
|
504
|
+
expect(res.status).toBe(200);
|
|
505
|
+
const data = JSON.parse(res.body);
|
|
506
|
+
expect(data.success).toBe(false);
|
|
507
|
+
expect(data.error).toContain("sessionId");
|
|
508
|
+
});
|
|
509
|
+
it("should handle retention action with dry run", async () => {
|
|
510
|
+
const res = await postRequest(server, "/api/action/retention", { dryRun: true, days: 30 });
|
|
511
|
+
expect(res.status).toBe(200);
|
|
512
|
+
const data = JSON.parse(res.body);
|
|
513
|
+
expect(data).toHaveProperty("success");
|
|
514
|
+
expect(data.dryRun).toBe(true);
|
|
515
|
+
});
|
|
516
|
+
it("should handle clean-traces action with dry run", async () => {
|
|
517
|
+
const res = await postRequest(server, "/api/action/clean-traces", { dryRun: true });
|
|
518
|
+
expect(res.status).toBe(200);
|
|
519
|
+
const data = JSON.parse(res.body);
|
|
520
|
+
expect(data).toHaveProperty("success");
|
|
521
|
+
expect(data.dryRun).toBe(true);
|
|
522
|
+
});
|
|
523
|
+
it("should handle redact action with missing file", async () => {
|
|
524
|
+
const res = await postRequest(server, "/api/action/redact", {});
|
|
525
|
+
expect(res.status).toBe(200);
|
|
526
|
+
const data = JSON.parse(res.body);
|
|
527
|
+
expect(data.success).toBe(false);
|
|
528
|
+
expect(data.error).toContain("file");
|
|
529
|
+
});
|
|
530
|
+
it("should handle redact action with nonexistent file", async () => {
|
|
531
|
+
const res = await postRequest(server, "/api/action/redact", { file: "/tmp/nonexistent-file.jsonl", line: 1 });
|
|
532
|
+
expect(res.status).toBe(200);
|
|
533
|
+
const data = JSON.parse(res.body);
|
|
534
|
+
expect(data.success).toBe(false);
|
|
535
|
+
});
|
|
536
|
+
it("should handle redact-all action", async () => {
|
|
537
|
+
const res = await postRequest(server, "/api/action/redact-all");
|
|
538
|
+
expect(res.status).toBe(200);
|
|
539
|
+
const data = JSON.parse(res.body);
|
|
540
|
+
expect(data).toHaveProperty("success");
|
|
541
|
+
expect(data).toHaveProperty("filesModified");
|
|
542
|
+
expect(data).toHaveProperty("secretsRedacted");
|
|
543
|
+
});
|
|
544
|
+
it("should handle archive action with dry run", async () => {
|
|
545
|
+
const res = await postRequest(server, "/api/action/archive", { dryRun: true });
|
|
546
|
+
expect(res.status).toBe(200);
|
|
547
|
+
const data = JSON.parse(res.body);
|
|
548
|
+
expect(data).toHaveProperty("success");
|
|
549
|
+
expect(data.dryRun).toBe(true);
|
|
550
|
+
});
|
|
551
|
+
it("should handle maintenance action as dry run by default", async () => {
|
|
552
|
+
const res = await postRequest(server, "/api/action/maintenance", {});
|
|
553
|
+
expect(res.status).toBe(200);
|
|
554
|
+
const data = JSON.parse(res.body);
|
|
555
|
+
expect(data).toHaveProperty("success");
|
|
556
|
+
expect(data).toHaveProperty("actionsPerformed");
|
|
557
|
+
expect(data.auto).toBe(false);
|
|
558
|
+
});
|
|
559
|
+
it("should handle delete-backups action", async () => {
|
|
560
|
+
const res = await postRequest(server, "/api/action/delete-backups", { days: 9999 });
|
|
561
|
+
expect(res.status).toBe(200);
|
|
562
|
+
const data = JSON.parse(res.body);
|
|
563
|
+
expect(data).toHaveProperty("success");
|
|
564
|
+
expect(data).toHaveProperty("deleted");
|
|
565
|
+
});
|
|
566
|
+
it("should handle restore action with missing backupPath", async () => {
|
|
567
|
+
const res = await postRequest(server, "/api/action/restore", {});
|
|
568
|
+
expect(res.status).toBe(200);
|
|
569
|
+
const data = JSON.parse(res.body);
|
|
570
|
+
expect(data.success).toBe(false);
|
|
571
|
+
expect(data.error).toContain("backupPath");
|
|
572
|
+
});
|
|
573
|
+
it("should handle export action with missing file", async () => {
|
|
574
|
+
const res = await postRequest(server, "/api/action/export", {});
|
|
575
|
+
expect(res.status).toBe(200);
|
|
576
|
+
const data = JSON.parse(res.body);
|
|
577
|
+
expect(data.success).toBe(false);
|
|
578
|
+
expect(data.error).toContain("file");
|
|
579
|
+
});
|
|
580
|
+
it("should handle export action with nonexistent file", async () => {
|
|
581
|
+
const res = await postRequest(server, "/api/action/export", { file: "/tmp/nonexistent-file.jsonl" });
|
|
582
|
+
expect(res.status).toBe(200);
|
|
583
|
+
const data = JSON.parse(res.body);
|
|
584
|
+
expect(data.success).toBe(false);
|
|
585
|
+
});
|
|
586
|
+
it("should handle wipe-traces action without confirmation", async () => {
|
|
587
|
+
const res = await postRequest(server, "/api/action/wipe-traces", {});
|
|
588
|
+
expect(res.status).toBe(200);
|
|
589
|
+
const data = JSON.parse(res.body);
|
|
590
|
+
expect(data.success).toBe(false);
|
|
591
|
+
expect(data.error).toContain("confirm");
|
|
592
|
+
});
|
|
593
|
+
it("should handle test-mcp action", async () => {
|
|
594
|
+
const res = await postRequest(server, "/api/action/test-mcp");
|
|
595
|
+
expect(res.status).toBe(200);
|
|
596
|
+
const data = JSON.parse(res.body);
|
|
597
|
+
expect(data).toHaveProperty("totalServers");
|
|
598
|
+
expect(data).toHaveProperty("healthyServers");
|
|
599
|
+
}, 30000);
|
|
600
|
+
it("should return 404 for unknown POST actions", async () => {
|
|
601
|
+
const res = await postRequest(server, "/api/action/nonexistent");
|
|
602
|
+
expect(res.status).toBe(404);
|
|
603
|
+
});
|
|
604
|
+
});
|
|
605
|
+
});
|
|
606
|
+
//# sourceMappingURL=dashboard.test.js.map
|