42 lines
3.1 KiB
TypeScript
42 lines
3.1 KiB
TypeScript
import { EventEmitter } from "node:events";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
const response = vi.hoisted(() => ({ status: 200, type: "video/mp4", body: Buffer.from("mock-MP4"), connectedAddress: "" }));
|
|
vi.mock("node:dns/promises", () => ({ lookup: () => Promise.resolve([{ address: "8.8.8.8", family: 4 }]) }));
|
|
vi.mock("node:https", () => ({ request: (_url: URL, options: { family?: number; lookup: (name: string, options: { all?: boolean }, callback: (error: Error | null, addresses: string | { address: string }[]) => void) => void }, receive: (value: EventEmitter) => void) => {
|
|
const req = new EventEmitter() as EventEmitter & { end: () => void; destroy: (error?: Error) => void };
|
|
let destroyed = false;
|
|
req.destroy = (error) => { destroyed = true; if (error) queueMicrotask(() => req.emit("error", error)); };
|
|
req.end = () => queueMicrotask(() => {
|
|
// Node 22 requests all addresses unless a concrete address family is supplied.
|
|
const all = options.family !== 4 && options.family !== 6;
|
|
options.lookup("cdn.example", { all }, (error, addresses) => {
|
|
if (error || (all && !Array.isArray(addresses))) { req.destroy(new Error("Invalid IP callback shape")); return; }
|
|
response.connectedAddress = typeof addresses === "string" ? addresses : addresses[0]?.address ?? "";
|
|
const incoming = Object.assign(new EventEmitter(), { statusCode: response.status, headers: { "content-type": response.type, "content-length": response.body.length }, destroy() {} });
|
|
receive(incoming); if (!destroyed) { incoming.emit("data", response.body); incoming.emit("end"); }
|
|
});
|
|
});
|
|
return req;
|
|
} }));
|
|
import { imageVideoTransfer } from "../src/image-video/media.js";
|
|
describe("bounded pinned HTTPS media transfer", () => {
|
|
it("connects with the checked public IP using Node's actual lookup callback contract", async () => {
|
|
response.status = 200; response.type = "video/mp4";
|
|
expect(await imageVideoTransfer("https://cdn.example/video?token=private")).toEqual(Buffer.from("mock-MP4"));
|
|
expect(response.connectedAddress).toBe("8.8.8.8");
|
|
});
|
|
it("rejects redirects, HTML masquerading as a video and oversized bodies", async () => {
|
|
response.status = 302; await expect(imageVideoTransfer("https://cdn.example/video")).rejects.toThrow();
|
|
response.status = 200; response.type = "text/html"; await expect(imageVideoTransfer("https://cdn.example/video")).rejects.toThrow();
|
|
response.type = "video/mp4"; await expect(imageVideoTransfer("https://cdn.example/video", { maxBytes: 2 })).rejects.toThrow();
|
|
});
|
|
it("marks temporary HTTP download failures retryable but redirect and content failures permanent", async () => {
|
|
response.status = 503; response.type = "video/mp4";
|
|
await expect(imageVideoTransfer("https://cdn.example/video")).rejects.toMatchObject({ code: "I2V_TRANSFER_TRANSIENT" });
|
|
response.status = 302;
|
|
await expect(imageVideoTransfer("https://cdn.example/video")).rejects.toMatchObject({ code: "I2V_TRANSFER_REJECTED" });
|
|
response.status = 200; response.type = "text/html";
|
|
await expect(imageVideoTransfer("https://cdn.example/video")).rejects.toMatchObject({ code: "I2V_TRANSFER_REJECTED" });
|
|
});
|
|
});
|