@node-llm/testing 0.1.0 → 0.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/CHANGELOG.md +22 -1
- package/LICENSE +21 -0
- package/README.md +42 -0
- package/dist/Mocker.d.ts +18 -0
- package/dist/Mocker.d.ts.map +1 -1
- package/dist/Mocker.js +39 -0
- package/dist/Scrubber.d.ts.map +1 -1
- package/dist/Scrubber.js +25 -0
- package/dist/Serializer.d.ts +16 -0
- package/dist/Serializer.d.ts.map +1 -0
- package/dist/Serializer.js +107 -0
- package/dist/vcr.d.ts +8 -6
- package/dist/vcr.d.ts.map +1 -1
- package/dist/vcr.js +11 -11
- package/package.json +8 -8
- package/src/Mocker.ts +55 -0
- package/src/Scrubber.ts +30 -0
- package/src/Serializer.ts +113 -0
- package/src/vcr.ts +37 -27
- package/test/cassettes/handles-rich-types.json +38 -0
- package/test/unit/__snapshots__/mocker_snapshots.test.ts.snap +12 -0
- package/test/unit/dx.test.ts +1 -1
- package/test/unit/mocker_history.test.ts +115 -0
- package/test/unit/mocker_snapshots.test.ts +48 -0
- package/test/unit/scoping.test.ts +8 -4
- package/test/unit/scrubbing.test.ts +9 -2
- package/test/unit/serializer.test.ts +81 -0
- package/test/unit/vcr-global-config.test.ts +15 -3
- package/test/unit/vcr-mismatch.test.ts +2 -1
- package/test/unit/vcr_advanced_types.test.ts +72 -0
|
@@ -33,7 +33,11 @@ describe("VCR: Global Configuration", () => {
|
|
|
33
33
|
const CASSETTE_NAME = "global-config-keys";
|
|
34
34
|
const CASSETTE_PATH = path.join(CASSETTE_DIR, `${CASSETTE_NAME}.json`);
|
|
35
35
|
|
|
36
|
-
const vcr = setupVCR(CASSETTE_NAME, {
|
|
36
|
+
const vcr = setupVCR(CASSETTE_NAME, {
|
|
37
|
+
mode: "record",
|
|
38
|
+
cassettesDir: CASSETTE_DIR,
|
|
39
|
+
_allowRecordingInCI: true
|
|
40
|
+
});
|
|
37
41
|
const llm = NodeLLM.withProvider("mock-provider");
|
|
38
42
|
|
|
39
43
|
await llm.chat().ask("regular question");
|
|
@@ -53,7 +57,11 @@ describe("VCR: Global Configuration", () => {
|
|
|
53
57
|
const CASSETTE_NAME = "global-config-patterns";
|
|
54
58
|
const CASSETTE_PATH = path.join(CASSETTE_DIR, `${CASSETTE_NAME}.json`);
|
|
55
59
|
|
|
56
|
-
const vcr = setupVCR(CASSETTE_NAME, {
|
|
60
|
+
const vcr = setupVCR(CASSETTE_NAME, {
|
|
61
|
+
mode: "record",
|
|
62
|
+
cassettesDir: CASSETTE_DIR,
|
|
63
|
+
_allowRecordingInCI: true
|
|
64
|
+
});
|
|
57
65
|
const llm = NodeLLM.withProvider("mock-provider");
|
|
58
66
|
|
|
59
67
|
await llm.chat().ask("Status of custom-secret-omega");
|
|
@@ -74,7 +82,11 @@ describe("VCR: Global Configuration", () => {
|
|
|
74
82
|
const CASSETTE_NAME = "global-config-reset";
|
|
75
83
|
const CASSETTE_PATH = path.join(CASSETTE_DIR, `${CASSETTE_NAME}.json`);
|
|
76
84
|
|
|
77
|
-
const vcr = setupVCR(CASSETTE_NAME, {
|
|
85
|
+
const vcr = setupVCR(CASSETTE_NAME, {
|
|
86
|
+
mode: "record",
|
|
87
|
+
cassettesDir: CASSETTE_DIR,
|
|
88
|
+
_allowRecordingInCI: true
|
|
89
|
+
});
|
|
78
90
|
const llm = NodeLLM.withProvider("mock-provider");
|
|
79
91
|
|
|
80
92
|
await llm.chat().ask("to_reset should not be redacted");
|
|
@@ -34,7 +34,8 @@ describe("VCR: Interaction Mismatch Detection", () => {
|
|
|
34
34
|
// First: Record with specific request
|
|
35
35
|
const vcrRecord = setupVCR(CASSETTE_NAME, {
|
|
36
36
|
mode: "record",
|
|
37
|
-
cassettesDir: CASSETTE_DIR
|
|
37
|
+
cassettesDir: CASSETTE_DIR,
|
|
38
|
+
_allowRecordingInCI: true
|
|
38
39
|
});
|
|
39
40
|
const llmRecord = NodeLLM.withProvider("mock-provider");
|
|
40
41
|
await llmRecord.chat().ask("Record this question");
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { test, expect, describe, afterEach } from "vitest";
|
|
2
|
+
import { withVCR, describeVCR } from "../../src/vcr.js";
|
|
3
|
+
import { NodeLLM, providerRegistry } from "@node-llm/core";
|
|
4
|
+
import { MockProvider } from "../helpers/MockProvider.js";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { Serializer } from "../../src/Serializer.js";
|
|
8
|
+
|
|
9
|
+
describe("VCR Advanced Types Persistence", () => {
|
|
10
|
+
const cassettePath = "test/cassettes/vcr-advanced-types-persistence/handles-rich-types.json";
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
if (fs.existsSync(cassettePath)) {
|
|
14
|
+
// Clean up file
|
|
15
|
+
fs.rmSync(cassettePath);
|
|
16
|
+
// Try to clean up parent dir if empty (optional but good)
|
|
17
|
+
try {
|
|
18
|
+
fs.rmdirSync(path.dirname(cassettePath));
|
|
19
|
+
} catch {
|
|
20
|
+
/* ignore */
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("Persists Date and Map in cassettes", async () => {
|
|
26
|
+
const date = new Date("2024-01-01T00:00:00.000Z");
|
|
27
|
+
const map = new Map<string, string>([["key", "value"]]);
|
|
28
|
+
|
|
29
|
+
await describeVCR("VCR Advanced Types Persistence", async () => {
|
|
30
|
+
providerRegistry.register("mock-provider", () => new MockProvider() as any);
|
|
31
|
+
|
|
32
|
+
// 1. Record Phase
|
|
33
|
+
await withVCR(
|
|
34
|
+
"Handles Rich Types",
|
|
35
|
+
{ mode: "record", _allowRecordingInCI: true },
|
|
36
|
+
async () => {
|
|
37
|
+
const llm = NodeLLM.withProvider("mock-provider");
|
|
38
|
+
|
|
39
|
+
// Pass rich types in params
|
|
40
|
+
await llm
|
|
41
|
+
.chat()
|
|
42
|
+
.withParams({
|
|
43
|
+
createdAt: date,
|
|
44
|
+
meta: map
|
|
45
|
+
})
|
|
46
|
+
.ask("Hello");
|
|
47
|
+
}
|
|
48
|
+
)();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// 2. Verify Disk Content (Serialization)
|
|
52
|
+
expect(fs.existsSync(cassettePath)).toBe(true);
|
|
53
|
+
const rawContent = fs.readFileSync(cassettePath, "utf-8");
|
|
54
|
+
|
|
55
|
+
// Should NOT contain raw ISO string only, but the typed wrapper
|
|
56
|
+
expect(rawContent).toContain('"$type": "Date"');
|
|
57
|
+
expect(rawContent).toContain('"value": "2024-01-01T00:00:00.000Z"');
|
|
58
|
+
expect(rawContent).toContain('"$type": "Map"');
|
|
59
|
+
|
|
60
|
+
// 3. Replay/Load Phase (Deserialization)
|
|
61
|
+
// We manually load to verify the deserialization logic
|
|
62
|
+
const cassette = Serializer.deserialize<any>(rawContent);
|
|
63
|
+
const request = cassette.interactions[0].request;
|
|
64
|
+
|
|
65
|
+
// Check that params are restored as real instances
|
|
66
|
+
expect(request.createdAt).toBeInstanceOf(Date);
|
|
67
|
+
expect(request.createdAt.toISOString()).toBe(date.toISOString());
|
|
68
|
+
|
|
69
|
+
expect(request.meta).toBeInstanceOf(Map);
|
|
70
|
+
expect(request.meta.get("key")).toBe("value");
|
|
71
|
+
});
|
|
72
|
+
});
|