test(ai): record responses websocket flows (#43660)
This commit is contained in:
@@ -31,8 +31,11 @@ interface PendingRecordings {
|
||||
}
|
||||
type Frame = string | Uint8Array
|
||||
|
||||
const normalizeProtocols = (protocols?: string | Array<string>): Array<string> =>
|
||||
protocols === undefined ? [] : typeof protocols === "string" ? [protocols] : [...protocols]
|
||||
const normalizeProtocols = (protocols: unknown): Array<string> => {
|
||||
if (typeof protocols === "string") return [protocols]
|
||||
if (Array.isArray(protocols)) return protocols.filter((protocol): protocol is string => typeof protocol === "string")
|
||||
return []
|
||||
}
|
||||
const frameFromWebSocketData = async (data: unknown): Promise<Frame> => {
|
||||
if (typeof data === "string") return data
|
||||
if (data instanceof Blob) return new Uint8Array(await data.arrayBuffer())
|
||||
@@ -371,7 +374,7 @@ const makeRecordingWebSocketConstructor = (
|
||||
return (url, protocols) => {
|
||||
const sequence = nextSequence++
|
||||
const requestedProtocols = normalizeProtocols(protocols)
|
||||
const native = upstream(url, requestedProtocols)
|
||||
const native = Reflect.apply(upstream, undefined, [url, protocols])
|
||||
const events: WebSocketEvent[] = []
|
||||
let opened = false
|
||||
let failed = false
|
||||
|
||||
@@ -80,6 +80,38 @@ describe("WebSocket", () => {
|
||||
])
|
||||
})
|
||||
|
||||
test("constructor recording forwards handshake options", async () => {
|
||||
using directory = tempDirectory("http-recorder-websocket-constructor-")
|
||||
let received: unknown
|
||||
const recorder = HttpRecorder.layerWebSocketConstructor("websocket/constructor-options", {
|
||||
directory: directory.path,
|
||||
}).pipe(
|
||||
Layer.provide(
|
||||
Layer.succeed(Socket.WebSocketConstructor, (url, options) => {
|
||||
received = options
|
||||
// oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion -- the fixture implements the WebSocket surface used by the recorder.
|
||||
return new EchoWebSocket(url) as unknown as globalThis.WebSocket
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
await Effect.runPromise(
|
||||
Effect.gen(function* () {
|
||||
const constructor = yield* Socket.WebSocketConstructor
|
||||
const options = { headers: { authorization: "Bearer fixture" } }
|
||||
const socket = Reflect.apply(constructor, undefined, ["wss://echo.example.test/options", options])
|
||||
yield* Effect.callback<void>((resume) => {
|
||||
socket.addEventListener("open", () => {
|
||||
socket.close()
|
||||
resume(Effect.void)
|
||||
})
|
||||
})
|
||||
}).pipe(Effect.scoped, Effect.provide(recorder)),
|
||||
)
|
||||
|
||||
expect(received).toEqual({ headers: { authorization: "Bearer fixture" } })
|
||||
})
|
||||
|
||||
test("constructor replay validates dynamic URLs and protocols without opening a live socket", async () => {
|
||||
using directory = tempDirectory("http-recorder-websocket-constructor-")
|
||||
await seedCassetteDirectory(directory.path, "websocket/constructor", [
|
||||
|
||||
Reference in New Issue
Block a user