@jamesaphoenix/tx-test-utils 0.4.2

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 (101) hide show
  1. package/dist/database/index.d.ts +8 -0
  2. package/dist/database/index.d.ts.map +1 -0
  3. package/dist/database/index.js +7 -0
  4. package/dist/database/index.js.map +1 -0
  5. package/dist/database/test-database.d.ts +101 -0
  6. package/dist/database/test-database.d.ts.map +1 -0
  7. package/dist/database/test-database.js +130 -0
  8. package/dist/database/test-database.js.map +1 -0
  9. package/dist/factories/anchor.factory.d.ts +117 -0
  10. package/dist/factories/anchor.factory.d.ts.map +1 -0
  11. package/dist/factories/anchor.factory.js +201 -0
  12. package/dist/factories/anchor.factory.js.map +1 -0
  13. package/dist/factories/candidate.factory.d.ts +151 -0
  14. package/dist/factories/candidate.factory.d.ts.map +1 -0
  15. package/dist/factories/candidate.factory.js +194 -0
  16. package/dist/factories/candidate.factory.js.map +1 -0
  17. package/dist/factories/edge.factory.d.ts +119 -0
  18. package/dist/factories/edge.factory.d.ts.map +1 -0
  19. package/dist/factories/edge.factory.js +191 -0
  20. package/dist/factories/edge.factory.js.map +1 -0
  21. package/dist/factories/factories.test.d.ts +8 -0
  22. package/dist/factories/factories.test.d.ts.map +1 -0
  23. package/dist/factories/factories.test.js +419 -0
  24. package/dist/factories/factories.test.js.map +1 -0
  25. package/dist/factories/index.d.ts +15 -0
  26. package/dist/factories/index.d.ts.map +1 -0
  27. package/dist/factories/index.js +21 -0
  28. package/dist/factories/index.js.map +1 -0
  29. package/dist/factories/learning.factory.d.ts +107 -0
  30. package/dist/factories/learning.factory.d.ts.map +1 -0
  31. package/dist/factories/learning.factory.js +150 -0
  32. package/dist/factories/learning.factory.js.map +1 -0
  33. package/dist/factories/task.factory.d.ts +106 -0
  34. package/dist/factories/task.factory.d.ts.map +1 -0
  35. package/dist/factories/task.factory.js +151 -0
  36. package/dist/factories/task.factory.js.map +1 -0
  37. package/dist/fixtures/index.d.ts +36 -0
  38. package/dist/fixtures/index.d.ts.map +1 -0
  39. package/dist/fixtures/index.js +47 -0
  40. package/dist/fixtures/index.js.map +1 -0
  41. package/dist/helpers/effect.d.ts +186 -0
  42. package/dist/helpers/effect.d.ts.map +1 -0
  43. package/dist/helpers/effect.js +298 -0
  44. package/dist/helpers/effect.js.map +1 -0
  45. package/dist/helpers/effect.test.d.ts +7 -0
  46. package/dist/helpers/effect.test.d.ts.map +1 -0
  47. package/dist/helpers/effect.test.js +271 -0
  48. package/dist/helpers/effect.test.js.map +1 -0
  49. package/dist/helpers/index.d.ts +7 -0
  50. package/dist/helpers/index.d.ts.map +1 -0
  51. package/dist/helpers/index.js +11 -0
  52. package/dist/helpers/index.js.map +1 -0
  53. package/dist/index.d.ts +26 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +52 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/llm-cache/cache.d.ts +152 -0
  58. package/dist/llm-cache/cache.d.ts.map +1 -0
  59. package/dist/llm-cache/cache.js +199 -0
  60. package/dist/llm-cache/cache.js.map +1 -0
  61. package/dist/llm-cache/cache.test.d.ts +7 -0
  62. package/dist/llm-cache/cache.test.d.ts.map +1 -0
  63. package/dist/llm-cache/cache.test.js +310 -0
  64. package/dist/llm-cache/cache.test.js.map +1 -0
  65. package/dist/llm-cache/cli.d.ts +113 -0
  66. package/dist/llm-cache/cli.d.ts.map +1 -0
  67. package/dist/llm-cache/cli.js +248 -0
  68. package/dist/llm-cache/cli.js.map +1 -0
  69. package/dist/llm-cache/index.d.ts +31 -0
  70. package/dist/llm-cache/index.d.ts.map +1 -0
  71. package/dist/llm-cache/index.js +31 -0
  72. package/dist/llm-cache/index.js.map +1 -0
  73. package/dist/mocks/anthropic.mock.d.ts +173 -0
  74. package/dist/mocks/anthropic.mock.d.ts.map +1 -0
  75. package/dist/mocks/anthropic.mock.js +125 -0
  76. package/dist/mocks/anthropic.mock.js.map +1 -0
  77. package/dist/mocks/ast-grep.mock.d.ts +216 -0
  78. package/dist/mocks/ast-grep.mock.d.ts.map +1 -0
  79. package/dist/mocks/ast-grep.mock.js +164 -0
  80. package/dist/mocks/ast-grep.mock.js.map +1 -0
  81. package/dist/mocks/file-system.mock.d.ts +181 -0
  82. package/dist/mocks/file-system.mock.d.ts.map +1 -0
  83. package/dist/mocks/file-system.mock.js +280 -0
  84. package/dist/mocks/file-system.mock.js.map +1 -0
  85. package/dist/mocks/index.d.ts +10 -0
  86. package/dist/mocks/index.d.ts.map +1 -0
  87. package/dist/mocks/index.js +16 -0
  88. package/dist/mocks/index.js.map +1 -0
  89. package/dist/mocks/mocks.test.d.ts +10 -0
  90. package/dist/mocks/mocks.test.d.ts.map +1 -0
  91. package/dist/mocks/mocks.test.js +961 -0
  92. package/dist/mocks/mocks.test.js.map +1 -0
  93. package/dist/mocks/openai.mock.d.ts +205 -0
  94. package/dist/mocks/openai.mock.d.ts.map +1 -0
  95. package/dist/mocks/openai.mock.js +178 -0
  96. package/dist/mocks/openai.mock.js.map +1 -0
  97. package/dist/setup/index.d.ts +7 -0
  98. package/dist/setup/index.d.ts.map +1 -0
  99. package/dist/setup/index.js +9 -0
  100. package/dist/setup/index.js.map +1 -0
  101. package/package.json +80 -0
@@ -0,0 +1,199 @@
1
+ /**
2
+ * LLM response caching with SHA256 keys.
3
+ *
4
+ * Caches expensive LLM API calls by hashing inputs with SHA256.
5
+ * Supports version-based cache invalidation and various bypass modes.
6
+ *
7
+ * @module @tx/test-utils/llm-cache/cache
8
+ */
9
+ import * as crypto from "crypto";
10
+ import * as fs from "fs/promises";
11
+ import * as path from "path";
12
+ /**
13
+ * Get the default cache directory.
14
+ * Checks TX_LLM_CACHE_DIR environment variable first, then falls back to default.
15
+ */
16
+ const getDefaultCacheDir = () => {
17
+ return process.env.TX_LLM_CACHE_DIR || "test/fixtures/llm-cache";
18
+ };
19
+ let config = {
20
+ cacheDir: getDefaultCacheDir(),
21
+ logging: !process.env.CI
22
+ };
23
+ /**
24
+ * Configure LLM cache settings.
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * configureLLMCache({
29
+ * cacheDir: 'custom/cache/path',
30
+ * logging: false
31
+ * })
32
+ * ```
33
+ */
34
+ export const configureLLMCache = (options) => {
35
+ config = { ...config, ...options };
36
+ };
37
+ /**
38
+ * Get current cache configuration.
39
+ */
40
+ export const getCacheConfig = () => config;
41
+ /**
42
+ * Reset cache configuration to defaults.
43
+ */
44
+ export const resetCacheConfig = () => {
45
+ config = {
46
+ cacheDir: getDefaultCacheDir(),
47
+ logging: !process.env.CI
48
+ };
49
+ };
50
+ // =============================================================================
51
+ // Core Functions
52
+ // =============================================================================
53
+ /**
54
+ * Compute SHA256 hash of input string for cache key.
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const hash = hashInput("What is the capital of France?")
59
+ * // -> "a1b2c3d4e5f6..." (64 hex chars)
60
+ * ```
61
+ */
62
+ export const hashInput = (input) => {
63
+ return crypto.createHash("sha256").update(input).digest("hex");
64
+ };
65
+ /**
66
+ * Internal logging helper.
67
+ */
68
+ const log = (message) => {
69
+ if (config.logging) {
70
+ console.log(message);
71
+ }
72
+ };
73
+ /**
74
+ * Get cache file path for a given hash.
75
+ */
76
+ const getCacheFilePath = (hash) => {
77
+ return path.join(config.cacheDir, `${hash}.json`);
78
+ };
79
+ /**
80
+ * Get cached LLM response or execute and cache.
81
+ *
82
+ * Features:
83
+ * - TX_NO_LLM_CACHE=1 env var bypasses cache entirely
84
+ * - forceRefresh option forces fresh call even if cached
85
+ * - version option enables cache invalidation on version mismatch
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * const result = await cachedLLMCall(
90
+ * "What is the capital of France?",
91
+ * "claude-sonnet-4",
92
+ * async () => {
93
+ * const response = await anthropic.messages.create({...})
94
+ * return response.content[0].text
95
+ * },
96
+ * { version: 2 }
97
+ * )
98
+ * ```
99
+ */
100
+ export const cachedLLMCall = async (input, model, call, options = {}) => {
101
+ // Bypass cache if env var is set or forceRefresh is true
102
+ if (process.env.TX_NO_LLM_CACHE === "1" || options.forceRefresh) {
103
+ log(`[LLM Cache BYPASS] Calling ${model} directly`);
104
+ return call();
105
+ }
106
+ const inputHash = hashInput(input);
107
+ const cacheFile = getCacheFilePath(inputHash);
108
+ // Try to read from cache
109
+ try {
110
+ const content = await fs.readFile(cacheFile, "utf-8");
111
+ const cached = JSON.parse(content);
112
+ // Version mismatch triggers cache miss
113
+ if (options.version !== undefined && cached.version !== options.version) {
114
+ log(`[LLM Cache VERSION MISMATCH] ${inputHash.slice(0, 12)}... (cached: v${cached.version}, requested: v${options.version})`);
115
+ throw new Error("Version mismatch");
116
+ }
117
+ log(`[LLM Cache HIT] ${inputHash.slice(0, 12)}...`);
118
+ return cached.response;
119
+ }
120
+ catch {
121
+ // Cache miss - file doesn't exist, is corrupted, or version mismatch
122
+ }
123
+ // Execute the call
124
+ log(`[LLM Cache MISS] ${inputHash.slice(0, 12)}... calling ${model}`);
125
+ const response = await call();
126
+ // Store in cache
127
+ try {
128
+ await fs.mkdir(config.cacheDir, { recursive: true });
129
+ const entry = {
130
+ inputHash,
131
+ input: input.slice(0, 1000), // Truncate for readability
132
+ response,
133
+ model,
134
+ cachedAt: new Date().toISOString(),
135
+ version: options.version ?? 1
136
+ };
137
+ await fs.writeFile(cacheFile, JSON.stringify(entry, null, 2), "utf-8");
138
+ log(`[LLM Cache STORED] ${inputHash.slice(0, 12)}...`);
139
+ }
140
+ catch (error) {
141
+ // Don't fail on cache write errors - just log and continue
142
+ log(`[LLM Cache WRITE ERROR] ${error instanceof Error ? error.message : "Unknown error"}`);
143
+ }
144
+ return response;
145
+ };
146
+ // =============================================================================
147
+ // Higher-Order Function Wrapper
148
+ // =============================================================================
149
+ /**
150
+ * Wrap an async function to use LLM caching.
151
+ *
152
+ * Creates a cached version of any async function. The input is serialized
153
+ * and hashed to create a cache key.
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * // Wrap an extraction function
158
+ * const cachedExtract = withLLMCache(
159
+ * async (transcript: string) => {
160
+ * const response = await anthropic.messages.create({
161
+ * model: "claude-sonnet-4-20250514",
162
+ * messages: [{ role: "user", content: extractPrompt(transcript) }]
163
+ * })
164
+ * return JSON.parse(response.content[0].text)
165
+ * },
166
+ * { model: "claude-sonnet-4", version: 1 }
167
+ * )
168
+ *
169
+ * // Use it - results are cached
170
+ * const result1 = await cachedExtract("some transcript")
171
+ * const result2 = await cachedExtract("some transcript") // cache hit!
172
+ * ```
173
+ *
174
+ * @example
175
+ * ```typescript
176
+ * // With custom serializer for complex input
177
+ * interface ComplexInput {
178
+ * transcript: string
179
+ * metadata: { source: string }
180
+ * }
181
+ *
182
+ * const cachedProcess = withLLMCache(
183
+ * async (input: ComplexInput) => processInput(input),
184
+ * {
185
+ * model: "claude-sonnet-4",
186
+ * serialize: (input) => `${input.metadata.source}::${input.transcript}`,
187
+ * version: 2
188
+ * }
189
+ * )
190
+ * ```
191
+ */
192
+ export const withLLMCache = (fn, options) => {
193
+ const serialize = options.serialize ?? JSON.stringify;
194
+ return async (input) => {
195
+ const serialized = serialize(input);
196
+ return cachedLLMCall(serialized, options.model, () => fn(input), { version: options.version });
197
+ };
198
+ };
199
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/llm-cache/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Integration tests for LLM response caching.
3
+ *
4
+ * @module @tx/test-utils/llm-cache/cache.test
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=cache.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.test.d.ts","sourceRoot":"","sources":["../../src/llm-cache/cache.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Integration tests for LLM response caching.
3
+ *
4
+ * @module @tx/test-utils/llm-cache/cache.test
5
+ */
6
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
7
+ import * as fs from "fs/promises";
8
+ import * as path from "path";
9
+ import * as os from "os";
10
+ import { hashInput, cachedLLMCall, withLLMCache, configureLLMCache, resetCacheConfig, getCacheStats, clearCache, formatCacheStats, getCacheEntry, listCacheEntries } from "./index.js";
11
+ // =============================================================================
12
+ // Test Setup
13
+ // =============================================================================
14
+ describe("LLM Cache", () => {
15
+ let tempDir;
16
+ beforeEach(async () => {
17
+ // Create isolated temp directory for each test
18
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "tx-llm-cache-test-"));
19
+ configureLLMCache({ cacheDir: tempDir, logging: false });
20
+ });
21
+ afterEach(async () => {
22
+ // Clean up temp directory
23
+ resetCacheConfig();
24
+ try {
25
+ await fs.rm(tempDir, { recursive: true, force: true });
26
+ }
27
+ catch {
28
+ // Ignore cleanup errors
29
+ }
30
+ });
31
+ // ===========================================================================
32
+ // hashInput Tests
33
+ // ===========================================================================
34
+ describe("hashInput", () => {
35
+ it("should return deterministic SHA256 hash", () => {
36
+ const input = "What is the capital of France?";
37
+ const hash1 = hashInput(input);
38
+ const hash2 = hashInput(input);
39
+ expect(hash1).toBe(hash2);
40
+ expect(hash1).toHaveLength(64); // SHA256 hex = 64 chars
41
+ expect(hash1).toMatch(/^[a-f0-9]{64}$/);
42
+ });
43
+ it("should produce different hashes for different inputs", () => {
44
+ const hash1 = hashInput("input one");
45
+ const hash2 = hashInput("input two");
46
+ expect(hash1).not.toBe(hash2);
47
+ });
48
+ it("should handle empty string", () => {
49
+ const hash = hashInput("");
50
+ expect(hash).toHaveLength(64);
51
+ // SHA256 of empty string is a known value
52
+ expect(hash).toBe("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
53
+ });
54
+ it("should handle unicode input", () => {
55
+ const hash = hashInput("こんにちは世界 🌍");
56
+ expect(hash).toHaveLength(64);
57
+ expect(hashInput("こんにちは世界 🌍")).toBe(hash);
58
+ });
59
+ it("should handle very long input", () => {
60
+ const longInput = "a".repeat(100000);
61
+ const hash = hashInput(longInput);
62
+ expect(hash).toHaveLength(64);
63
+ });
64
+ });
65
+ // ===========================================================================
66
+ // cachedLLMCall Tests
67
+ // ===========================================================================
68
+ describe("cachedLLMCall", () => {
69
+ it("should call function on cache miss and store result", async () => {
70
+ const mockCall = vi.fn().mockResolvedValue({ answer: "Paris" });
71
+ const result = await cachedLLMCall("What is the capital of France?", "claude-sonnet-4", mockCall);
72
+ expect(result).toEqual({ answer: "Paris" });
73
+ expect(mockCall).toHaveBeenCalledTimes(1);
74
+ // Verify cache file was created
75
+ const files = await fs.readdir(tempDir);
76
+ expect(files.length).toBe(1);
77
+ expect(files[0]).toMatch(/^[a-f0-9]{64}\.json$/);
78
+ });
79
+ it("should return cached value on cache hit without calling function", async () => {
80
+ const mockCall = vi.fn().mockResolvedValue({ answer: "Paris" });
81
+ const input = "What is the capital of France?";
82
+ // First call - cache miss
83
+ await cachedLLMCall(input, "claude-sonnet-4", mockCall);
84
+ expect(mockCall).toHaveBeenCalledTimes(1);
85
+ // Second call - cache hit
86
+ const result = await cachedLLMCall(input, "claude-sonnet-4", mockCall);
87
+ expect(result).toEqual({ answer: "Paris" });
88
+ expect(mockCall).toHaveBeenCalledTimes(1); // Not called again
89
+ });
90
+ it("should bypass cache when TX_NO_LLM_CACHE=1", async () => {
91
+ const originalEnv = process.env.TX_NO_LLM_CACHE;
92
+ process.env.TX_NO_LLM_CACHE = "1";
93
+ try {
94
+ const mockCall = vi.fn().mockResolvedValue({ answer: "Paris" });
95
+ const input = "What is the capital of France?";
96
+ // First call
97
+ await cachedLLMCall(input, "claude-sonnet-4", mockCall);
98
+ // Second call - should still call function
99
+ await cachedLLMCall(input, "claude-sonnet-4", mockCall);
100
+ expect(mockCall).toHaveBeenCalledTimes(2);
101
+ // Cache should not have been written
102
+ const files = await fs.readdir(tempDir);
103
+ expect(files.length).toBe(0);
104
+ }
105
+ finally {
106
+ if (originalEnv === undefined) {
107
+ delete process.env.TX_NO_LLM_CACHE;
108
+ }
109
+ else {
110
+ process.env.TX_NO_LLM_CACHE = originalEnv;
111
+ }
112
+ }
113
+ });
114
+ it("should bypass cache when forceRefresh=true", async () => {
115
+ const mockCall = vi.fn()
116
+ .mockResolvedValueOnce({ answer: "Paris" })
117
+ .mockResolvedValueOnce({ answer: "Paris (updated)" });
118
+ const input = "What is the capital of France?";
119
+ // First call - cache miss
120
+ await cachedLLMCall(input, "claude-sonnet-4", mockCall);
121
+ // Second call with forceRefresh - should call function
122
+ const result = await cachedLLMCall(input, "claude-sonnet-4", mockCall, { forceRefresh: true });
123
+ expect(result).toEqual({ answer: "Paris (updated)" });
124
+ expect(mockCall).toHaveBeenCalledTimes(2);
125
+ });
126
+ it("should trigger cache miss on version mismatch", async () => {
127
+ const mockCall = vi.fn()
128
+ .mockResolvedValueOnce({ answer: "Paris v1" })
129
+ .mockResolvedValueOnce({ answer: "Paris v2" });
130
+ const input = "What is the capital of France?";
131
+ // First call with version 1
132
+ await cachedLLMCall(input, "claude-sonnet-4", mockCall, { version: 1 });
133
+ // Second call with version 2 - should be cache miss
134
+ const result = await cachedLLMCall(input, "claude-sonnet-4", mockCall, { version: 2 });
135
+ expect(result).toEqual({ answer: "Paris v2" });
136
+ expect(mockCall).toHaveBeenCalledTimes(2);
137
+ });
138
+ it("should store correct cache entry format", async () => {
139
+ const mockCall = vi.fn().mockResolvedValue({ answer: "Paris" });
140
+ const input = "What is the capital of France?";
141
+ await cachedLLMCall(input, "claude-sonnet-4", mockCall, { version: 3 });
142
+ const hash = hashInput(input);
143
+ const cacheFile = path.join(tempDir, `${hash}.json`);
144
+ const content = JSON.parse(await fs.readFile(cacheFile, "utf-8"));
145
+ expect(content.inputHash).toBe(hash);
146
+ expect(content.input).toBe(input);
147
+ expect(content.response).toEqual({ answer: "Paris" });
148
+ expect(content.model).toBe("claude-sonnet-4");
149
+ expect(content.version).toBe(3);
150
+ expect(content.cachedAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
151
+ });
152
+ it("should truncate long input in cache entry", async () => {
153
+ const mockCall = vi.fn().mockResolvedValue({ answer: "42" });
154
+ const longInput = "a".repeat(2000);
155
+ await cachedLLMCall(longInput, "claude-sonnet-4", mockCall);
156
+ const hash = hashInput(longInput);
157
+ const cacheFile = path.join(tempDir, `${hash}.json`);
158
+ const content = JSON.parse(await fs.readFile(cacheFile, "utf-8"));
159
+ expect(content.input.length).toBe(1000); // Truncated
160
+ expect(content.inputHash).toBe(hash); // Full hash preserved
161
+ });
162
+ });
163
+ // ===========================================================================
164
+ // withLLMCache Tests
165
+ // ===========================================================================
166
+ describe("withLLMCache", () => {
167
+ it("should wrap function with caching", async () => {
168
+ const mockFn = vi.fn().mockResolvedValue("result");
169
+ const cachedFn = withLLMCache(mockFn, { model: "claude-sonnet-4" });
170
+ const result1 = await cachedFn("input");
171
+ const result2 = await cachedFn("input");
172
+ expect(result1).toBe("result");
173
+ expect(result2).toBe("result");
174
+ expect(mockFn).toHaveBeenCalledTimes(1);
175
+ });
176
+ it("should use custom serializer", async () => {
177
+ const mockFn = vi.fn().mockResolvedValue("result");
178
+ const cachedFn = withLLMCache(mockFn, {
179
+ model: "claude-sonnet-4",
180
+ // Only use query for cache key (ignore context)
181
+ serialize: (input) => input.query
182
+ });
183
+ // Same query, different context - should hit cache
184
+ await cachedFn({ query: "test", context: ["a"] });
185
+ await cachedFn({ query: "test", context: ["b", "c"] });
186
+ expect(mockFn).toHaveBeenCalledTimes(1);
187
+ });
188
+ it("should pass version to cachedLLMCall", async () => {
189
+ const mockFn = vi.fn()
190
+ .mockResolvedValueOnce("v1 result")
191
+ .mockResolvedValueOnce("v2 result");
192
+ const cachedFnV1 = withLLMCache(mockFn, { model: "claude-sonnet-4", version: 1 });
193
+ const cachedFnV2 = withLLMCache(mockFn, { model: "claude-sonnet-4", version: 2 });
194
+ await cachedFnV1("input");
195
+ const result = await cachedFnV2("input");
196
+ expect(result).toBe("v2 result");
197
+ expect(mockFn).toHaveBeenCalledTimes(2);
198
+ });
199
+ });
200
+ // ===========================================================================
201
+ // CLI Utilities Tests
202
+ // ===========================================================================
203
+ describe("getCacheStats", () => {
204
+ it("should return empty stats for empty cache", async () => {
205
+ const stats = await getCacheStats();
206
+ expect(stats.count).toBe(0);
207
+ expect(stats.totalBytes).toBe(0);
208
+ expect(stats.oldestDate).toBeNull();
209
+ expect(stats.newestDate).toBeNull();
210
+ expect(stats.byModel).toEqual({});
211
+ expect(stats.byVersion).toEqual({});
212
+ });
213
+ it("should return correct stats for populated cache", async () => {
214
+ // Create some cache entries
215
+ await cachedLLMCall("input1", "claude-sonnet-4", () => Promise.resolve("r1"), { version: 1 });
216
+ await cachedLLMCall("input2", "claude-sonnet-4", () => Promise.resolve("r2"), { version: 1 });
217
+ await cachedLLMCall("input3", "claude-haiku", () => Promise.resolve("r3"), { version: 2 });
218
+ const stats = await getCacheStats();
219
+ expect(stats.count).toBe(3);
220
+ expect(stats.totalBytes).toBeGreaterThan(0);
221
+ expect(stats.oldestDate).toBeInstanceOf(Date);
222
+ expect(stats.newestDate).toBeInstanceOf(Date);
223
+ expect(stats.byModel).toEqual({
224
+ "claude-sonnet-4": 2,
225
+ "claude-haiku": 1
226
+ });
227
+ expect(stats.byVersion).toEqual({
228
+ 1: 2,
229
+ 2: 1
230
+ });
231
+ });
232
+ });
233
+ describe("clearCache", () => {
234
+ it("should clear all entries with all=true", async () => {
235
+ await cachedLLMCall("input1", "claude-sonnet-4", () => Promise.resolve("r1"));
236
+ await cachedLLMCall("input2", "claude-sonnet-4", () => Promise.resolve("r2"));
237
+ const deleted = await clearCache({ all: true });
238
+ expect(deleted).toBe(-1); // Unknown count for all
239
+ const files = await fs.readdir(tempDir);
240
+ expect(files.length).toBe(0);
241
+ });
242
+ it("should clear entries by model", async () => {
243
+ await cachedLLMCall("input1", "claude-sonnet-4", () => Promise.resolve("r1"));
244
+ await cachedLLMCall("input2", "claude-haiku", () => Promise.resolve("r2"));
245
+ const deleted = await clearCache({ model: "claude-sonnet-4" });
246
+ expect(deleted).toBe(1);
247
+ const stats = await getCacheStats();
248
+ expect(stats.count).toBe(1);
249
+ expect(stats.byModel).toEqual({ "claude-haiku": 1 });
250
+ });
251
+ it("should clear entries by version", async () => {
252
+ await cachedLLMCall("input1", "claude-sonnet-4", () => Promise.resolve("r1"), { version: 1 });
253
+ await cachedLLMCall("input2", "claude-sonnet-4", () => Promise.resolve("r2"), { version: 2 });
254
+ const deleted = await clearCache({ version: 1 });
255
+ expect(deleted).toBe(1);
256
+ const stats = await getCacheStats();
257
+ expect(stats.byVersion).toEqual({ 2: 1 });
258
+ });
259
+ it("should clear entries older than date", async () => {
260
+ await cachedLLMCall("input1", "claude-sonnet-4", () => Promise.resolve("r1"));
261
+ // Clear entries older than 1 second in the future (all entries)
262
+ const futureDate = new Date(Date.now() + 1000);
263
+ const deleted = await clearCache({ olderThan: futureDate });
264
+ expect(deleted).toBe(1);
265
+ });
266
+ });
267
+ describe("formatCacheStats", () => {
268
+ it("should format stats as readable string", async () => {
269
+ await cachedLLMCall("input1", "claude-sonnet-4", () => Promise.resolve("r1"), { version: 1 });
270
+ await cachedLLMCall("input2", "claude-haiku", () => Promise.resolve("r2"), { version: 2 });
271
+ const stats = await getCacheStats();
272
+ const formatted = formatCacheStats(stats);
273
+ expect(formatted).toContain("LLM Cache Statistics:");
274
+ expect(formatted).toContain("Entries: 2");
275
+ expect(formatted).toContain("By model:");
276
+ expect(formatted).toContain("claude-sonnet-4: 1");
277
+ expect(formatted).toContain("claude-haiku: 1");
278
+ });
279
+ });
280
+ describe("getCacheEntry", () => {
281
+ it("should return entry by hash", async () => {
282
+ const input = "test input";
283
+ await cachedLLMCall(input, "claude-sonnet-4", () => Promise.resolve("result"));
284
+ const hash = hashInput(input);
285
+ const entry = await getCacheEntry(hash);
286
+ expect(entry).not.toBeNull();
287
+ expect(entry?.response).toBe("result");
288
+ expect(entry?.model).toBe("claude-sonnet-4");
289
+ });
290
+ it("should return null for non-existent hash", async () => {
291
+ const entry = await getCacheEntry("nonexistent");
292
+ expect(entry).toBeNull();
293
+ });
294
+ });
295
+ describe("listCacheEntries", () => {
296
+ it("should list all cache entry hashes", async () => {
297
+ await cachedLLMCall("input1", "claude-sonnet-4", () => Promise.resolve("r1"));
298
+ await cachedLLMCall("input2", "claude-sonnet-4", () => Promise.resolve("r2"));
299
+ const entries = await listCacheEntries();
300
+ expect(entries.length).toBe(2);
301
+ expect(entries[0]).toHaveLength(64);
302
+ expect(entries[1]).toHaveLength(64);
303
+ });
304
+ it("should return empty array for empty cache", async () => {
305
+ const entries = await listCacheEntries();
306
+ expect(entries).toEqual([]);
307
+ });
308
+ });
309
+ });
310
+ //# sourceMappingURL=cache.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.test.js","sourceRoot":"","sources":["../../src/llm-cache/cache.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * CLI utilities for LLM cache management.
3
+ *
4
+ * Provides functions to inspect, clear, and manage the LLM response cache.
5
+ *
6
+ * @module @tx/test-utils/llm-cache/cli
7
+ */
8
+ import { CacheEntry } from "./cache.js";
9
+ /**
10
+ * Statistics about the LLM cache.
11
+ */
12
+ export interface CacheStats {
13
+ /** Total number of cache entries */
14
+ count: number;
15
+ /** Total size in bytes */
16
+ totalBytes: number;
17
+ /** Oldest cache entry date (null if empty) */
18
+ oldestDate: Date | null;
19
+ /** Newest cache entry date (null if empty) */
20
+ newestDate: Date | null;
21
+ /** Count of entries by model */
22
+ byModel: Record<string, number>;
23
+ /** Count of entries by version */
24
+ byVersion: Record<number, number>;
25
+ }
26
+ /**
27
+ * Options for clearing cache.
28
+ */
29
+ export interface ClearCacheOptions {
30
+ /** Delete entries older than this date */
31
+ olderThan?: Date;
32
+ /** Delete entries for specific model */
33
+ model?: string;
34
+ /** Delete entries with specific version */
35
+ version?: number;
36
+ /** Delete all entries */
37
+ all?: boolean;
38
+ }
39
+ /**
40
+ * Get statistics about the LLM cache.
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * const stats = await getCacheStats()
45
+ * console.log(`Cache has ${stats.count} entries using ${stats.totalBytes} bytes`)
46
+ * console.log(`Models: ${Object.keys(stats.byModel).join(', ')}`)
47
+ * ```
48
+ */
49
+ export declare const getCacheStats: () => Promise<CacheStats>;
50
+ /**
51
+ * Clear cache entries based on options.
52
+ *
53
+ * @returns Number of entries deleted (-1 if unknown when using `all`)
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * // Clear all cache
58
+ * await clearCache({ all: true })
59
+ *
60
+ * // Clear entries older than 30 days
61
+ * const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
62
+ * const deleted = await clearCache({ olderThan: thirtyDaysAgo })
63
+ *
64
+ * // Clear entries for specific model
65
+ * await clearCache({ model: 'claude-haiku' })
66
+ *
67
+ * // Clear entries with specific version (useful after schema changes)
68
+ * await clearCache({ version: 1 })
69
+ * ```
70
+ */
71
+ export declare const clearCache: (options?: ClearCacheOptions) => Promise<number>;
72
+ /**
73
+ * Format cache statistics for display.
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * const stats = await getCacheStats()
78
+ * console.log(formatCacheStats(stats))
79
+ * // Output:
80
+ * // LLM Cache Statistics:
81
+ * // Entries: 142
82
+ * // Size: 3.2 MB
83
+ * // Date range: 2024-01-15 to 2024-02-01
84
+ * // By model:
85
+ * // claude-sonnet-4: 98
86
+ * // claude-haiku: 44
87
+ * ```
88
+ */
89
+ export declare const formatCacheStats: (stats: CacheStats) => string;
90
+ /**
91
+ * Get a specific cache entry by hash.
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * const entry = await getCacheEntry('a1b2c3d4...')
96
+ * if (entry) {
97
+ * console.log(`Cached at: ${entry.cachedAt}`)
98
+ * console.log(`Model: ${entry.model}`)
99
+ * }
100
+ * ```
101
+ */
102
+ export declare const getCacheEntry: <T = unknown>(hash: string) => Promise<CacheEntry<T> | null>;
103
+ /**
104
+ * List all cache entry hashes.
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * const hashes = await listCacheEntries()
109
+ * console.log(`Found ${hashes.length} cached responses`)
110
+ * ```
111
+ */
112
+ export declare const listCacheEntries: () => Promise<string[]>;
113
+ //# sourceMappingURL=cli.d.ts.map