90 lines
2.5 KiB
TypeScript
90 lines
2.5 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import {
|
|
createTemporaryObjectCleaner,
|
|
ffmpegVideoTranscoder,
|
|
memoryQuotaStore,
|
|
multipartUpload,
|
|
postgresQuotaStore,
|
|
} from "../src/index.ts";
|
|
|
|
test("quota stores enforce durable byte and object limits", async () => {
|
|
const quota = memoryQuotaStore();
|
|
expect(await quota.reserve("tenant", 60, { bytes: 100, objects: 2 })).toBeTrue();
|
|
expect(await quota.reserve("tenant", 50, { bytes: 100, objects: 2 })).toBeFalse();
|
|
await quota.release("tenant", 60);
|
|
expect(await quota.get("tenant")).toMatchObject({ bytes: 0, objects: 0 });
|
|
const calls: unknown[][] = [];
|
|
const sql = postgresQuotaStore({
|
|
async query<T>(_sql: string, parameters?: unknown[]) {
|
|
calls.push(parameters ?? []);
|
|
return { rows: [{ owner: "tenant" } as T] };
|
|
},
|
|
});
|
|
expect(await sql.reserve("tenant", 10, { bytes: 20 })).toBeTrue();
|
|
expect(calls[0]?.[0]).toBe("tenant");
|
|
});
|
|
|
|
test("multipart uploader limits concurrency, completes and aborts failures", async () => {
|
|
const uploaded: number[] = [];
|
|
let completed = false;
|
|
let aborted = false;
|
|
const client = {
|
|
async create() {
|
|
return "upload";
|
|
},
|
|
async uploadPart(_id: string, _key: string, part: number) {
|
|
uploaded.push(part);
|
|
return `etag-${part}`;
|
|
},
|
|
async complete() {
|
|
completed = true;
|
|
},
|
|
async abort() {
|
|
aborted = true;
|
|
},
|
|
};
|
|
await multipartUpload(
|
|
client,
|
|
"video",
|
|
new Uint8Array(11 * 1024 * 1024),
|
|
{ contentType: "video/mp4" },
|
|
{ partBytes: 5 * 1024 * 1024, concurrency: 2 },
|
|
);
|
|
expect(uploaded).toEqual([1, 2, 3]);
|
|
expect(completed).toBeTrue();
|
|
expect(aborted).toBeFalse();
|
|
});
|
|
|
|
test("temporary cleanup and video transcoding are bounded and injectable", async () => {
|
|
let now = 10;
|
|
const deleted: string[] = [];
|
|
const cleaner = createTemporaryObjectCleaner(
|
|
{
|
|
async put() {},
|
|
async get() {
|
|
return null;
|
|
},
|
|
async delete(key) {
|
|
deleted.push(key);
|
|
},
|
|
publicUrl() {
|
|
return null;
|
|
},
|
|
},
|
|
{ now: () => now },
|
|
);
|
|
cleaner.track("tmp/a", 5);
|
|
expect(await cleaner.cleanup()).toBe(0);
|
|
now = 15;
|
|
expect(await cleaner.cleanup()).toBe(1);
|
|
expect(deleted).toEqual(["tmp/a"]);
|
|
let command: string[] = [];
|
|
await ffmpegVideoTranscoder({
|
|
spawn(args) {
|
|
command = args;
|
|
return { exited: Promise.resolve(0) };
|
|
},
|
|
})("input.mov", "output.mp4", { format: "mp4", width: 1280, videoBitrateKbps: 2000 });
|
|
expect(command).toContain("scale=1280:-2");
|
|
});
|