lynkr 7.2.5 → 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +3 -3
  2. package/config/model-tiers.json +89 -0
  3. package/install.sh +6 -1
  4. package/package.json +4 -2
  5. package/scripts/setup.js +0 -1
  6. package/src/agents/executor.js +14 -6
  7. package/src/api/middleware/session.js +15 -2
  8. package/src/api/openai-router.js +162 -37
  9. package/src/api/providers-handler.js +15 -1
  10. package/src/api/router.js +107 -2
  11. package/src/budget/index.js +4 -3
  12. package/src/clients/databricks.js +431 -234
  13. package/src/clients/gpt-utils.js +181 -0
  14. package/src/clients/ollama-utils.js +66 -140
  15. package/src/clients/routing.js +0 -1
  16. package/src/clients/standard-tools.js +99 -3
  17. package/src/config/index.js +133 -35
  18. package/src/context/toon.js +173 -0
  19. package/src/logger/index.js +23 -0
  20. package/src/orchestrator/index.js +688 -213
  21. package/src/routing/agentic-detector.js +320 -0
  22. package/src/routing/complexity-analyzer.js +202 -2
  23. package/src/routing/cost-optimizer.js +305 -0
  24. package/src/routing/index.js +168 -159
  25. package/src/routing/model-tiers.js +365 -0
  26. package/src/server.js +4 -14
  27. package/src/sessions/cleanup.js +3 -3
  28. package/src/sessions/record.js +10 -1
  29. package/src/sessions/store.js +7 -2
  30. package/src/tools/agent-task.js +48 -1
  31. package/src/tools/index.js +19 -2
  32. package/src/tools/lazy-loader.js +7 -0
  33. package/src/tools/tinyfish.js +358 -0
  34. package/src/tools/truncate.js +1 -0
  35. package/.github/FUNDING.yml +0 -15
  36. package/.github/workflows/README.md +0 -215
  37. package/.github/workflows/ci.yml +0 -69
  38. package/.github/workflows/index.yml +0 -62
  39. package/.github/workflows/web-tools-tests.yml +0 -56
  40. package/CITATIONS.bib +0 -6
  41. package/CLAWROUTER_ROUTING_PLAN.md +0 -910
  42. package/DEPLOYMENT.md +0 -1001
  43. package/LYNKR-TUI-PLAN.md +0 -984
  44. package/PERFORMANCE-REPORT.md +0 -866
  45. package/PLAN-per-client-model-routing.md +0 -252
  46. package/ROUTER_COMPARISON.md +0 -173
  47. package/TIER_ROUTING_PLAN.md +0 -771
  48. package/docs/42642f749da6234f41b6b425c3bb07c9.txt +0 -1
  49. package/docs/BingSiteAuth.xml +0 -4
  50. package/docs/docs-style.css +0 -478
  51. package/docs/docs.html +0 -197
  52. package/docs/google5be250e608e6da39.html +0 -1
  53. package/docs/index.html +0 -577
  54. package/docs/index.md +0 -577
  55. package/docs/robots.txt +0 -4
  56. package/docs/sitemap.xml +0 -44
  57. package/docs/style.css +0 -1223
  58. package/documentation/README.md +0 -100
  59. package/documentation/api.md +0 -806
  60. package/documentation/claude-code-cli.md +0 -672
  61. package/documentation/codex-cli.md +0 -397
  62. package/documentation/contributing.md +0 -571
  63. package/documentation/cursor-integration.md +0 -731
  64. package/documentation/docker.md +0 -867
  65. package/documentation/embeddings.md +0 -760
  66. package/documentation/faq.md +0 -659
  67. package/documentation/features.md +0 -396
  68. package/documentation/headroom.md +0 -519
  69. package/documentation/installation.md +0 -706
  70. package/documentation/memory-system.md +0 -476
  71. package/documentation/production.md +0 -601
  72. package/documentation/providers.md +0 -906
  73. package/documentation/testing.md +0 -629
  74. package/documentation/token-optimization.md +0 -323
  75. package/documentation/tools.md +0 -697
  76. package/documentation/troubleshooting.md +0 -893
  77. package/final-test.js +0 -33
  78. package/headroom-sidecar/config.py +0 -93
  79. package/headroom-sidecar/requirements.txt +0 -14
  80. package/headroom-sidecar/server.py +0 -451
  81. package/monitor-agents.sh +0 -31
  82. package/scripts/audit-log-reader.js +0 -399
  83. package/scripts/compact-dictionary.js +0 -204
  84. package/scripts/test-deduplication.js +0 -448
  85. package/src/db/database.sqlite +0 -0
  86. package/test/README.md +0 -212
  87. package/test/azure-openai-config.test.js +0 -204
  88. package/test/azure-openai-error-resilience.test.js +0 -238
  89. package/test/azure-openai-format-conversion.test.js +0 -354
  90. package/test/azure-openai-integration.test.js +0 -281
  91. package/test/azure-openai-routing.test.js +0 -177
  92. package/test/azure-openai-streaming.test.js +0 -171
  93. package/test/bedrock-integration.test.js +0 -471
  94. package/test/comprehensive-test-suite.js +0 -928
  95. package/test/config-validation.test.js +0 -207
  96. package/test/cursor-integration.test.js +0 -484
  97. package/test/format-conversion.test.js +0 -578
  98. package/test/hybrid-routing-integration.test.js +0 -254
  99. package/test/hybrid-routing-performance.test.js +0 -418
  100. package/test/llamacpp-integration.test.js +0 -863
  101. package/test/lmstudio-integration.test.js +0 -335
  102. package/test/memory/extractor.test.js +0 -398
  103. package/test/memory/retriever.test.js +0 -613
  104. package/test/memory/retriever.test.js.bak +0 -585
  105. package/test/memory/search.test.js +0 -537
  106. package/test/memory/search.test.js.bak +0 -389
  107. package/test/memory/store.test.js +0 -344
  108. package/test/memory/store.test.js.bak +0 -312
  109. package/test/memory/surprise.test.js +0 -300
  110. package/test/memory-performance.test.js +0 -472
  111. package/test/openai-integration.test.js +0 -686
  112. package/test/openrouter-error-resilience.test.js +0 -418
  113. package/test/passthrough-mode.test.js +0 -385
  114. package/test/performance-benchmark.js +0 -351
  115. package/test/performance-tests.js +0 -528
  116. package/test/routing.test.js +0 -219
  117. package/test/web-tools.test.js +0 -329
  118. package/test-agents-simple.js +0 -43
  119. package/test-cli-connection.sh +0 -33
  120. package/test-learning-unit.js +0 -126
  121. package/test-learning.js +0 -112
  122. package/test-parallel-agents.sh +0 -124
  123. package/test-parallel-direct.js +0 -155
  124. package/test-subagents.sh +0 -117
@@ -1,448 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Test script for deduplication functionality
5
- * Creates mock log entries and verifies deduplication works correctly
6
- */
7
-
8
- const fs = require("fs");
9
- const path = require("path");
10
- const { ContentDeduplicator } = require("../src/logger/deduplicator");
11
-
12
- // Test configuration
13
- const TEST_DICT_PATH = path.join(process.cwd(), "logs", "test-dictionary.jsonl");
14
- const TEST_LOG_PATH = path.join(process.cwd(), "logs", "test-audit.log");
15
-
16
- // Clean up test files if they exist
17
- function cleanup() {
18
- if (fs.existsSync(TEST_DICT_PATH)) {
19
- fs.unlinkSync(TEST_DICT_PATH);
20
- }
21
- if (fs.existsSync(TEST_LOG_PATH)) {
22
- fs.unlinkSync(TEST_LOG_PATH);
23
- }
24
- }
25
-
26
- // Test 1: Basic deduplication
27
- function testBasicDeduplication() {
28
- console.log("\n=== Test 1: Basic Deduplication ===");
29
-
30
- const deduplicator = new ContentDeduplicator(TEST_DICT_PATH, {
31
- minSize: 50, // Lower threshold for testing
32
- cacheSize: 10,
33
- });
34
-
35
- const content1 = "This is a test content that is longer than 50 characters and should be deduplicated.";
36
- const content2 = "This is a test content that is longer than 50 characters and should be deduplicated.";
37
- const content3 = "Short";
38
-
39
- // First content should be stored
40
- const ref1 = deduplicator.storeContent(content1);
41
- console.log("✓ Stored content1:", ref1);
42
-
43
- // Second identical content should return same reference
44
- const ref2 = deduplicator.storeContent(content2);
45
- console.log("✓ Stored content2 (should be same hash):", ref2);
46
-
47
- // Verify same hash
48
- if (ref1.$ref !== ref2.$ref) {
49
- console.error("✗ FAIL: Different hashes for identical content!");
50
- return false;
51
- }
52
- console.log("✓ PASS: Identical content produces same hash");
53
-
54
- // Short content should not be deduplicated (below threshold)
55
- const shouldNotDedup = deduplicator.shouldDeduplicate(content3, 50);
56
- if (shouldNotDedup) {
57
- console.error("✗ FAIL: Short content should not be deduplicated!");
58
- return false;
59
- }
60
- console.log("✓ PASS: Short content not deduplicated");
61
-
62
- return true;
63
- }
64
-
65
- // Test 2: Content restoration
66
- function testContentRestoration() {
67
- console.log("\n=== Test 2: Content Restoration ===");
68
-
69
- const deduplicator = new ContentDeduplicator(TEST_DICT_PATH, {
70
- minSize: 50,
71
- cacheSize: 10,
72
- });
73
-
74
- const originalContent = "This is original content that needs to be restored from the dictionary file.";
75
-
76
- // Store and get reference
77
- const ref = deduplicator.storeContent(originalContent);
78
- console.log("✓ Stored content with ref:", ref.$ref);
79
-
80
- // Retrieve content
81
- const retrieved = deduplicator.getContent(ref.$ref);
82
- console.log("✓ Retrieved content length:", retrieved?.length);
83
-
84
- // Verify content matches
85
- if (retrieved !== originalContent) {
86
- console.error("✗ FAIL: Retrieved content doesn't match original!");
87
- console.error("Expected:", originalContent);
88
- console.error("Got:", retrieved);
89
- return false;
90
- }
91
- console.log("✓ PASS: Content restored correctly");
92
-
93
- return true;
94
- }
95
-
96
- // Test 3: Entry deduplication and restoration
97
- function testEntryProcessing() {
98
- console.log("\n=== Test 3: Entry Deduplication and Restoration ===");
99
-
100
- const deduplicator = new ContentDeduplicator(TEST_DICT_PATH, {
101
- minSize: 50,
102
- cacheSize: 10,
103
- });
104
-
105
- const systemPrompt = "You are a helpful AI assistant. This is a long system prompt that should be deduplicated.";
106
- const userMessage = "This is a user message that is long enough to be deduplicated by the deduplication system.";
107
-
108
- const entry = {
109
- type: "llm_request",
110
- correlationId: "test-123",
111
- systemPrompt: systemPrompt,
112
- userMessages: userMessage,
113
- model: "test-model",
114
- };
115
-
116
- // Deduplicate entry
117
- const deduplicated = deduplicator.deduplicateEntry(entry, ["systemPrompt", "userMessages"]);
118
- console.log("✓ Deduplicated entry:", JSON.stringify(deduplicated, null, 2));
119
-
120
- // Verify fields are now references
121
- if (typeof deduplicated.systemPrompt !== "object" || !deduplicated.systemPrompt.$ref) {
122
- console.error("✗ FAIL: systemPrompt was not deduplicated!");
123
- return false;
124
- }
125
- if (typeof deduplicated.userMessages !== "object" || !deduplicated.userMessages.$ref) {
126
- console.error("✗ FAIL: userMessages was not deduplicated!");
127
- return false;
128
- }
129
- console.log("✓ PASS: Fields converted to references");
130
-
131
- // Restore entry
132
- const restored = deduplicator.restoreEntry(deduplicated);
133
- console.log("✓ Restored entry keys:", Object.keys(restored));
134
-
135
- // Verify restoration
136
- if (restored.systemPrompt !== systemPrompt) {
137
- console.error("✗ FAIL: systemPrompt not restored correctly!");
138
- console.error("Expected:", systemPrompt);
139
- console.error("Got:", restored.systemPrompt);
140
- return false;
141
- }
142
- if (restored.userMessages !== userMessage) {
143
- console.error("✗ FAIL: userMessages not restored correctly!");
144
- console.error("Expected:", userMessage);
145
- console.error("Got:", restored.userMessages);
146
- return false;
147
- }
148
- console.log("✓ PASS: Entry restored correctly");
149
-
150
- return true;
151
- }
152
-
153
- // Test 4: Dictionary persistence
154
- function testDictionaryPersistence() {
155
- console.log("\n=== Test 4: Dictionary Persistence ===");
156
-
157
- // Create first deduplicator and store content
158
- const deduplicator1 = new ContentDeduplicator(TEST_DICT_PATH, {
159
- minSize: 50,
160
- cacheSize: 10,
161
- });
162
-
163
- const content = "This is test content for persistence verification across deduplicator instances.";
164
- const ref = deduplicator1.storeContent(content);
165
- console.log("✓ Stored content with first deduplicator:", ref.$ref);
166
-
167
- // Wait for async write to complete
168
- setTimeout(() => {
169
- // Create second deduplicator (should load from dictionary)
170
- const deduplicator2 = new ContentDeduplicator(TEST_DICT_PATH, {
171
- minSize: 50,
172
- cacheSize: 10,
173
- });
174
-
175
- // Try to retrieve with second deduplicator
176
- const retrieved = deduplicator2.getContent(ref.$ref);
177
-
178
- if (retrieved !== content) {
179
- console.error("✗ FAIL: Content not persisted to dictionary!");
180
- console.error("Expected:", content);
181
- console.error("Got:", retrieved);
182
- return false;
183
- }
184
- console.log("✓ PASS: Dictionary persisted and loaded correctly");
185
-
186
- // Show dictionary stats
187
- const stats = deduplicator2.getStats();
188
- console.log("\nDeduplication Stats:");
189
- console.log(` Cache size: ${stats.cacheSize}`);
190
- console.log(` Unique blocks: ${stats.uniqueContentBlocks}`);
191
- console.log(` Total references: ${stats.totalReferences}`);
192
-
193
- return true;
194
- }, 100);
195
- }
196
-
197
- // Test 5: Size calculation and verification
198
- function testSizeCalculation() {
199
- console.log("\n=== Test 5: Size Calculation ===");
200
-
201
- const deduplicator = new ContentDeduplicator(TEST_DICT_PATH, {
202
- minSize: 50,
203
- cacheSize: 10,
204
- });
205
-
206
- const content = "This is a test content string that will be deduplicated and have its size calculated.";
207
- const ref = deduplicator.storeContent(content);
208
-
209
- console.log("✓ Content length:", content.length);
210
- console.log("✓ Reference size field:", ref.size);
211
-
212
- if (ref.size !== content.length) {
213
- console.error("✗ FAIL: Size mismatch!");
214
- return false;
215
- }
216
- console.log("✓ PASS: Size calculated correctly");
217
-
218
- // Calculate space saved
219
- const refSize = JSON.stringify(ref).length;
220
- const originalSize = content.length;
221
- const saved = originalSize - refSize;
222
- const savedPercent = ((saved / originalSize) * 100).toFixed(1);
223
-
224
- console.log(`\nSpace saved: ${saved} bytes (${savedPercent}%)`);
225
- console.log(` Original: ${originalSize} bytes`);
226
- console.log(` Reference: ${refSize} bytes`);
227
-
228
- return true;
229
- }
230
-
231
- // Test 6: Content sanitization (empty User: entries removal)
232
- function testContentSanitization() {
233
- console.log("\n=== Test 6: Content Sanitization (Empty User: Removal) ===");
234
-
235
- const deduplicator = new ContentDeduplicator(TEST_DICT_PATH, {
236
- minSize: 50,
237
- cacheSize: 10,
238
- sanitize: true, // Enable sanitization
239
- });
240
-
241
- // Content with multiple empty "User:" entries
242
- const dirtyContent = `Claude: I'll implement...
243
-
244
- User:
245
-
246
- Claude: Now I'll implement...
247
-
248
- User:
249
-
250
- User:
251
-
252
- User:
253
-
254
- Respond with the title for the conversation and nothing else.`;
255
-
256
- console.log("✓ Original content length:", dirtyContent.length);
257
- console.log("✓ Empty 'User:' entries in original:", (dirtyContent.match(/User:\s*\n/g) || []).length);
258
-
259
- // Store the content (should be sanitized internally)
260
- const ref = deduplicator.storeContent(dirtyContent);
261
- console.log("✓ Stored content with ref:", ref.$ref);
262
-
263
- // Retrieve it back
264
- const retrieved = deduplicator.getContent(ref.$ref);
265
- console.log("✓ Retrieved content length:", retrieved?.length);
266
-
267
- // Count empty "User:" entries in retrieved content
268
- // Pattern: "User:" followed by newline(s) and then "Claude:" or another "User:" or end
269
- const emptyUserMatches = retrieved.match(/User:\s*\n+(?=(Claude:|User:|$))/g) || [];
270
- console.log("✓ Empty 'User:' entries in retrieved:", emptyUserMatches.length);
271
-
272
- // Verify empty User: entries were removed
273
- if (emptyUserMatches.length > 0) {
274
- console.error("✗ FAIL: Empty User: entries not removed!");
275
- console.error("Retrieved content:", retrieved);
276
- return false;
277
- }
278
-
279
- // Verify content still contains Claude: entries
280
- if (!retrieved.includes("Claude:")) {
281
- console.error("✗ FAIL: Claude: entries were incorrectly removed!");
282
- return false;
283
- }
284
-
285
- // Verify the last line is preserved
286
- if (!retrieved.includes("Respond with the title")) {
287
- console.error("✗ FAIL: Content was over-sanitized!");
288
- return false;
289
- }
290
-
291
- console.log("✓ PASS: Empty User: entries removed, content preserved");
292
-
293
- // Test with sanitization disabled
294
- const dedupNoSanitize = new ContentDeduplicator(TEST_DICT_PATH, {
295
- minSize: 50,
296
- cacheSize: 10,
297
- sanitize: false, // Disable sanitization
298
- });
299
-
300
- const refNoSanitize = dedupNoSanitize.storeContent(dirtyContent);
301
- const retrievedNoSanitize = dedupNoSanitize.getContent(refNoSanitize.$ref);
302
-
303
- // Should have empty User: entries when sanitization is disabled
304
- const emptyUserNoSanitize = retrievedNoSanitize.match(/User:\s*\n+(?=(Claude:|User:|$))/g) || [];
305
- if (emptyUserNoSanitize.length === 0) {
306
- console.error("✗ FAIL: Content was sanitized even with sanitize=false!");
307
- return false;
308
- }
309
-
310
- console.log("✓ PASS: Sanitization can be disabled");
311
-
312
- return true;
313
- }
314
-
315
- // Test 7: Content sanitization preserves non-empty User: entries
316
- function testSanitizationPreservesContent() {
317
- console.log("\n=== Test 7: Sanitization Preserves Non-Empty User: Entries ===");
318
-
319
- const deduplicator = new ContentDeduplicator(TEST_DICT_PATH, {
320
- minSize: 50,
321
- cacheSize: 10,
322
- sanitize: true,
323
- });
324
-
325
- // Content with both empty and non-empty User: entries
326
- const mixedContent = `Claude: I'll help you.
327
-
328
- User: Can you explain this?
329
-
330
- Claude: Sure, here's the explanation.
331
-
332
- User:
333
-
334
- User: Another question here.
335
-
336
- Claude: Here's the answer.`;
337
-
338
- console.log("✓ Original has both empty and non-empty User: entries");
339
-
340
- const ref = deduplicator.storeContent(mixedContent);
341
- const retrieved = deduplicator.getContent(ref.$ref);
342
-
343
- // Check that non-empty User: entries are preserved
344
- if (!retrieved.includes("User: Can you explain this?")) {
345
- console.error("✗ FAIL: Non-empty User: entry was removed!");
346
- return false;
347
- }
348
-
349
- if (!retrieved.includes("User: Another question here.")) {
350
- console.error("✗ FAIL: Non-empty User: entry was removed!");
351
- return false;
352
- }
353
-
354
- // Check that empty User: entries are removed
355
- const lines = retrieved.split('\n');
356
- let hasEmptyUser = false;
357
- for (let i = 0; i < lines.length; i++) {
358
- const line = lines[i].trim();
359
- if (line === 'User:' || line === 'User: ') {
360
- const nextLine = i + 1 < lines.length ? lines[i + 1].trim() : '';
361
- if (nextLine === '' || nextLine === 'Claude:' || nextLine === 'User:') {
362
- hasEmptyUser = true;
363
- break;
364
- }
365
- }
366
- }
367
-
368
- if (hasEmptyUser) {
369
- console.error("✗ FAIL: Empty User: entries not removed from mixed content!");
370
- return false;
371
- }
372
-
373
- console.log("✓ PASS: Non-empty User: entries preserved, empty ones removed");
374
-
375
- return true;
376
- }
377
-
378
- // Main test runner
379
- async function runTests() {
380
- console.log("=".repeat(60));
381
- console.log("LLM Audit Log Deduplication Test Suite");
382
- console.log("=".repeat(60));
383
-
384
- // Clean up before tests
385
- cleanup();
386
-
387
- const tests = [
388
- testBasicDeduplication,
389
- testContentRestoration,
390
- testEntryProcessing,
391
- testSizeCalculation,
392
- testContentSanitization,
393
- testSanitizationPreservesContent,
394
- ];
395
-
396
- let passed = 0;
397
- let failed = 0;
398
-
399
- for (const test of tests) {
400
- try {
401
- const result = test();
402
- if (result) {
403
- passed++;
404
- } else {
405
- failed++;
406
- }
407
- } catch (err) {
408
- console.error(`✗ Test failed with error: ${err.message}`);
409
- console.error(err.stack);
410
- failed++;
411
- }
412
- }
413
-
414
- // Run async test separately
415
- setTimeout(() => {
416
- testDictionaryPersistence();
417
-
418
- console.log("\n" + "=".repeat(60));
419
- console.log("Test Results");
420
- console.log("=".repeat(60));
421
- console.log(`Passed: ${passed}/${passed + failed}`);
422
- console.log(`Failed: ${failed}/${passed + failed}`);
423
-
424
- if (failed === 0) {
425
- console.log("\n✓ All tests passed!");
426
- console.log("\nDictionary file created at:", TEST_DICT_PATH);
427
- console.log("You can inspect it with: cat", TEST_DICT_PATH);
428
- } else {
429
- console.log("\n✗ Some tests failed!");
430
- process.exit(1);
431
- }
432
-
433
- // Clean up after tests
434
- console.log("\nCleaning up test files...");
435
- cleanup();
436
- console.log("✓ Test files removed");
437
- }, 200);
438
- }
439
-
440
- // Run tests
441
- if (require.main === module) {
442
- runTests().catch((err) => {
443
- console.error("Test suite failed:", err);
444
- process.exit(1);
445
- });
446
- }
447
-
448
- module.exports = { runTests };
File without changes
package/test/README.md DELETED
@@ -1,212 +0,0 @@
1
- # Test Suite Documentation
2
-
3
- All tests for the Lynkr project are consolidated in this `test/` directory.
4
-
5
- ## Test Files
6
-
7
- ### Unit Tests
8
- **File**: `routing.test.js`
9
- **Purpose**: Tests the hybrid routing logic in isolation
10
- **Run**: `DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/routing.test.js`
11
- **Coverage**: 10 tests
12
- - Routing with PREFER_OLLAMA disabled
13
- - Simple requests → Ollama
14
- - Complex requests → Cloud
15
- - Tool capability checks
16
- - Fallback configuration
17
-
18
- ---
19
-
20
- ### Integration Tests
21
- **File**: `hybrid-routing-integration.test.js`
22
- **Purpose**: Tests configuration validation and metrics recording
23
- **Run**: `node --test test/hybrid-routing-integration.test.js`
24
- **Coverage**: 13 tests
25
- - Configuration validation (5 tests)
26
- - Metrics recording (6 tests)
27
- - Helper functions (2 tests)
28
-
29
- ---
30
-
31
- ### Hybrid Routing Performance Tests
32
- **File**: `hybrid-routing-performance.test.js`
33
- **Purpose**: Measures performance overhead of hybrid routing
34
- **Run**: `node test/hybrid-routing-performance.test.js`
35
- **Key Metrics**:
36
- - Routing decision: <0.01ms (36.8M decisions/sec)
37
- - Metrics overhead: <0.01ms (43.6M ops/sec)
38
- - Combined overhead: <0.02ms (15.6M ops/sec)
39
-
40
- ---
41
-
42
- ### System Performance Tests
43
- **File**: `performance-tests.js`
44
- **Purpose**: Tests system-wide performance optimizations
45
- **Run**: `DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node test/performance-tests.js`
46
- **Coverage**:
47
- - Database indexes (100% complete)
48
- - Persistent prompt cache
49
- - Regex pattern caching (4.5x faster)
50
- - Lazy loading
51
- - HTTP connection pooling
52
- - Response compression
53
-
54
- ---
55
-
56
- ### Middleware Benchmarks
57
- **File**: `performance-benchmark.js`
58
- **Purpose**: Benchmarks middleware overhead
59
- **Run**: `DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node test/performance-benchmark.js`
60
- **Coverage**:
61
- - Metrics collection (3.4M ops/sec)
62
- - Circuit breakers (3.9M ops/sec)
63
- - Input validation (5.7M ops/sec)
64
- - Load shedding
65
- - Combined middleware stack
66
-
67
- ---
68
-
69
- ## Running All Tests
70
-
71
- ### Using npm scripts (Recommended)
72
-
73
- **Run all tests (unit + performance):**
74
- ```bash
75
- npm test
76
- ```
77
-
78
- **Run only unit/integration tests:**
79
- ```bash
80
- npm run test:unit
81
- ```
82
-
83
- **Run only performance tests:**
84
- ```bash
85
- npm run test:performance
86
- ```
87
-
88
- **Run only benchmarks:**
89
- ```bash
90
- npm run test:benchmark
91
- ```
92
-
93
- **Run quick smoke test (routing only):**
94
- ```bash
95
- npm run test:quick
96
- ```
97
-
98
- **Run everything including benchmarks:**
99
- ```bash
100
- npm run test:all
101
- ```
102
-
103
- ### Manual execution (if needed)
104
-
105
- **Quick Test:**
106
- Run all unit and integration tests:
107
- ```bash
108
- DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/
109
- ```
110
-
111
- **Full Test Suite:**
112
- Run everything including performance tests:
113
- ```bash
114
- # Unit + Integration tests
115
- DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/routing.test.js
116
- DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/hybrid-routing-integration.test.js
117
-
118
- # Performance tests
119
- node test/hybrid-routing-performance.test.js
120
- DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node test/performance-tests.js
121
- DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node test/performance-benchmark.js
122
- ```
123
-
124
- ---
125
-
126
- ## Test Organization
127
-
128
- ```
129
- test/
130
- ├── README.md ← This file
131
- ├── routing.test.js ← Unit tests (10 tests)
132
- ├── hybrid-routing-integration.test.js ← Integration tests (13 tests)
133
- ├── hybrid-routing-performance.test.js ← Routing performance benchmarks
134
- ├── performance-tests.js ← System performance tests
135
- └── performance-benchmark.js ← Middleware benchmarks
136
- ```
137
-
138
- ---
139
-
140
- ## Important Notes
141
-
142
- ### Not Test Files
143
- - `src/tests/` - This is **application code**, not tests! It provides test execution functionality as a feature.
144
-
145
- ### Environment Variables
146
- Most tests require Databricks credentials (even though they're not used in actual API calls):
147
- ```bash
148
- export DATABRICKS_API_KEY=test-key
149
- export DATABRICKS_API_BASE=http://test.com
150
- ```
151
-
152
- ### Test Results Summary
153
-
154
- | Test Type | Status | Count |
155
- |-----------|--------|-------|
156
- | Unit Tests | ✅ Passing | 10/10 |
157
- | Integration Tests | ✅ Passing | 13/13 |
158
- | Routing Performance | ✅ Complete | <0.02ms overhead |
159
- | System Performance | ✅ Complete | 100% optimizations |
160
- | Middleware Benchmarks | ✅ Complete | Acceptable overhead |
161
-
162
- ---
163
-
164
- ## CI/CD Integration
165
-
166
- To run in CI/CD pipelines:
167
-
168
- ```bash
169
- #!/bin/bash
170
- set -e
171
-
172
- # Run all tests using npm
173
- echo "Running test suite..."
174
- npm run test:all
175
-
176
- echo "All tests passed!"
177
- ```
178
-
179
- Or for a faster CI pipeline (skip benchmarks):
180
-
181
- ```bash
182
- #!/bin/bash
183
- set -e
184
-
185
- echo "Running tests..."
186
- npm test
187
-
188
- echo "Tests passed!"
189
- ```
190
-
191
- ---
192
-
193
- ## Adding New Tests
194
-
195
- When adding new tests, follow these conventions:
196
-
197
- 1. **Unit tests**: Test individual functions in isolation
198
- - Place in `test/`
199
- - Use `node:test` framework
200
- - Name: `feature-name.test.js`
201
-
202
- 2. **Integration tests**: Test multiple components together
203
- - Place in `test/`
204
- - Use `node:test` framework
205
- - Name: `feature-name-integration.test.js`
206
-
207
- 3. **Performance tests**: Benchmark specific features
208
- - Place in `test/`
209
- - Use custom benchmark utilities
210
- - Name: `feature-name-performance.test.js`
211
-
212
- 4. **Always**: Document in this README!