@node-llm/testing 0.2.1 → 0.3.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.
@@ -1 +1 @@
1
- {"version":3,"file":"Mocker.d.ts","sourceRoot":"","sources":["../src/Mocker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Mocker.d.ts","sourceRoot":"","sources":["../src/Mocker.ts"],"names":[],"mappings":"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"}
package/dist/Mocker.js CHANGED
@@ -1,5 +1,13 @@
1
1
  import { providerRegistry } from "@node-llm/core";
2
- const EXECUTION_METHODS = ["chat", "stream", "paint", "transcribe", "moderate", "embed"];
2
+ const EXECUTION_METHODS = [
3
+ "chat",
4
+ "stream",
5
+ "paint",
6
+ "transcribe",
7
+ "moderate",
8
+ "embed",
9
+ "listModels"
10
+ ];
3
11
  export class Mocker {
4
12
  mocks = [];
5
13
  _history = [];
@@ -267,6 +275,9 @@ export class Mocker {
267
275
  ]
268
276
  };
269
277
  }
278
+ case "listModels": {
279
+ return res || [];
280
+ }
270
281
  default:
271
282
  return res;
272
283
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Scrubber.d.ts","sourceRoot":"","sources":["../src/Scrubber.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,UAInC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC;IAC5C,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,cAAc,CAAC,CAA6B;IACpD,OAAO,CAAC,iBAAiB,CAAW;IACpC,OAAO,CAAC,aAAa,CAAc;gBAEvB,OAAO,GAAE,eAAoB;IAazC;;OAEG;IACI,KAAK,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;IAYpC,OAAO,CAAC,SAAS;CAqElB"}
1
+ {"version":3,"file":"Scrubber.d.ts","sourceRoot":"","sources":["../src/Scrubber.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,UAYnC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC;IAC5C,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,cAAc,CAAC,CAA6B;IACpD,OAAO,CAAC,iBAAiB,CAAW;IACpC,OAAO,CAAC,aAAa,CAAc;gBAEvB,OAAO,GAAE,eAAoB;IAwBzC;;OAEG;IACI,KAAK,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;IAYpC,OAAO,CAAC,SAAS;CAqElB"}
package/dist/Scrubber.js CHANGED
@@ -1,7 +1,14 @@
1
1
  export const DEFAULT_SECRET_PATTERNS = [
2
- /sk-[a-zA-Z0-9]{20,}/g, // OpenAI/Anthropic likely patterns
3
- /x-[a-zA-Z0-9]{20,}/g, // Generic API keys
4
- /[a-zA-Z0-9]{32,}/g // Long hashes/keys
2
+ // API Keys & Auth Tokens
3
+ /sk-[a-zA-Z0-9]{20,}/g, // OpenAI/Anthropic keys
4
+ /x-[a-zA-Z0-9]{20,}/g, // Generic key patterns
5
+ /ey[a-zA-Z0-9-_=]+\.ey[a-zA-Z0-9-_=]+\.?[a-zA-Z0-9-_.+/=]*/g, // JWT tokens
6
+ /\b(AIza[0-9A-Za-z-_]{35})\b/g, // Google API keys
7
+ /\b[0-9a-f]{32,}\b/gi, // Long hex hashes/IDs (often keys)
8
+ // PII (Personal Identifiable Information)
9
+ /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, // Emails
10
+ /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, // IPv4 Addresses
11
+ /\b(access_key|secret_key|aws_access_key_id|aws_secret_access_key):\s*[^\s,{}"]+/gi // AWS-like pairs
5
12
  ];
6
13
  export class Scrubber {
7
14
  customScrubber;
@@ -13,9 +20,20 @@ export class Scrubber {
13
20
  this.sensitiveKeys = new Set([
14
21
  "key",
15
22
  "api_key",
23
+ "apikey",
16
24
  "token",
17
25
  "auth",
18
26
  "authorization",
27
+ "password",
28
+ "secret",
29
+ "cookie",
30
+ "set-cookie",
31
+ "x-api-key",
32
+ "x-auth-token",
33
+ "access_key",
34
+ "secret_key",
35
+ "bearer",
36
+ "credential",
19
37
  ...(options.sensitiveKeys || []).map((k) => k.toLowerCase())
20
38
  ]);
21
39
  }
package/dist/Time.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Time utility for deterministic testing.
3
+ * Provides a clean API for freezing, advancing, and restoring system time.
4
+ * Built on top of Vitest's timer system.
5
+ */
6
+ export declare class Time {
7
+ /**
8
+ * Freezes time at a specific date for the duration of the provided function.
9
+ * Automatically restores real time even if the function throws an error.
10
+ *
11
+ * @example
12
+ * await Time.frozen('2024-05-20', async () => {
13
+ * const now = new Date();
14
+ * expect(now.toISOString()).toContain('2024-05-20');
15
+ * });
16
+ */
17
+ static frozen<T>(date: string | Date | number, fn: () => Promise<T> | T): Promise<T>;
18
+ /**
19
+ * Freezes system time.
20
+ * Use this in beforeEach or for sequential time manipulation.
21
+ * Remember to call Time.restore() in afterEach!
22
+ */
23
+ static freeze(date: string | Date | number): void;
24
+ /**
25
+ * Advances the frozen time by a specific amount of milliseconds.
26
+ */
27
+ static advance(ms: number): void;
28
+ /**
29
+ * Restores system time to reality.
30
+ */
31
+ static restore(): void;
32
+ }
33
+ //# sourceMappingURL=Time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Time.d.ts","sourceRoot":"","sources":["../src/Time.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,qBAAa,IAAI;IACf;;;;;;;;;OASG;WACU,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAS1F;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI;IAMjD;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIhC;;OAEG;IACH,MAAM,CAAC,OAAO,IAAI,IAAI;CAGvB"}
package/dist/Time.js ADDED
@@ -0,0 +1,49 @@
1
+ import { vi } from "vitest";
2
+ /**
3
+ * Time utility for deterministic testing.
4
+ * Provides a clean API for freezing, advancing, and restoring system time.
5
+ * Built on top of Vitest's timer system.
6
+ */
7
+ export class Time {
8
+ /**
9
+ * Freezes time at a specific date for the duration of the provided function.
10
+ * Automatically restores real time even if the function throws an error.
11
+ *
12
+ * @example
13
+ * await Time.frozen('2024-05-20', async () => {
14
+ * const now = new Date();
15
+ * expect(now.toISOString()).toContain('2024-05-20');
16
+ * });
17
+ */
18
+ static async frozen(date, fn) {
19
+ this.freeze(date);
20
+ try {
21
+ return await fn();
22
+ }
23
+ finally {
24
+ this.restore();
25
+ }
26
+ }
27
+ /**
28
+ * Freezes system time.
29
+ * Use this in beforeEach or for sequential time manipulation.
30
+ * Remember to call Time.restore() in afterEach!
31
+ */
32
+ static freeze(date) {
33
+ const targetDate = new Date(date);
34
+ vi.useFakeTimers();
35
+ vi.setSystemTime(targetDate);
36
+ }
37
+ /**
38
+ * Advances the frozen time by a specific amount of milliseconds.
39
+ */
40
+ static advance(ms) {
41
+ vi.advanceTimersByTime(ms);
42
+ }
43
+ /**
44
+ * Restores system time to reality.
45
+ */
46
+ static restore() {
47
+ vi.useRealTimers();
48
+ }
49
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from "./vcr.js";
2
2
  export * from "./Mocker.js";
3
+ export * from "./Time.js";
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC"}
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./vcr.js";
2
2
  export * from "./Mocker.js";
3
+ export * from "./Time.js";
package/dist/vcr.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"vcr.d.ts","sourceRoot":"","sources":["../src/vcr.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vcr.d.ts","sourceRoot":"","sources":["../src/vcr.ts"],"names":[],"mappings":"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"}
package/dist/vcr.js CHANGED
@@ -192,7 +192,15 @@ export class VCR {
192
192
  .replace(/--+/g, "-");
193
193
  }
194
194
  }
195
- const EXECUTION_METHODS = ["chat", "stream", "paint", "transcribe", "moderate", "embed"];
195
+ const EXECUTION_METHODS = [
196
+ "chat",
197
+ "stream",
198
+ "paint",
199
+ "transcribe",
200
+ "moderate",
201
+ "embed",
202
+ "listModels"
203
+ ];
196
204
  export function setupVCR(name, options = {}) {
197
205
  const vcr = new VCR(name, options);
198
206
  providerRegistry.setInterceptor((provider) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-llm/testing",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Deterministic testing for NodeLLM powered AI systems",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -8,7 +8,7 @@
8
8
  "devDependencies": {
9
9
  "vitest": "^1.0.0",
10
10
  "typescript": "^5.0.0",
11
- "@node-llm/core": "1.8.0"
11
+ "@node-llm/core": "1.9.0"
12
12
  },
13
13
  "scripts": {
14
14
  "build": "tsc",
package/src/Mocker.ts CHANGED
@@ -53,7 +53,15 @@ export interface MockerDebugInfo {
53
53
  methods: string[];
54
54
  }
55
55
 
56
- const EXECUTION_METHODS = ["chat", "stream", "paint", "transcribe", "moderate", "embed"];
56
+ const EXECUTION_METHODS = [
57
+ "chat",
58
+ "stream",
59
+ "paint",
60
+ "transcribe",
61
+ "moderate",
62
+ "embed",
63
+ "listModels"
64
+ ];
57
65
 
58
66
  export interface MockerOptions {
59
67
  /**
@@ -354,6 +362,9 @@ export class Mocker {
354
362
  ]
355
363
  } as ModerationResponse;
356
364
  }
365
+ case "listModels": {
366
+ return res || [];
367
+ }
357
368
  default:
358
369
  return res;
359
370
  }
package/src/Scrubber.ts CHANGED
@@ -1,7 +1,15 @@
1
1
  export const DEFAULT_SECRET_PATTERNS = [
2
- /sk-[a-zA-Z0-9]{20,}/g, // OpenAI/Anthropic likely patterns
3
- /x-[a-zA-Z0-9]{20,}/g, // Generic API keys
4
- /[a-zA-Z0-9]{32,}/g // Long hashes/keys
2
+ // API Keys & Auth Tokens
3
+ /sk-[a-zA-Z0-9]{20,}/g, // OpenAI/Anthropic keys
4
+ /x-[a-zA-Z0-9]{20,}/g, // Generic key patterns
5
+ /ey[a-zA-Z0-9-_=]+\.ey[a-zA-Z0-9-_=]+\.?[a-zA-Z0-9-_.+/=]*/g, // JWT tokens
6
+ /\b(AIza[0-9A-Za-z-_]{35})\b/g, // Google API keys
7
+ /\b[0-9a-f]{32,}\b/gi, // Long hex hashes/IDs (often keys)
8
+
9
+ // PII (Personal Identifiable Information)
10
+ /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, // Emails
11
+ /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, // IPv4 Addresses
12
+ /\b(access_key|secret_key|aws_access_key_id|aws_secret_access_key):\s*[^\s,{}"]+/gi // AWS-like pairs
5
13
  ];
6
14
 
7
15
  export interface ScrubberOptions {
@@ -21,9 +29,20 @@ export class Scrubber {
21
29
  this.sensitiveKeys = new Set([
22
30
  "key",
23
31
  "api_key",
32
+ "apikey",
24
33
  "token",
25
34
  "auth",
26
35
  "authorization",
36
+ "password",
37
+ "secret",
38
+ "cookie",
39
+ "set-cookie",
40
+ "x-api-key",
41
+ "x-auth-token",
42
+ "access_key",
43
+ "secret_key",
44
+ "bearer",
45
+ "credential",
27
46
  ...(options.sensitiveKeys || []).map((k) => k.toLowerCase())
28
47
  ]);
29
48
  }
package/src/Time.ts ADDED
@@ -0,0 +1,52 @@
1
+ import { vi } from "vitest";
2
+
3
+ /**
4
+ * Time utility for deterministic testing.
5
+ * Provides a clean API for freezing, advancing, and restoring system time.
6
+ * Built on top of Vitest's timer system.
7
+ */
8
+ export class Time {
9
+ /**
10
+ * Freezes time at a specific date for the duration of the provided function.
11
+ * Automatically restores real time even if the function throws an error.
12
+ *
13
+ * @example
14
+ * await Time.frozen('2024-05-20', async () => {
15
+ * const now = new Date();
16
+ * expect(now.toISOString()).toContain('2024-05-20');
17
+ * });
18
+ */
19
+ static async frozen<T>(date: string | Date | number, fn: () => Promise<T> | T): Promise<T> {
20
+ this.freeze(date);
21
+ try {
22
+ return await fn();
23
+ } finally {
24
+ this.restore();
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Freezes system time.
30
+ * Use this in beforeEach or for sequential time manipulation.
31
+ * Remember to call Time.restore() in afterEach!
32
+ */
33
+ static freeze(date: string | Date | number): void {
34
+ const targetDate = new Date(date);
35
+ vi.useFakeTimers();
36
+ vi.setSystemTime(targetDate);
37
+ }
38
+
39
+ /**
40
+ * Advances the frozen time by a specific amount of milliseconds.
41
+ */
42
+ static advance(ms: number): void {
43
+ vi.advanceTimersByTime(ms);
44
+ }
45
+
46
+ /**
47
+ * Restores system time to reality.
48
+ */
49
+ static restore(): void {
50
+ vi.useRealTimers();
51
+ }
52
+ }
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./vcr.js";
2
2
  export * from "./Mocker.js";
3
+ export * from "./Time.js";
package/src/vcr.ts CHANGED
@@ -259,7 +259,15 @@ export class VCR {
259
259
  }
260
260
  }
261
261
 
262
- const EXECUTION_METHODS = ["chat", "stream", "paint", "transcribe", "moderate", "embed"];
262
+ const EXECUTION_METHODS = [
263
+ "chat",
264
+ "stream",
265
+ "paint",
266
+ "transcribe",
267
+ "moderate",
268
+ "embed",
269
+ "listModels"
270
+ ];
263
271
 
264
272
  export function setupVCR(name: string, options: VCROptions = {}) {
265
273
  const vcr = new VCR(name, options);
@@ -0,0 +1,50 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { Time } from "../../src/Time.js";
3
+
4
+ describe("Testing Utility: Time", () => {
5
+ describe("Time.frozen", () => {
6
+ it("freezes time and restores it automatically", async () => {
7
+ const realStart = new Date().getTime();
8
+ const target = "2024-05-20T10:00:00.000Z";
9
+
10
+ await Time.frozen(target, async () => {
11
+ expect(new Date().toISOString()).toBe(target);
12
+ });
13
+
14
+ // After restoration, time should represent reality again
15
+ const realEnd = new Date().getTime();
16
+ expect(realEnd).toBeGreaterThanOrEqual(realStart);
17
+ expect(new Date().toISOString()).not.toBe(target);
18
+ });
19
+
20
+ it("restores time even on error", async () => {
21
+ const target = "2024-01-01T00:00:00.000Z";
22
+
23
+ try {
24
+ await Time.frozen(target, () => {
25
+ throw new Error("BOOM");
26
+ });
27
+ } catch {
28
+ // Ignored
29
+ }
30
+
31
+ expect(new Date().toISOString()).not.toBe(target);
32
+ });
33
+ });
34
+
35
+ describe("Stateful API (freeze / advance / restore)", () => {
36
+ it("allows manual manipulation of time flow", () => {
37
+ const start = "2024-12-31T23:59:59.000Z";
38
+ Time.freeze(start);
39
+
40
+ expect(new Date().toISOString()).toBe(start);
41
+
42
+ // Advance by 2 seconds
43
+ Time.advance(2000);
44
+ expect(new Date().toISOString()).toBe("2025-01-01T00:00:01.000Z");
45
+
46
+ Time.restore();
47
+ expect(new Date().toISOString()).not.toBe("2025-01-01T00:00:01.000Z");
48
+ });
49
+ });
50
+ });
@@ -0,0 +1,69 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { Scrubber } from "../../src/Scrubber.js";
3
+
4
+ describe("Enhanced Auto-Scrubbing", () => {
5
+ const scrubber = new Scrubber();
6
+
7
+ it("should scrub email addresses from strings", () => {
8
+ const input = "Contact me at user@example.com or support@company.co.uk";
9
+ const result = scrubber.scrub(input);
10
+ expect(result).toBe("Contact me at [REDACTED] or [REDACTED]");
11
+ });
12
+
13
+ it("should scrub JWT tokens", () => {
14
+ const jwt =
15
+ "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
16
+ const result = scrubber.scrub(jwt);
17
+ expect(result).toBe("Bearer [REDACTED]");
18
+ });
19
+
20
+ it("should scrub IPv4 addresses", () => {
21
+ const input = "Server IP is 192.168.1.1 and gateway is 10.0.0.1";
22
+ const result = scrubber.scrub(input);
23
+ expect(result).toBe("Server IP is [REDACTED] and gateway is [REDACTED]");
24
+ });
25
+
26
+ it("should scrub Google API keys", () => {
27
+ const input = "key=AIzaSyA1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q";
28
+ const result = scrubber.scrub(input);
29
+ expect(result).toBe("key=[REDACTED]");
30
+ });
31
+
32
+ it("should scrub sensitive keys in nested objects", () => {
33
+ const input = {
34
+ user: {
35
+ email: "test@example.com",
36
+ password: "secretpassword123",
37
+ settings: {
38
+ apiKey: "sk-1234567890abcdef1234567890"
39
+ }
40
+ },
41
+ status: "ok"
42
+ };
43
+ const result = scrubber.scrub(input) as any;
44
+
45
+ expect(result.user.email).toBe("[REDACTED]");
46
+ expect(result.user.password).toBe("[REDACTED]");
47
+ expect(result.user.settings.apiKey).toBe("[REDACTED]");
48
+ expect(result.status).toBe("ok");
49
+ });
50
+
51
+ it("should scrub custom sensitive keys like 'cookie'", () => {
52
+ const input = {
53
+ headers: {
54
+ Cookie: "sessionid=12345; user=abc",
55
+ "Content-Type": "application/json"
56
+ }
57
+ };
58
+ const result = scrubber.scrub(input) as any;
59
+ expect(result.headers.Cookie).toBe("[REDACTED]");
60
+ expect(result.headers["Content-Type"]).toBe("application/json");
61
+ });
62
+
63
+ it("should scrub AWS-like credential pairs in strings", () => {
64
+ const input =
65
+ "Using access_key: AKIA1234567890EXAMPLE and secret_key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
66
+ const result = scrubber.scrub(input);
67
+ expect(result).toBe("Using [REDACTED] and [REDACTED]");
68
+ });
69
+ });