Files
clean-architecture-frontend…/tests/unit/upload-cancellation-channel.test.ts
T

118 lines
3.3 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import {
createBrowserUploadCancellationChannel,
type UploadCancellationBroadcastFacade,
} from "../../src/adapters/browser-transfer/resumable-upload/upload-cancellation-channel.ts";
type MessageListener = (
event: Readonly<{ data: unknown }>,
) => void;
function createBroadcastHarness() {
const listeners: Array<Set<MessageListener>> = [];
const closes: Array<ReturnType<typeof vi.fn>> = [];
function createChannel(): UploadCancellationBroadcastFacade {
const ownIndex = listeners.length;
const ownListeners = new Set<MessageListener>();
const close = vi.fn();
listeners.push(ownListeners);
closes.push(close);
return {
postMessage(message) {
for (const [index, peerListeners] of listeners.entries()) {
if (index === ownIndex) continue;
for (const listener of [...peerListeners]) {
listener({ data: structuredClone(message) });
}
}
},
addEventListener(_type, listener) {
ownListeners.add(listener);
},
removeEventListener(_type, listener) {
ownListeners.delete(listener);
},
close,
};
}
return {
createChannel,
emit(index: number, data: unknown) {
for (const listener of [...(listeners[index] ?? [])]) {
listener({ data });
}
},
listenerCount(index: number) {
return listeners[index]?.size ?? 0;
},
closes,
};
}
describe("browser upload cancellation channel", () => {
it("delivers only the closed v1 wire message to another context", () => {
const harness = createBroadcastHarness();
const first = createBrowserUploadCancellationChannel({
channelName: "product-upload-cancel-v1",
createChannel: harness.createChannel,
});
const second = createBrowserUploadCancellationChannel({
channelName: "product-upload-cancel-v1",
createChannel: harness.createChannel,
});
expect(first).toBeDefined();
expect(second).toBeDefined();
if (!first || !second) return;
const received = vi.fn();
const release = second.subscribe(received);
expect(first.publish("upload_key_cross_context")).toBe(true);
expect(received).toHaveBeenCalledOnce();
expect(received).toHaveBeenCalledWith(
"upload_key_cross_context",
);
for (const malformed of [
null,
{
protocol: "RESUMABLE_UPLOAD_CANCEL_V0",
uploadKey: "upload_key_cross_context",
},
{
protocol: "RESUMABLE_UPLOAD_CANCEL_V1",
uploadKey: "short",
},
{
protocol: "RESUMABLE_UPLOAD_CANCEL_V1",
uploadKey: "upload_key_cross_context",
extra: true,
},
]) {
harness.emit(1, malformed);
}
expect(received).toHaveBeenCalledTimes(1);
release();
second.close();
expect(harness.listenerCount(1)).toBe(0);
expect(harness.closes[1]).toHaveBeenCalledOnce();
expect(second.publish("upload_key_cross_context")).toBe(false);
first.close();
});
it("fails closed when the host is unsupported and rejects invalid namespaces", () => {
expect(
createBrowserUploadCancellationChannel({ host: {} }),
).toBeUndefined();
expect(() =>
createBrowserUploadCancellationChannel({
channelName: "invalid channel name",
host: {},
}),
).toThrow(/channel name/u);
});
});