Files
suaveplan/packages/networking/drive/drive-uri/src/adapter/drive-storage-like.test.ts
2026-05-27 23:47:53 +01:00

246 lines
8.1 KiB
TypeScript

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<string> {}
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<Uint8Array> {
return new Uint8Array();
},
async write(): Promise<void> {},
async delete(): Promise<boolean> {
return false;
},
async stat(): Promise<unknown | null> {
return null;
},
async list(): Promise<unknown> {
return {
entries: [{ path: "p1" }, { path: "p2" }],
};
},
createReadStream(): ReadableStream<Uint8Array> {
return new ReadableStream();
},
};
const a = new DriveStorageLikeAdapter(
odd as unknown as Parameters<typeof DriveStorageLikeAdapter>[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<Uint8Array> {
return new Uint8Array();
},
async write(): Promise<void> {},
async delete(): Promise<boolean> {
return false;
},
async stat(): Promise<unknown | null> {
return null;
},
async list(): Promise<unknown> {
return undefined;
},
createReadStream(): ReadableStream<Uint8Array> {
return new ReadableStream();
},
};
const a = new DriveStorageLikeAdapter(
oddler as unknown as Parameters<typeof DriveStorageLikeAdapter>[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<Uint8Array> {
return new Uint8Array();
},
async write(): Promise<void> {},
async delete(): Promise<boolean> {
return false;
},
async stat(): Promise<unknown | null> {
return null;
},
async list(): Promise<unknown> {
return {
keys: ["a", "b"],
total: 99,
hasMore: true,
continuationToken: "next-page",
};
},
createReadStream(): ReadableStream<Uint8Array> {
return new ReadableStream();
},
};
const a = new DriveStorageLikeAdapter(
cursored as unknown as Parameters<typeof DriveStorageLikeAdapter>[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();
});
});