UNPKG

mhtml-stream

Version:
149 lines (148 loc) 6.2 kB
import { describe, expect, test } from "bun:test"; import { bytesEqual, collect, decodeBinary, decodeIdentity, decodeQuotedPrintable, indexOf, splitStream, } from "./utils"; const encoder = new TextEncoder(); const decoder = new TextDecoder(); // eslint-disable-next-line @typescript-eslint/require-await async function* toAsyncIterable(items) { for (const item of items) { yield item; } } describe("bytesEqual()", () => { test("length failure", () => { const left = new Uint8Array([1, 2, 3]); const right = new Uint8Array([1, 2, 3, 4]); expect(bytesEqual(left, right)).toBe(false); }); test("simple success", () => { const left = new Uint8Array([1, 2, 3]); const right = new Uint8Array([1, 2, 3]); expect(bytesEqual(left, right)).toBe(true); }); test("simple failure", () => { const left = new Uint8Array([1, 2, 3]); const right = new Uint8Array([1, 2, 4]); expect(bytesEqual(left, right)).toBe(false); }); test("fast path success", () => { const left = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]).subarray(1); const right = new Uint8Array([0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7]).subarray(5); expect(bytesEqual(left, right)).toBe(true); }); test("fast path failure start", () => { const left = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]).subarray(1); const right = new Uint8Array([0, 0, 0, 0, 0, 2, 2, 3, 4, 5, 6, 7]).subarray(5); expect(bytesEqual(left, right)).toBe(false); }); test("fast path failure middle", () => { const left = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]).subarray(1); const right = new Uint8Array([0, 0, 0, 0, 0, 1, 2, 4, 4, 5, 6, 7]).subarray(5); expect(bytesEqual(left, right)).toBe(false); }); test("fast path failure end", () => { const left = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]).subarray(1); const right = new Uint8Array([0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 8]).subarray(5); expect(bytesEqual(left, right)).toBe(false); }); test("slow path success", () => { const left = new Uint8Array([1, 2, 3, 4, 5]); const right = new Uint8Array([0, 1, 2, 3, 4, 5]).subarray(1); expect(bytesEqual(left, right)).toBe(true); }); test("slow path failure", () => { const left = new Uint8Array([1, 2, 3, 4, 5]); const right = new Uint8Array([0, 1, 2, 3, 4, 6]).subarray(1); expect(bytesEqual(left, right)).toBe(false); }); }); describe("indexOf()", () => { test("simple success", () => { const haystack = new Uint8Array([0, 1, 2]); const needle = new Uint8Array([1]); expect(indexOf(haystack, needle)).toEqual(1); }); test("simple failure", () => { const haystack = new Uint8Array([0, 1, 2]); const needle = new Uint8Array([3]); expect(indexOf(haystack, needle)).toEqual(-1); }); test("complex success", () => { const haystack = new Uint8Array([0, 1, 3, 1, 2, 3]); const needle = new Uint8Array([1, 2]); expect(indexOf(haystack, needle)).toEqual(3); }); test("complex failure", () => { const haystack = new Uint8Array([0, 1, 3, 1, 4, 3]); const needle = new Uint8Array([1, 2]); expect(indexOf(haystack, needle)).toEqual(-1); }); }); test("splitStream()", async () => { const split = new Uint8Array([1, 2]); const input = [ new Uint8Array([255, 1, 2, 3, 4, 1, 2, 1]), new Uint8Array([2, 5, 6, 7, 8]), new Uint8Array([9, 10, 11, 2, 1, 12, 13]), new Uint8Array([14, 15, 1, 2, 16]), ]; const parts = []; for await (const part of splitStream(toAsyncIterable(input), split)) { parts.push(part); } const expected = [ new Uint8Array([255]), new Uint8Array([3, 4]), new Uint8Array(0), new Uint8Array([5, 6, 7, 8, 9, 10, 11, 2, 1, 12, 13, 14, 15]), new Uint8Array([16]), ]; expect(parts.length).toEqual(expected.length); for (const [i, part] of parts.entries()) { expect(bytesEqual(part, expected[i])).toBe(true); } }); test("collect()", async () => { const res = await collect(toAsyncIterable([ new Uint8Array([0, 1]), new Uint8Array([2, 3, 4]), new Uint8Array([5, 6]), ])); const expected = new Uint8Array([0, 1, 2, 3, 4, 5, 6]); expect(bytesEqual(res, expected)).toBe(true); }); describe("decodeQuotedPrintable()", () => { test("success", async () => { // eslint-disable-next-line spellcheck/spell-checker const input = ["key=3Dvalue", "this line continues =", "on the next line"]; const res = decoder.decode(await collect(decodeQuotedPrintable(toAsyncIterable(input.map((l) => encoder.encode(l)))))); expect(res).toStrictEqual("key=value\nthis line continues on the next line\n"); }); test("ascii failure", async () => { const input = ["\xff"]; const decoded = decodeQuotedPrintable(toAsyncIterable(input.map((l) => encoder.encode(l)))); // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression,@typescript-eslint/await-thenable await expect((async () => { for await (const _ of decoded) { // } })()).rejects.toThrow("non-ascii"); }); test("encoding failure", async () => { const input = ["a=0"]; const decoded = decodeQuotedPrintable(toAsyncIterable(input.map((l) => encoder.encode(l)))); // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression,@typescript-eslint/await-thenable await expect((async () => { for await (const _ of decoded) { // } })()).rejects.toThrow("quoted printable escape"); }); }); test("decodeIdentity()", async () => { const input = ["line ", "not ascii \xff"]; const res = decoder.decode(await collect(decodeIdentity(toAsyncIterable(input.map((l) => encoder.encode(l)))))); expect(res).toStrictEqual("line not ascii \xff"); }); test("decodeBinary()", () => { expect(decodeBinary).toThrow("explicitly"); });