// @vitest-environment jsdom import { describe, expect, it, vi } from "vitest"; import type { FileVerificationReceipt, LocalFileRef, } from "../../src/application/ports/browser-file-storage/file.ts"; import { browserFilePolicyReference, createBrowserFileRuntime, type BrowserFilePolicyProfile, } from "../../src/adapters/browser-files/index.ts"; const filePolicy = browserFilePolicyReference( "runtime-file", "select-and-preview", ); const downloadPolicy = browserFilePolicyReference( "runtime-download", "bounded-generated-bin", ); const policies: readonly BrowserFilePolicyProfile[] = [ { reference: filePolicy, selection: { policyId: "attachment-v1", purpose: "attachment", classification: "PERSONAL", multiple: false, maxCount: 1, maxFileBytes: 8, maxTotalBytes: 8, allowEmpty: false, accept: [{ mediaType: "text/plain", extensions: [".txt"] }], }, inspection: { policyId: "preview-v1", maxInspectionBytes: 8, acceptedSignatures: [ { mediaType: "image/png", extensions: [".png"], patterns: [{ offset: 0, bytes: [0x89] }], }, ], }, preview: { allowedMediaTypes: ["image/png"], maxPreviewBytes: 8, }, }, { reference: downloadPolicy, download: { strategy: "BOUNDED_OBJECT_URL", mediaType: "application/octet-stream", safeExtension: ".bin", maxTransferBytes: 8, maxBufferedBytes: 8, integrity: "OPTIONAL", }, }, ]; const unusedBrowserManagedCapabilities = { resolve() { return { ok: false as const, error: { code: "POLICY_REJECTED" as const, operation: "DOWNLOAD" as const, retryable: false, recovery: "NONE" as const, }, }; }, }; function fileInput(): HTMLInputElement { const label = document.createElement("label"); label.textContent = "Attachment"; const input = document.createElement("input"); input.type = "file"; label.append(input); document.body.append(label); return input; } describe("browser file runtime hard limits and disposal", () => { it("revokes active download leases immediately on runtime dispose", async () => { const revoked: string[] = []; let scheduled: (() => void) | undefined; const runtime = createBrowserFileRuntime({ input: fileInput(), policies, limits: { hardMaxPreviewBytes: 8, hardMaxObjectUrlBytes: 8, hardMaxTransferBytes: 16, }, objectUrlApi: { createObjectURL: () => "blob:runtime-download", revokeObjectURL: (url) => revoked.push(url), }, download: { host: { handoff: vi.fn() }, browserManagedCapabilities: unusedBrowserManagedCapabilities, scheduler: { setTimeout(callback) { scheduled = callback; return 1; }, }, }, }); const delivery = { policy: downloadPolicy, source: { kind: "GENERATED" as const, bytes: { byteLength: 3, async *stream() { yield { ok: true as const, value: new Uint8Array([1, 2, 3]), }; }, }, }, suggestedFileName: "artifact", maxTransferBytes: 8, maxBufferedBytes: 8, signal: new AbortController().signal, onProgress() {}, }; expect(await runtime.downloads.deliver(delivery)).toMatchObject({ ok: true, value: { kind: "BROWSER_HANDOFF" }, }); expect(revoked).toEqual([]); runtime.dispose(); expect(revoked).toEqual(["blob:runtime-download"]); expect(await runtime.downloads.deliver(delivery)).toMatchObject({ ok: false, error: { code: "UNAVAILABLE" }, }); expect( await runtime.content.readRange({ ref: "file:disposed" as LocalFileRef, offset: 0, length: 0, signal: new AbortController().signal, }), ).toMatchObject({ ok: false, error: { code: "UNAVAILABLE" }, }); expect( await runtime.previews.create({ ref: "file:disposed" as LocalFileRef, verificationReceipt: "verification:disposed" as FileVerificationReceipt, policy: filePolicy, maxPreviewBytes: 1, signal: new AbortController().signal, }), ).toMatchObject({ ok: false, error: { code: "UNAVAILABLE" }, }); scheduled?.(); runtime.dispose(); expect(revoked).toEqual(["blob:runtime-download"]); }); it("aborts a pending native picker and prevents event resurrection", async () => { const input = fileInput(); Object.defineProperty(input, "showPicker", { configurable: true, value: vi.fn(), }); const runtime = createBrowserFileRuntime({ input, policies, limits: { hardMaxPreviewBytes: 8, hardMaxObjectUrlBytes: 8, hardMaxTransferBytes: 16, }, vault: { createReference: () => "file:must-not-exist", }, download: { host: { handoff() {} }, browserManagedCapabilities: unusedBrowserManagedCapabilities, }, }); const pending = runtime.baselinePicker.select({ policy: filePolicy, }); runtime.dispose(); expect(await pending).toMatchObject({ ok: false, error: { code: "ABORTED" }, }); Object.defineProperty(input, "files", { configurable: true, value: [ new File(["late"], "late.txt", { type: "text/plain", lastModified: 1, }), ], }); input.dispatchEvent(new Event("change")); expect( await runtime.baselinePicker.select({ policy: filePolicy, }), ).toMatchObject({ ok: false, error: { code: "UNAVAILABLE" }, }); }); it("aborts a generated download whose producer ignores cancellation", async () => { let reportStarted: (() => void) | undefined; const started = new Promise((resolve) => { reportStarted = resolve; }); let unblockProducer: (() => void) | undefined; const producerBlock = new Promise((resolve) => { unblockProducer = resolve; }); const runtime = createBrowserFileRuntime({ input: fileInput(), policies, limits: { hardMaxPreviewBytes: 8, hardMaxObjectUrlBytes: 8, hardMaxTransferBytes: 16, }, download: { host: { handoff() {} }, browserManagedCapabilities: unusedBrowserManagedCapabilities, }, }); const pending = runtime.downloads.deliver({ policy: downloadPolicy, source: { kind: "GENERATED", bytes: { byteLength: 1, async *stream() { reportStarted?.(); await producerBlock; yield { ok: true as const, value: new Uint8Array([1]), }; }, }, }, suggestedFileName: "blocked", maxTransferBytes: 8, maxBufferedBytes: 8, signal: new AbortController().signal, onProgress() {}, }); await started; runtime.dispose(); expect(await pending).toMatchObject({ ok: false, error: { code: "ABORTED", operation: "DOWNLOAD" }, }); unblockProducer?.(); }); it("rejects inconsistent absolute limits during composition", () => { expect(() => createBrowserFileRuntime({ input: fileInput(), policies, limits: { hardMaxPreviewBytes: 8, hardMaxObjectUrlBytes: 17, hardMaxTransferBytes: 16, }, download: { host: { handoff() {} }, browserManagedCapabilities: unusedBrowserManagedCapabilities, }, }), ).toThrowError("Browser file runtime hard limits are invalid."); }); });