-
-
Notifications
You must be signed in to change notification settings - Fork 679
Description
Bug Description
When excluding a url from a "record" run of SnapshotAgent, encountering that same url in "playback" mode throws an error: despite the url being ignored.
This means it's not possible to e.g. record requests from an app being tested on one run of an integration test, and play them back on another run, while excluding the requests to the app being tested (they'll raise an error on the playback run).
Reproducible By
import { SnapshotAgent, setGlobalDispatcher } from "undici";
const recordingAgent = new SnapshotAgent({
mode: "record",
snapshotPath: "./snapshot.json",
excludeUrls: [`https://example.com/2`],
});
setGlobalDispatcher(recordingAgent);
await fetch("https://example.com/1");
await fetch("https://example.com/2");
await recordingAgent.saveSnapshots();
const playbackAgent = new SnapshotAgent({
mode: "playback",
snapshotPath: "./snapshot.json",
excludeUrls: [`https://example.com/2`],
});
setGlobalDispatcher(playbackAgent);
await fetch("https://example.com/1");
await fetch("https://example.com/2");Expected Behavior
SnapshotAgent should allow the request to pass unmocked, without raising an error.
The issue seems to stem from findSnapshot returning undefined for both excluded/ignored requests and missing requests:
undici/lib/mock/snapshot-recorder.js
Lines 342 to 350 in f29471c
| if (!this.shouldPlayback(requestOpts)) { | |
| return undefined // Skip playback | |
| } | |
| // Check URL exclusion patterns | |
| const url = new URL(requestOpts.path, requestOpts.origin).toString() | |
| if (this.#isUrlExcluded(url)) { | |
| return undefined // Skip playback | |
| } |
undici/lib/mock/snapshot-recorder.js
Line 356 in f29471c
| if (!snapshot) return undefined |
So in SnapshotAgent it's not possible to distinguish between the two:
undici/lib/mock/snapshot-agent.js
Lines 99 to 106 in f29471c
| } else { | |
| // Playback mode but no snapshot found | |
| const error = new UndiciError(`No snapshot found for ${opts.method || 'GET'} ${opts.path}`) | |
| if (handler.onError) { | |
| handler.onError(error) | |
| return | |
| } | |
| throw error |
Logs & Screenshots
node:internal/deps/undici/undici:13510
Error.captureStackTrace(err);
^
TypeError: fetch failed
at node:internal/deps/undici/undici:13510:13
at async file:///home/leo/Projects/mdn/undici-snapshot-bug/index.js:23:1 {
[cause]: UndiciError: No snapshot found for GET /2
at SnapshotAgent.dispatch (/home/leo/Projects/mdn/undici-snapshot-bug/node_modules/undici/lib/mock/snapshot-agent.js:101:23)
at node:internal/deps/undici/undici:11144:55
at new Promise (<anonymous>)
at dispatch (node:internal/deps/undici/undici:11144:16)
at httpNetworkFetch (node:internal/deps/undici/undici:11041:73)
at httpNetworkOrCacheFetch (node:internal/deps/undici/undici:10927:39)
at httpFetch (node:internal/deps/undici/undici:10760:43)
at schemeFetch (node:internal/deps/undici/undici:10677:18)
at node:internal/deps/undici/undici:10522:26
at mainFetch (node:internal/deps/undici/undici:10541:11) {
code: 'UND_ERR'
}
}
Node.js v22.18.0