74 lines
2.7 KiB
TypeScript
74 lines
2.7 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { createResumableUploadManager, memoryResumableSessionStore } from "../src/index.ts";
|
|
import type { StorageDriver } from "../src/index.ts";
|
|
|
|
function driver() {
|
|
const objects = new Map<string, Uint8Array>();
|
|
const storage: StorageDriver = {
|
|
async put(key, data) {
|
|
objects.set(key, data.slice());
|
|
},
|
|
async get(key) {
|
|
const body = objects.get(key);
|
|
return body ? { body, contentType: "application/octet-stream", size: body.length } : null;
|
|
},
|
|
async delete(key) {
|
|
objects.delete(key);
|
|
},
|
|
publicUrl: () => null,
|
|
};
|
|
return { storage, objects };
|
|
}
|
|
|
|
test("resumable uploads accept out-of-order idempotent chunks and assemble once", async () => {
|
|
const { storage, objects } = driver();
|
|
const manager = createResumableUploadManager({
|
|
driver: storage,
|
|
chunkSize: 3,
|
|
maxBytes: 20,
|
|
accept: ["text/plain"],
|
|
});
|
|
const session = await manager.create({ name: "hello.txt", type: "text/plain", size: 8 });
|
|
expect(session.totalChunks).toBe(3);
|
|
expect((await manager.uploadChunk(session.id, 1, new TextEncoder().encode("lo "))).complete).toBe(
|
|
false,
|
|
);
|
|
await manager.uploadChunk(session.id, 0, new TextEncoder().encode("hel"));
|
|
await manager.uploadChunk(session.id, 0, new TextEncoder().encode("hel"));
|
|
const result = await manager.uploadChunk(session.id, 2, new TextEncoder().encode("!!"));
|
|
expect(result.complete).toBe(true);
|
|
expect(new TextDecoder().decode(objects.get(result.file!.key))).toBe("hello !!");
|
|
expect(await manager.status(session.id)).toBeNull();
|
|
});
|
|
|
|
test("resumable uploads enforce checksums, conflicts, limits, expiry, and cancellation", async () => {
|
|
let now = 0;
|
|
const { storage } = driver();
|
|
const sessions = memoryResumableSessionStore();
|
|
const manager = createResumableUploadManager({
|
|
driver: storage,
|
|
sessions,
|
|
chunkSize: 2,
|
|
maxBytes: 4,
|
|
maxSessions: 1,
|
|
ttlMs: 10,
|
|
now: () => now,
|
|
});
|
|
const session = await manager.create({ name: "data.bin", size: 4 });
|
|
await expect(manager.create({ name: "other.bin", size: 1 })).rejects.toThrow("too many");
|
|
await expect(manager.uploadChunk(session.id, 0, new Uint8Array([1, 2]), "bad")).rejects.toThrow(
|
|
"checksum",
|
|
);
|
|
await manager.uploadChunk(session.id, 0, new Uint8Array([1, 2]));
|
|
await expect(manager.uploadChunk(session.id, 0, new Uint8Array([2, 1]))).rejects.toThrow(
|
|
"different content",
|
|
);
|
|
expect(await manager.cancel(session.id)).toBe(true);
|
|
const expiring = await manager.create({ name: "expire.bin", size: 2 });
|
|
now = 11;
|
|
expect(await manager.prune()).toBe(1);
|
|
await expect(manager.uploadChunk(expiring.id, 0, new Uint8Array([1, 2]))).rejects.toThrow(
|
|
"expired",
|
|
);
|
|
});
|