import { Readable } from "node:stream"; import { afterAll, describe, expect, it } from "vitest"; import { deleteObject, getObjectSize, getObjectStream, listObjects, objectExists, putObject, putObjectStream, } from "../../../apps/api/src/lib/object-storage.js"; describe("object-storage (local backend)", () => { const key = `outputs/test-${process.pid}/hello.txt`; afterAll(async () => { await deleteObject(key).catch(() => {}); }); it("round-trips buffers and streams with size and listing", async () => { await putObject(key, Buffer.from("hello world")); expect(await objectExists(key)).toBe(true); expect(await getObjectSize(key)).toBe(11); const chunks: Buffer[] = []; for await (const c of await getObjectStream(key)) chunks.push(c as Buffer); expect(Buffer.concat(chunks).toString()).toBe("hello world"); const ranged: Buffer[] = []; for await (const c of await getObjectStream(key, { start: 6, end: 10 })) ranged.push(c as Buffer); expect(Buffer.concat(ranged).toString()).toBe("world"); const listed = await listObjects(`outputs/test-${process.pid}/`); expect(listed.some((o) => o.key === key)).toBe(true); const streamKey = `outputs/test-${process.pid}/streamed.bin`; const written = await putObjectStream(streamKey, Readable.from([Buffer.alloc(1024, 1)]), { maxBytes: 2048, }); expect(written).toBe(1024); await expect( putObjectStream( `outputs/test-${process.pid}/too-big.bin`, Readable.from([Buffer.alloc(4096, 1)]), { maxBytes: 2048, }, ), ).rejects.toThrow(/exceeds/i); await deleteObject(streamKey); }); it("rejects path traversal in keys", async () => { await expect(putObject("outputs/../../etc/passwd", Buffer.from("x"))).rejects.toThrow( /invalid/i, ); }); });