import { SuaveplanError } from "@suaveplan/error"; import { beforeEach, describe, expect, it } from "@suaveplan/testing/runner"; import { asDrive, FakeDrive, FakeDriveError, } from "../../tests/setup/fake-drive.js"; import { DRIVE_URI_CODES, DriveUriError } from "../error-codes/error-codes.js"; import { DriveStorageLikeAdapter, isDriveNotFound, } from "./drive-storage-like.js"; class CustomNotInDriveError extends SuaveplanError {} describe("isDriveNotFound", () => { it("matches any SuaveplanError whose code ends in .NOT_FOUND", { timeout: 5000, }, () => { expect( isDriveNotFound(new CustomNotInDriveError("DRIVE.NOT_FOUND", "x")) ).toBe(true); expect( isDriveNotFound( new CustomNotInDriveError("SOMETHING_ELSE.NOT_FOUND", "x") ) ).toBe(true); }); it("returns false for non-SuaveplanError values", { timeout: 5000 }, () => { expect(isDriveNotFound(undefined)).toBe(false); expect(isDriveNotFound(null)).toBe(false); expect(isDriveNotFound("string")).toBe(false); expect(isDriveNotFound(new Error("plain"))).toBe(false); }); it("walks the cause chain", { timeout: 5000 }, () => { const inner = new FakeDriveError("DRIVE.NOT_FOUND", "missing"); const outer = new Error("wrapped"); (outer as Error & { cause?: unknown }).cause = inner; expect(isDriveNotFound(outer)).toBe(true); }); it("aborts on a self-referential cause cycle", { timeout: 5000 }, () => { const err = new Error("cyclic"); (err as Error & { cause?: unknown }).cause = err; expect(isDriveNotFound(err)).toBe(false); }); it("returns false when no cause member exists", { timeout: 5000 }, () => { expect(isDriveNotFound({ message: "no cause", code: "X" })).toBe(false); }); }); describe("DriveStorageLikeAdapter", () => { let fake: FakeDrive; let adapter: DriveStorageLikeAdapter; beforeEach(() => { fake = new FakeDrive({ contents: new Map([["a.bin", new Uint8Array([1, 2, 3])]]), }); adapter = new DriveStorageLikeAdapter(asDrive(fake)); }); it("returns undefined for Drive not-found errors", { timeout: 5000, }, async () => { const result = await adapter.get("missing.bin"); expect(result).toBeUndefined(); }); it("returns the bytes on a hit", { timeout: 5000 }, async () => { const bytes = await adapter.get("a.bin"); expect(bytes).toEqual(new Uint8Array([1, 2, 3])); }); it("re-throws non-not-found Drive errors unchanged", { timeout: 5000, }, async () => { const failure = new FakeDriveError( "DRIVE.PLUGIN_FAILED", "plugin crashed" ); const bad = new FakeDrive({ readError: () => failure }); const a = new DriveStorageLikeAdapter(asDrive(bad)); await expect(a.get("x")).rejects.toBe(failure); }); it("forwards set, delete, exists, list", { timeout: 5000 }, async () => { await adapter.set("b.bin", new Uint8Array([9])); expect(fake.calls.write).toHaveLength(1); expect(await adapter.exists("a.bin")).toBe(true); expect(await adapter.exists("missing")).toBe(false); const removed = await adapter.delete("b.bin"); expect(removed).toBe(true); const listed = await adapter.list({ prefix: "a" }); expect(listed.keys).toContain("a.bin"); expect(listed.hasMore).toBe(false); expect(typeof listed.total).toBe("number"); }); it("list with no options defaults prefix/limit to undefined", { timeout: 5000, }, async () => { const result = await adapter.list(); expect(Array.isArray(result.keys)).toBe(true); }); it("list forwards options.limit to the drive", { timeout: 5000, }, async () => { const result = await adapter.list({ prefix: "a", limit: 10 }); expect(Array.isArray(result.keys)).toBe(true); }); it("list copes with a drive returning entries-shape instead of keys-shape", { timeout: 5000, }, async () => { const odd = { async read(): Promise { return new Uint8Array(); }, async write(): Promise {}, async delete(): Promise { return false; }, async stat(): Promise { return null; }, async list(): Promise { return { entries: [{ path: "p1" }, { path: "p2" }], }; }, createReadStream(): ReadableStream { return new ReadableStream(); }, }; const a = new DriveStorageLikeAdapter( odd as unknown as Parameters[0] ); const r = await a.list(); expect(r.keys).toEqual(["p1", "p2"]); expect(r.total).toBe(2); }); it("list copes with a drive returning neither shape", { timeout: 5000, }, async () => { const oddler = { async read(): Promise { return new Uint8Array(); }, async write(): Promise {}, async delete(): Promise { return false; }, async stat(): Promise { return null; }, async list(): Promise { return undefined; }, createReadStream(): ReadableStream { return new ReadableStream(); }, }; const a = new DriveStorageLikeAdapter( oddler as unknown as Parameters[0] ); const r = await a.list(); expect(r.keys).toEqual([]); expect(r.total).toBe(0); expect(r.hasMore).toBe(false); }); it("list forwards continuationToken when the drive returns one", { timeout: 5000, }, async () => { const cursored = { async read(): Promise { return new Uint8Array(); }, async write(): Promise {}, async delete(): Promise { return false; }, async stat(): Promise { return null; }, async list(): Promise { return { keys: ["a", "b"], total: 99, hasMore: true, continuationToken: "next-page", }; }, createReadStream(): ReadableStream { return new ReadableStream(); }, }; const a = new DriveStorageLikeAdapter( cursored as unknown as Parameters[0] ); const r = await a.list(); expect(r.continuationToken).toBe("next-page"); expect(r.hasMore).toBe(true); expect(r.total).toBe(99); }); it("clear is unsupported and names the alternative", { timeout: 5000, }, async () => { await expect(adapter.clear()).rejects.toBeInstanceOf(SuaveplanError); try { await adapter.clear(); } catch (err) { expect(err).toBeInstanceOf(DriveUriError); expect((err as DriveUriError).code).toBe( DRIVE_URI_CODES.WRITE_UNSUPPORTED ); expect((err as DriveUriError).message).toContain( "Drive.delete(path)" ); } }); it("createReadStream forwards to Drive.createReadStream", { timeout: 5000, }, async () => { const stream = adapter.createReadStream("a.bin"); expect(fake.calls.createReadStream).toHaveLength(1); // Drain it const reader = stream.getReader(); const { value } = await reader.read(); expect(value).toEqual(new Uint8Array([1, 2, 3])); await reader.read(); // close reader.releaseLock(); }); });