109 lines
3.6 KiB
TypeScript
109 lines
3.6 KiB
TypeScript
import { mkdtemp, rm } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { createNodeFsStorage } from "@suaveplan/storage-file";
|
|
import { createHttpStorage } from "@suaveplan/storage-http";
|
|
import {
|
|
afterEach,
|
|
beforeEach,
|
|
describe,
|
|
expect,
|
|
it,
|
|
} from "@suaveplan/testing/runner";
|
|
import type { StorageLike } from "@suaveplan/types";
|
|
import { createStorageUriResolver } from "../../src/index.js";
|
|
import type { UriPlugin } from "../../src/types/plugin.types.js";
|
|
|
|
function fileUriPlugin(adapter: StorageLike, rootDir: string): UriPlugin {
|
|
return {
|
|
scheme: "file",
|
|
adapter,
|
|
parse(uri) {
|
|
// file:///abs/path → key relative to rootDir.
|
|
const prefix = "file://";
|
|
const rest = uri.startsWith(prefix)
|
|
? uri.slice(prefix.length)
|
|
: uri;
|
|
const abs = rest.startsWith("/") ? rest : `/${rest}`;
|
|
const root = rootDir.endsWith("/") ? rootDir : `${rootDir}/`;
|
|
const key = abs.startsWith(root)
|
|
? abs.slice(root.length)
|
|
: abs.replace(/^\//, "");
|
|
return { key };
|
|
},
|
|
build(key) {
|
|
const root = rootDir.endsWith("/") ? rootDir : `${rootDir}/`;
|
|
return `file://${root}${key}`;
|
|
},
|
|
};
|
|
}
|
|
|
|
function httpsUriPlugin(adapter: StorageLike): UriPlugin {
|
|
return {
|
|
scheme: "https",
|
|
adapter,
|
|
parse(uri) {
|
|
const url = new URL(uri);
|
|
return {
|
|
key: url.pathname.replace(/^\//, ""),
|
|
meta: { host: url.host },
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
describe("cross-scheme integration", () => {
|
|
let root: string;
|
|
|
|
beforeEach(async () => {
|
|
root = await mkdtemp(join(tmpdir(), "storage-uri-int-"));
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await rm(root, { recursive: true, force: true });
|
|
});
|
|
|
|
it("composes a resolver with real storage-file and storage-http adapters", {
|
|
timeout: 15000,
|
|
}, async () => {
|
|
const fileAdapter = createNodeFsStorage({ rootDir: root });
|
|
|
|
// In-process fetch stub serving a single payload at a known URL.
|
|
const payload = new TextEncoder().encode("from-http");
|
|
const httpAdapter = createHttpStorage({
|
|
baseUrl: "https://origin.test/",
|
|
fetch: async (input: unknown) => {
|
|
const url =
|
|
typeof input === "string"
|
|
? input
|
|
: input instanceof URL
|
|
? input.toString()
|
|
: input instanceof Request
|
|
? input.url
|
|
: String((input as { url?: string }).url ?? input);
|
|
if (url.endsWith("/poster.bin")) {
|
|
return new Response(payload, { status: 200 });
|
|
}
|
|
return new Response("not found", { status: 404 });
|
|
},
|
|
});
|
|
|
|
const resolver = createStorageUriResolver([
|
|
fileUriPlugin(fileAdapter, root),
|
|
httpsUriPlugin(httpAdapter),
|
|
]);
|
|
|
|
// HTTP read.
|
|
const httpBytes = await resolver.read("https://origin.test/poster.bin");
|
|
expect(new TextDecoder().decode(httpBytes)).toBe("from-http");
|
|
|
|
// File round-trip via resolver.
|
|
const fileUri = `file://${root}/cache/copy.bin`;
|
|
await resolver.write(fileUri, httpBytes);
|
|
const back = await resolver.read(fileUri);
|
|
expect(new TextDecoder().decode(back)).toBe("from-http");
|
|
|
|
expect(await resolver.exists(fileUri)).toBe(true);
|
|
});
|
|
});
|