85 lines
3.2 KiB
JavaScript
85 lines
3.2 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import test from "node:test";
|
|
import ts from "typescript";
|
|
|
|
const repoRoot = path.resolve(import.meta.dirname, "../../..");
|
|
const sourcePath = path.join(repoRoot, "web/protocol/file-byte-reader.ts");
|
|
const transpiled = ts.transpileModule(fs.readFileSync(sourcePath, "utf8"), {
|
|
compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 },
|
|
fileName: sourcePath,
|
|
reportDiagnostics: true,
|
|
});
|
|
assert.deepEqual(transpiled.diagnostics, []);
|
|
const moduleUrl = `data:text/javascript;base64,${Buffer.from(transpiled.outputText).toString("base64")}`;
|
|
const { FileByteReadError, readFileBytes } = await import(moduleUrl);
|
|
|
|
function chunkSource(chunks, declaredSize = chunks.reduce((total, chunk) => total + (chunk.byteLength ?? chunk.length), 0)) {
|
|
return {
|
|
size: declaredSize,
|
|
stream() {
|
|
let index = 0;
|
|
return new ReadableStream({
|
|
pull(controller) {
|
|
if (index === chunks.length) controller.close();
|
|
else controller.enqueue(Uint8Array.from(chunks[index++]));
|
|
},
|
|
});
|
|
},
|
|
};
|
|
}
|
|
|
|
test("M7-03 reports progress from exact consumed byte counts", async () => {
|
|
const observations = [];
|
|
const result = await readFileBytes(chunkSource([[1, 2], [3, 4, 5], [6]]), {
|
|
signal: new AbortController().signal,
|
|
onProgress: (item) => observations.push(item),
|
|
});
|
|
assert.deepEqual(Array.from(new Uint8Array(result)), [1, 2, 3, 4, 5, 6]);
|
|
assert.deepEqual(observations.map(({ phase, bytesRead, totalBytes, fraction }) => [phase, bytesRead, totalBytes, fraction]), [
|
|
["STARTED", 0, 6, 0],
|
|
["READING", 2, 6, 2 / 6],
|
|
["READING", 5, 6, 5 / 6],
|
|
["READING", 6, 6, 1],
|
|
["COMPLETED", 6, 6, 1],
|
|
]);
|
|
});
|
|
|
|
test("M7-03 cancels between chunks without reporting completion", async () => {
|
|
const controller = new AbortController();
|
|
const observations = [];
|
|
const resources = [];
|
|
await assert.rejects(
|
|
readFileBytes(chunkSource([[1, 2], [3, 4], [5, 6]]), {
|
|
signal: controller.signal,
|
|
onProgress: (item) => {
|
|
observations.push(item);
|
|
if (item.phase === "READING" && item.bytesRead === 2) controller.abort();
|
|
},
|
|
onResourceState: (state) => resources.push(state),
|
|
}),
|
|
(error) => error instanceof FileByteReadError && error.code === "FILE_READ_CANCELLED",
|
|
);
|
|
assert.deepEqual(observations.map((item) => [item.phase, item.bytesRead]), [
|
|
["STARTED", 0],
|
|
["READING", 2],
|
|
["CANCELLED", 2],
|
|
]);
|
|
assert.deepEqual(resources, [
|
|
{ liveReaders: 1, liveInputBytes: 6, liveStagingFiles: 0 },
|
|
{ liveReaders: 0, liveInputBytes: 0, liveStagingFiles: 0 },
|
|
]);
|
|
});
|
|
|
|
test("M7-03 rejects streams shorter or longer than their declared byte size", async () => {
|
|
await assert.rejects(
|
|
readFileBytes(chunkSource([[1, 2]], 3), { signal: new AbortController().signal }),
|
|
(error) => error instanceof FileByteReadError && error.code === "FILE_READ_SIZE_MISMATCH",
|
|
);
|
|
await assert.rejects(
|
|
readFileBytes(chunkSource([[1, 2, 3]], 2), { signal: new AbortController().signal }),
|
|
(error) => error instanceof FileByteReadError && error.code === "FILE_READ_SIZE_MISMATCH",
|
|
);
|
|
});
|