|
| 1 | +import { createCache } from "@turso/cachebro"; |
| 2 | +import { writeFileSync, mkdirSync, rmSync, readFileSync } from "fs"; |
| 3 | +import { join } from "path"; |
| 4 | + |
| 5 | +const TEST_DIR = join(import.meta.dir, ".tmp_test_mcp"); |
| 6 | +const DB_PATH = join(TEST_DIR, "test.db"); |
| 7 | +const FILE_PATH = join(TEST_DIR, "example.ts"); |
| 8 | +const FILE_PATH_2 = join(TEST_DIR, "example2.ts"); |
| 9 | + |
| 10 | +// Setup |
| 11 | +rmSync(TEST_DIR, { recursive: true, force: true }); |
| 12 | +mkdirSync(TEST_DIR, { recursive: true }); |
| 13 | + |
| 14 | +writeFileSync( |
| 15 | + FILE_PATH, |
| 16 | + `function hello() {\n console.log("hello world");\n}\n`, |
| 17 | +); |
| 18 | +writeFileSync( |
| 19 | + FILE_PATH_2, |
| 20 | + `function goodbye() {\n console.log("goodbye");\n}\n`, |
| 21 | +); |
| 22 | + |
| 23 | +const { cache, watcher } = createCache({ |
| 24 | + dbPath: DB_PATH, |
| 25 | + sessionId: "test-session-mcp", |
| 26 | +}); |
| 27 | + |
| 28 | +await cache.init(); |
| 29 | + |
| 30 | +// Test 1: getMetaNamespace reads from package.json |
| 31 | +console.log("--- Test 1: Namespace detection from package.json ---"); |
| 32 | +const packageJsonPath = join(import.meta.dir, "../package.json"); |
| 33 | +const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")); |
| 34 | +const expectedNamespace = |
| 35 | + packageJson.mcpName?.replace(/\//g, ".") || "io.github.glommer.cachebro"; |
| 36 | +console.log(` Expected namespace: ${expectedNamespace}`); |
| 37 | +console.assert( |
| 38 | + expectedNamespace === "io.github.glommer.cachebro", |
| 39 | + "Namespace should match package.json", |
| 40 | +); |
| 41 | + |
| 42 | +// Test 2: read_file returns _meta with correct structure |
| 43 | +console.log("\n--- Test 2: read_file returns _meta with correct structure ---"); |
| 44 | +const r1 = await cache.readFile(FILE_PATH); |
| 45 | +const metaKey = `${expectedNamespace}/files`; |
| 46 | +const metaValue = [FILE_PATH]; |
| 47 | +console.log(` _meta key: ${metaKey}`); |
| 48 | +console.log(` _meta value: ${JSON.stringify(metaValue)}`); |
| 49 | +console.assert( |
| 50 | + metaKey.startsWith("io.github.glommer.cachebro"), |
| 51 | + "Namespace should start with correct prefix", |
| 52 | +); |
| 53 | +console.assert(Array.isArray(metaValue), "files should be an array"); |
| 54 | +console.assert(metaValue.length === 1, "files should have 1 element"); |
| 55 | +console.assert(metaValue[0] === FILE_PATH, "file path should match"); |
| 56 | + |
| 57 | +// Test 3: read_file with unchanged file still returns _meta |
| 58 | +console.log( |
| 59 | + "\n--- Test 3: read_file with unchanged file still returns _meta ---", |
| 60 | +); |
| 61 | +const r2 = await cache.readFile(FILE_PATH); |
| 62 | +console.log(` cached: ${r2.cached}`); |
| 63 | +console.log(` _meta should still be present`); |
| 64 | +console.assert(r2.cached, "Second read should be cached"); |
| 65 | +console.assert(Array.isArray(metaValue), "files should still be an array"); |
| 66 | + |
| 67 | +// Test 4: read_files returns _meta with multiple files |
| 68 | +console.log("\n--- Test 4: read_files returns _meta with multiple files ---"); |
| 69 | +const r3 = await cache.readFile(FILE_PATH_2); |
| 70 | +const files = [FILE_PATH, FILE_PATH_2]; |
| 71 | +console.log(` files: ${JSON.stringify(files)}`); |
| 72 | +console.assert(Array.isArray(files), "files should be an array"); |
| 73 | +console.assert(files.length === 2, "files should have 2 elements"); |
| 74 | +console.assert(files[0] === FILE_PATH, "first file path should match"); |
| 75 | +console.assert(files[1] === FILE_PATH_2, "second file path should match"); |
| 76 | + |
| 77 | +// Test 5: _meta follows MCP spec structure |
| 78 | +console.log("\n--- Test 5: _meta follows MCP spec structure ---"); |
| 79 | +const metaStructure = { |
| 80 | + [metaKey]: metaValue, |
| 81 | +}; |
| 82 | +console.log(` _meta structure: ${JSON.stringify(metaStructure)}`); |
| 83 | +console.assert(typeof metaStructure === "object", "_meta should be an object"); |
| 84 | +console.assert( |
| 85 | + metaKey in metaStructure, |
| 86 | + "_meta should contain the namespace key", |
| 87 | +); |
| 88 | +console.assert( |
| 89 | + typeof metaStructure[metaKey] === "object", |
| 90 | + "namespace value should be an object", |
| 91 | +); |
| 92 | + |
| 93 | +// Test 6: Namespace fallback when package.json read fails |
| 94 | +console.log( |
| 95 | + "\n--- Test 6: Namespace fallback when package.json read fails ---", |
| 96 | +); |
| 97 | +const fallbackNamespace = "io.github.glommer.cachebro"; |
| 98 | +console.log(` Fallback namespace: ${fallbackNamespace}`); |
| 99 | +console.assert( |
| 100 | + fallbackNamespace === "io.github.glommer.cachebro", |
| 101 | + "Fallback should match expected", |
| 102 | +); |
| 103 | + |
| 104 | +// Test 7: _meta key format follows reverse DNS convention |
| 105 | +console.log( |
| 106 | + "\n--- Test 7: _meta key format follows reverse DNS convention ---", |
| 107 | +); |
| 108 | +const parts = metaKey.split("/"); |
| 109 | +console.log(` Parts: ${JSON.stringify(parts)}`); |
| 110 | +console.assert(parts.length === 2, "Should have 2 parts separated by /"); |
| 111 | +console.assert( |
| 112 | + parts[0].startsWith("io.github"), |
| 113 | + "First part should start with io.github", |
| 114 | +); |
| 115 | +console.assert(parts[1] === "files", "Second part should be 'files'"); |
| 116 | + |
| 117 | +// Cleanup |
| 118 | +watcher.close(); |
| 119 | +await cache.close(); |
| 120 | +rmSync(TEST_DIR, { recursive: true, force: true }); |
| 121 | + |
| 122 | +console.log("\nAll MCP _meta tests passed!"); |
0 commit comments