mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 16:38:19 +01:00
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 45s
Build Docker Images / lint (push) Successful in 27s
Build Docker Images / check (push) Successful in 1m7s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 12s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 32m31s
222 lines
7.3 KiB
TypeScript
222 lines
7.3 KiB
TypeScript
/* import {
|
|
afterEach,
|
|
beforeEach,
|
|
describe,
|
|
expect,
|
|
jest,
|
|
mock,
|
|
test,
|
|
} from "bun:test";
|
|
import { ZodError, type ZodTypeAny, z } from "zod";
|
|
import { Plugin } from "~/packages/plugin-kit";
|
|
import { type Manifest, manifestSchema } from "~/packages/plugin-kit/schema";
|
|
import { PluginLoader } from "./loader.ts";
|
|
|
|
const mockReaddir = jest.fn();
|
|
const mockGetLogger = jest.fn(() => ({
|
|
fatal: jest.fn(),
|
|
}));
|
|
const mockParseJSON5 = jest.fn();
|
|
const mockParseJSONC = jest.fn();
|
|
const mockFromZodError = jest.fn();
|
|
|
|
mock.module("node:fs/promises", () => ({
|
|
readdir: mockReaddir,
|
|
}));
|
|
|
|
mock.module("@logtape/logtape", () => ({
|
|
getLogger: mockGetLogger,
|
|
}));
|
|
|
|
mock.module("confbox", () => ({
|
|
parseJSON5: mockParseJSON5,
|
|
parseJSONC: mockParseJSONC,
|
|
}));
|
|
|
|
mock.module("zod-validation-error", () => ({
|
|
fromZodError: mockFromZodError,
|
|
}));
|
|
|
|
describe("PluginLoader", () => {
|
|
let pluginLoader: PluginLoader;
|
|
|
|
beforeEach(() => {
|
|
pluginLoader = new PluginLoader();
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
test("getDirectories should return directories", async () => {
|
|
mockReaddir.mockResolvedValue([
|
|
{ name: "dir1", isDirectory: (): true => true },
|
|
{ name: "file1", isDirectory: (): false => false },
|
|
{ name: "dir2", isDirectory: (): true => true },
|
|
]);
|
|
|
|
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
|
const directories = await PluginLoader["getDirectories"]("/some/path");
|
|
expect(directories).toEqual(["dir1", "dir2"]);
|
|
});
|
|
|
|
test("findManifestFile should return manifest file if found", async () => {
|
|
mockReaddir.mockResolvedValue(["manifest.json", "otherfile.txt"]);
|
|
|
|
const manifestFile =
|
|
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
|
await PluginLoader["findManifestFile"]("/some/path");
|
|
expect(manifestFile).toBe("manifest.json");
|
|
});
|
|
|
|
test("hasEntrypoint should return true if entrypoint file is found", async () => {
|
|
mockReaddir.mockResolvedValue(["index.ts", "otherfile.txt"]);
|
|
|
|
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
|
const hasEntrypoint = await PluginLoader["hasEntrypoint"]("/some/path");
|
|
expect(hasEntrypoint).toBe(true);
|
|
});
|
|
|
|
test("parseManifestFile should parse JSON manifest", async () => {
|
|
const manifestContent = { name: "test-plugin" };
|
|
Bun.file = jest.fn().mockReturnValue({
|
|
text: (): Promise<string> =>
|
|
Promise.resolve(JSON.stringify(manifestContent)),
|
|
});
|
|
|
|
// biome-ignore lint/complexity/useLiteralKeys: Private method
|
|
const manifest = await pluginLoader["parseManifestFile"](
|
|
"/some/path/manifest.json",
|
|
"manifest.json",
|
|
);
|
|
expect(manifest).toEqual(manifestContent);
|
|
});
|
|
|
|
test("findPlugins should return plugin directories with valid manifest and entrypoint", async () => {
|
|
mockReaddir
|
|
.mockResolvedValueOnce([
|
|
{ name: "plugin1", isDirectory: (): true => true },
|
|
{ name: "plugin2", isDirectory: (): true => true },
|
|
])
|
|
.mockResolvedValue(["manifest.json", "index.ts"]);
|
|
|
|
const plugins = await PluginLoader.findPlugins("/some/path");
|
|
expect(plugins).toEqual(["plugin1", "plugin2"]);
|
|
});
|
|
|
|
test("parseManifest should parse and validate manifest", async () => {
|
|
const manifestContent: Manifest = {
|
|
name: "test-plugin",
|
|
version: "1.1.0",
|
|
description: "Doobaee",
|
|
};
|
|
mockReaddir.mockResolvedValue(["manifest.json"]);
|
|
Bun.file = jest.fn().mockReturnValue({
|
|
text: (): Promise<string> =>
|
|
Promise.resolve(JSON.stringify(manifestContent)),
|
|
});
|
|
manifestSchema.safeParseAsync = jest.fn().mockResolvedValue({
|
|
success: true,
|
|
data: manifestContent,
|
|
});
|
|
|
|
const manifest = await pluginLoader.parseManifest(
|
|
"/some/path",
|
|
"plugin1",
|
|
);
|
|
expect(manifest).toEqual(manifestContent);
|
|
});
|
|
|
|
test("parseManifest should throw error if manifest is missing", async () => {
|
|
mockReaddir.mockResolvedValue([]);
|
|
|
|
await expect(
|
|
pluginLoader.parseManifest("/some/path", "plugin1"),
|
|
).rejects.toThrow("Plugin plugin1 is missing a manifest file");
|
|
});
|
|
|
|
test("parseManifest should throw error if manifest is invalid", async () => {
|
|
// @ts-expect-error trying to cause a type error here
|
|
const manifestContent: Manifest = {
|
|
name: "test-plugin",
|
|
version: "1.1.0",
|
|
};
|
|
mockReaddir.mockResolvedValue(["manifest.json"]);
|
|
Bun.file = jest.fn().mockReturnValue({
|
|
text: (): Promise<string> =>
|
|
Promise.resolve(JSON.stringify(manifestContent)),
|
|
});
|
|
manifestSchema.safeParseAsync = jest.fn().mockResolvedValue({
|
|
success: false,
|
|
error: new ZodError([]),
|
|
});
|
|
|
|
await expect(
|
|
pluginLoader.parseManifest("/some/path", "plugin1"),
|
|
).rejects.toThrow();
|
|
});
|
|
|
|
test("loadPlugin should load and return a Plugin instance", async () => {
|
|
const mockPlugin = new Plugin(z.object({}));
|
|
mock.module("/some/path/index.ts", () => ({
|
|
default: mockPlugin,
|
|
}));
|
|
|
|
const plugin = await pluginLoader.loadPlugin("/some/path", "index.ts");
|
|
expect(plugin).toBeInstanceOf(Plugin);
|
|
});
|
|
|
|
test("loadPlugin should throw error if default export is not a Plugin", async () => {
|
|
mock.module("/some/path/index.ts", () => ({
|
|
default: "cheese",
|
|
}));
|
|
|
|
await expect(
|
|
pluginLoader.loadPlugin("/some/path", "index.ts"),
|
|
).rejects.toThrow("Entrypoint is not a Plugin");
|
|
});
|
|
|
|
test("loadPlugins should load all plugins in a directory", async () => {
|
|
const manifestContent: Manifest = {
|
|
name: "test-plugin",
|
|
version: "1.1.0",
|
|
description: "Doobaee",
|
|
};
|
|
const mockPlugin = new Plugin(z.object({}));
|
|
|
|
mockReaddir
|
|
.mockResolvedValueOnce([
|
|
{ name: "plugin1", isDirectory: (): true => true },
|
|
{ name: "plugin2", isDirectory: (): true => true },
|
|
])
|
|
.mockResolvedValue(["manifest.json", "index.ts"]);
|
|
Bun.file = jest.fn().mockReturnValue({
|
|
text: (): Promise<string> =>
|
|
Promise.resolve(JSON.stringify(manifestContent)),
|
|
});
|
|
manifestSchema.safeParseAsync = jest.fn().mockResolvedValue({
|
|
success: true,
|
|
data: manifestContent,
|
|
});
|
|
mock.module("/some/path/plugin1/index", () => ({
|
|
default: mockPlugin,
|
|
}));
|
|
mock.module("/some/path/plugin2/index", () => ({
|
|
default: mockPlugin,
|
|
}));
|
|
|
|
const plugins = await pluginLoader.loadPlugins("/some/path", true);
|
|
expect(plugins).toEqual([
|
|
{
|
|
manifest: manifestContent,
|
|
plugin: mockPlugin as unknown as Plugin<ZodTypeAny>,
|
|
},
|
|
{
|
|
manifest: manifestContent,
|
|
plugin: mockPlugin as unknown as Plugin<ZodTypeAny>,
|
|
},
|
|
]);
|
|
});
|
|
});
|
|
*/
|