1import * as BunMultipart from "@effect/platform-bun/BunMultipart"
2import { describe, it } from "@effect/vitest"
3import { assertTrue, strictEqual } from "@effect/vitest/utils"
4import { Effect, Stream } from "effect"
5
6describe("BunMultipart", () => {
7 it.live("propagates a request body read error while consuming an active file", () =>
8 Effect.gen(function*() {
9 const cause = new Error("body-read-failed")
10 const body = new ReadableStream<Uint8Array>({
11 start(controller) {
12 controller.enqueue(new TextEncoder().encode(
13 "--b\r\nContent-Disposition: form-data; name=\"file\"; filename=\"a.txt\"\r\n\r\nhello"
14 ))
15 },
16 pull(controller) {
17 controller.error(cause)
18 }
19 }, {
20 // Delay the error until the file bytes are consumed.
21 highWaterMark: 0
22 })
23 const request = new Request("http://localhost/upload", {
24 method: "POST",
25 headers: { "content-type": "multipart/form-data; boundary=b" },
26 body
27 })
28 let bytesRead = 0
29
30 const error = yield* BunMultipart.stream(request).pipe(
31 Stream.runForEach((part) =>
32 part._tag === "File"
33 ? Stream.runForEach(part.content, (chunk) =>
34 Effect.sync(() => {
35 bytesRead += chunk.length
36 }))
37 : Effect.die("expected file")
38 ),
39 Effect.timeout("1 second"),
40 Effect.flip
41 )
42
43 strictEqual(bytesRead, 5)
44 assertTrue(error._tag === "MultipartError")
45 strictEqual(error.reason._tag, "InternalError")
46 strictEqual(error.reason.cause, cause)
47 }))
48})