235 lines · 7.1 KB · TypeScript
RawDownload
import { describe, it, expect, beforeAll, afterAll } from "@effect/vitest";import * as fs from "node:fs";import * as path from "node:path"; import type { AnyPlugin } from "@executor-js/sdk";import { loadPluginsFromJsonc } from "./load-plugins"; // Fixtures are generated under// packages/core/config/__test-fixtures__/tmp/node_modules. The tmp jsonc// files live beside that directory, so Node's `require.resolve` walks up// from each config file and lands on these package-shaped test fixtures.const FIXTURES_ROOT = path.resolve(__dirname, "..", "__test-fixtures__");const TMP_ROOT = path.join(FIXTURES_ROOT, "tmp");const FIXTURE_PACKAGES_ROOT = path.join(TMP_ROOT, "node_modules", "@loader-fixture"); // Single typed boundary for the loader's runtime-erased `AnyPlugin[]`:// fixture plugins are hand-rolled JS objects whose extra fields (id,// packageName, __optionsReceived) only matter to these tests, not to the// loader. Centralising the bridge here keeps every assertion in the// suite working off a precise type instead of repeating cast noise.interface FixturePlugin {  readonly id: string;  readonly packageName: string;  readonly __optionsReceived: Record<string, unknown> | null;} const asFixturePlugins = (plugins: readonly AnyPlugin[] | null): readonly FixturePlugin[] => {  // Fixture servers return shapes wider than AnyPlugin (they carry  // __optionsReceived for test assertions); narrow once here.  // oxlint-disable-next-line executor/no-double-cast  return plugins as unknown as readonly FixturePlugin[];}; const writeJsonc = (name: string, body: string): string => {  const dir = fs.mkdtempSync(path.join(TMP_ROOT, `${name}-`));  const file = path.join(dir, "executor.jsonc");  fs.writeFileSync(file, body);  return file;}; const writeFixturePackage = (name: string, server: string): void => {  const packageName = `@loader-fixture/${name}`;  const dir = path.join(FIXTURE_PACKAGES_ROOT, name);  fs.mkdirSync(dir, { recursive: true });  fs.writeFileSync(    path.join(dir, "package.json"),    JSON.stringify(      {        name: packageName,        version: "0.0.0",        private: true,        type: "module",        exports: {          "./server": "./server.js",        },      },      null,      2,    ),  );  fs.writeFileSync(path.join(dir, "server.js"), server);}; const writeFixturePackages = (): void => {  writeFixturePackage(    "plugin-alpha",    `export default (options) => ({  id: "alpha",  packageName: "@loader-fixture/plugin-alpha",  __optionsReceived: options ?? null,});`,  );  writeFixturePackage(    "plugin-beta",    `export default (options) => ({  id: "beta",  packageName: "@loader-fixture/plugin-beta",  __optionsReceived: options ?? null,});`,  );  writeFixturePackage("plugin-bad", `export default { not: "a function" };\n`);}; beforeAll(() => {  fs.rmSync(TMP_ROOT, { recursive: true, force: true });  fs.mkdirSync(TMP_ROOT, { recursive: true });  writeFixturePackages();}); afterAll(() => {  fs.rmSync(TMP_ROOT, { recursive: true, force: true });}); describe("loadPluginsFromJsonc", () => {  it("returns null when the jsonc file does not exist", async () => {    const plugins = await loadPluginsFromJsonc({      path: path.join(TMP_ROOT, "nonexistent-config.jsonc"),    });    expect(plugins).toBeNull();  });   it("returns null when the file has no `plugins` key", async () => {    const file = writeJsonc("no-plugins-key", `{ "name": "demo" }`);    const plugins = await loadPluginsFromJsonc({ path: file });    expect(plugins).toBeNull();  });   it("returns null for an empty plugins array", async () => {    const file = writeJsonc("empty-plugins", `{ "plugins": [] }`);    const plugins = await loadPluginsFromJsonc({ path: file });    expect(plugins).toBeNull();  });   it("throws on JSONC parse errors", async () => {    const file = writeJsonc("parse-error", `{ "plugins": [`);    await expect(loadPluginsFromJsonc({ path: file })).rejects.toThrow(/failed to parse/i);  });   it("throws with a helpful message when the package can't be resolved", async () => {    const file = writeJsonc(      "unresolved",      `{        "plugins": [          { "package": "@loader-fixture/does-not-exist" }        ]      }`,    );    await expect(loadPluginsFromJsonc({ path: file })).rejects.toThrow(      /cannot resolve "@loader-fixture\/does-not-exist\/server"/,    );  });   it("throws when the resolved module has no callable factory", async () => {    const file = writeJsonc(      "no-factory",      `{        "plugins": [          { "package": "@loader-fixture/plugin-bad" }        ]      }`,    );    await expect(loadPluginsFromJsonc({ path: file })).rejects.toThrow(      /did not export a default[\s\S]*definePlugin/,    );  });   it("loads a plugin and exposes the package's id + packageName", async () => {    const file = writeJsonc(      "happy-path",      `{        "plugins": [          { "package": "@loader-fixture/plugin-alpha" }        ]      }`,    );    const plugins = asFixturePlugins(await loadPluginsFromJsonc({ path: file }));    expect(plugins).toHaveLength(1);    expect(plugins[0]!.id).toBe("alpha");    expect(plugins[0]!.packageName).toBe("@loader-fixture/plugin-alpha");  });   it("forwards merged deps + options to the factory; entry options win on conflict", async () => {    const file = writeJsonc(      "merge-options",      `{        "plugins": [          {            "package": "@loader-fixture/plugin-alpha",            "options": { "from": "options", "shared": "options-wins" }          }        ]      }`,    );    const plugins = asFixturePlugins(      await loadPluginsFromJsonc({        path: file,        deps: { from: "deps", shared: "deps-loses", onlyDep: 42 },      }),    );    expect(plugins[0]!.__optionsReceived).toEqual({      from: "options",      shared: "options-wins",      onlyDep: 42,    });  });   it("calls the factory with deps only when no options are declared", async () => {    const file = writeJsonc(      "deps-only",      `{        "plugins": [          { "package": "@loader-fixture/plugin-alpha" }        ]      }`,    );    const plugins = asFixturePlugins(      await loadPluginsFromJsonc({        path: file,        deps: { configFile: "stub-sink" },      }),    );    expect(plugins[0]!.__optionsReceived).toEqual({ configFile: "stub-sink" });  });   it("loads multiple plugins in declared order", async () => {    const file = writeJsonc(      "ordered",      `{        "plugins": [          { "package": "@loader-fixture/plugin-beta" },          { "package": "@loader-fixture/plugin-alpha" }        ]      }`,    );    const plugins = asFixturePlugins(await loadPluginsFromJsonc({ path: file }));    expect(plugins.map((p) => p.id)).toEqual(["beta", "alpha"]);  });   it("accepts line and block comments (jsonc semantics)", async () => {    const file = writeJsonc(      "jsonc-syntax",      `{        // pick the alpha fixture        "plugins": [          /* one entry */          { "package": "@loader-fixture/plugin-alpha" }        ]      }`,    );    const plugins = await loadPluginsFromJsonc({ path: file });    expect(plugins).toHaveLength(1);  });});