import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { Drive } from "@suaveplan/drive"; import { createNodeFsStorage } from "@suaveplan/storage-file"; import { createStorageUriResolver, type UriPlugin, } from "@suaveplan/storage-uri"; import { afterEach, beforeEach, describe, expect, it, } from "@suaveplan/testing/runner"; import { driveUriPlugin } from "../../src/plugins/drive/drive.js"; interface FileUriOptions { readonly rootDir: string; } function fileUriPlugin(options: FileUriOptions): UriPlugin { const adapter = createNodeFsStorage({ rootDir: options.rootDir, mkdirp: true, }); return { scheme: "file", adapter, parse(uri: string) { // file:///some/path -> key = "some/path" const idx = uri.indexOf("://"); const after = uri.slice(idx + 3); const pathStart = after.indexOf("/"); const path = pathStart >= 0 ? after.slice(pathStart + 1) : after; return { key: path }; }, build(key: string) { return `file:///${key.replace(/^\//, "")}`; }, }; } describe("compose-with-storage-uri integration", () => { let rootDir: string; let driveRoot: string; let drive: Drive; beforeEach(async () => { rootDir = mkdtempSync(join(tmpdir(), "drive-uri-file-")); driveRoot = mkdtempSync(join(tmpdir(), "drive-uri-drive-")); const driveStorage = Object.assign( createNodeFsStorage({ rootDir: driveRoot, mkdirp: true }), { id: "default" } ); drive = new Drive(driveStorage); await drive.initialize(); }); afterEach(async () => { await drive.dispose(); rmSync(rootDir, { recursive: true, force: true }); rmSync(driveRoot, { recursive: true, force: true }); }); it("file:// and drive:// resolve through one StorageUriResolver", { timeout: 30000, }, async () => { const resolver = createStorageUriResolver([ fileUriPlugin({ rootDir }), driveUriPlugin({ drive, driveId: "primary" }), ]); // Seed both backends with distinct bytes. await drive.write("a.bin", new Uint8Array([0xaa])); // Use the file plugin directly to write — the resolver rejects // writes for drive:// (by design) so we exercise the file side via // resolver.write to confirm it works. await resolver.write("file:///b.bin", new Uint8Array([0xbb])); const fileBytes = await resolver.read("file:///b.bin"); const driveBytes = await resolver.read("drive://primary/a.bin"); expect(Array.from(fileBytes)).toEqual([0xbb]); expect(Array.from(driveBytes)).toEqual([0xaa]); // Neither plugin sees the other's state. expect(resolver.hasScheme("file")).toBe(true); expect(resolver.hasScheme("drive")).toBe(true); expect(resolver.schemeCount).toBe(2); }); });