| 1 | import { assert, describe, it } from "@effect/vitest"; |
| 2 | import * as Effect from "effect/Effect"; |
| 3 | import * as Stream from "effect/Stream"; |
| 4 | |
| 5 | import { collectUint8StreamText } from "./collectStreamText.ts"; |
| 6 | |
| 7 | const encoder = new TextEncoder(); |
| 8 | |
| 9 | describe("collectUint8StreamText", () => { |
| 10 | it.effect("collects Uint8Array chunks into decoded text", () => |
| 11 | Effect.gen(function* () { |
| 12 | const result = yield* collectUint8StreamText({ |
| 13 | stream: Stream.make(encoder.encode("hello "), encoder.encode("world")), |
| 14 | }); |
| 15 | |
| 16 | assert.deepStrictEqual(result, { |
| 17 | text: "hello world", |
| 18 | bytes: 11, |
| 19 | truncated: false, |
| 20 | invalidUtf8: false, |
| 21 | }); |
| 22 | }), |
| 23 | ); |
| 24 | |
| 25 | it.effect("truncates by bytes and appends an optional marker once", () => |
| 26 | Effect.gen(function* () { |
| 27 | const result = yield* collectUint8StreamText({ |
| 28 | stream: Stream.make(encoder.encode("abcdef"), encoder.encode("ghij")), |
| 29 | maxBytes: 5, |
| 30 | truncatedMarker: "[truncated]", |
| 31 | }); |
| 32 | |
| 33 | assert.deepStrictEqual(result, { |
| 34 | text: "abcde[truncated]", |
| 35 | bytes: 5, |
| 36 | truncated: true, |
| 37 | invalidUtf8: false, |
| 38 | }); |
| 39 | }), |
| 40 | ); |
| 41 | |
| 42 | it.effect("reports invalid UTF-8 separately from a literal replacement character", () => |
| 43 | Effect.gen(function* () { |
| 44 | const invalid = yield* collectUint8StreamText({ |
| 45 | stream: Stream.make(new Uint8Array([0x66, 0x80, 0x6f])), |
| 46 | }); |
| 47 | const literal = yield* collectUint8StreamText({ |
| 48 | stream: Stream.make(encoder.encode("before\uFFFDafter")), |
| 49 | }); |
| 50 | |
| 51 | assert.strictEqual(invalid.invalidUtf8, true); |
| 52 | assert.strictEqual(invalid.text, "f\uFFFDo"); |
| 53 | assert.strictEqual(literal.invalidUtf8, false); |
| 54 | assert.strictEqual(literal.text, "before\uFFFDafter"); |
| 55 | }), |
| 56 | ); |
| 57 | }); |