1import { assert, describe, it } from "@effect/vitest";
2import * as Effect from "effect/Effect";
3import * as Stream from "effect/Stream";
4
5import { collectUint8StreamText } from "./collectStreamText.ts";
6
7const encoder = new TextEncoder();
8
9describe("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});