mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-31 03:41:51 +00:00
157 lines
4.5 KiB
TypeScript
157 lines
4.5 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { createEmptyPluginRegistry } from "./registry.js";
|
|
import type { OpenClawPluginService, OpenClawPluginServiceContext } from "./types.js";
|
|
|
|
const mockedLogger = vi.hoisted(() => ({
|
|
info: vi.fn<(msg: string) => void>(),
|
|
warn: vi.fn<(msg: string) => void>(),
|
|
error: vi.fn<(msg: string) => void>(),
|
|
debug: vi.fn<(msg: string) => void>(),
|
|
child: vi.fn(() => mockedLogger),
|
|
}));
|
|
|
|
vi.mock("../logging/subsystem.js", () => ({
|
|
createSubsystemLogger: () => mockedLogger,
|
|
}));
|
|
|
|
import { STATE_DIR } from "../config/paths.js";
|
|
import { startPluginServices } from "./services.js";
|
|
|
|
function createRegistry(services: OpenClawPluginService[]) {
|
|
const registry = createEmptyPluginRegistry();
|
|
registry.services = services.map((service) => ({
|
|
pluginId: "plugin:test",
|
|
service,
|
|
source: "test",
|
|
rootDir: "/plugins/test-plugin",
|
|
})) as typeof registry.services;
|
|
return registry;
|
|
}
|
|
|
|
function createServiceConfig() {
|
|
return {} as Parameters<typeof startPluginServices>[0]["config"];
|
|
}
|
|
|
|
function expectServiceContext(
|
|
ctx: OpenClawPluginServiceContext,
|
|
config: Parameters<typeof startPluginServices>[0]["config"],
|
|
) {
|
|
expect(ctx.config).toBe(config);
|
|
expect(ctx.workspaceDir).toBe("/tmp/workspace");
|
|
expect(ctx.stateDir).toBe(STATE_DIR);
|
|
expectServiceLogger(ctx);
|
|
}
|
|
|
|
function expectServiceLogger(ctx: OpenClawPluginServiceContext) {
|
|
expect(ctx.logger).toBeDefined();
|
|
expect(typeof ctx.logger.info).toBe("function");
|
|
expect(typeof ctx.logger.warn).toBe("function");
|
|
expect(typeof ctx.logger.error).toBe("function");
|
|
}
|
|
|
|
function expectServiceContexts(
|
|
contexts: OpenClawPluginServiceContext[],
|
|
config: Parameters<typeof startPluginServices>[0]["config"],
|
|
) {
|
|
expect(contexts).not.toHaveLength(0);
|
|
contexts.forEach((ctx) => {
|
|
expectServiceContext(ctx, config);
|
|
});
|
|
}
|
|
|
|
function createTrackingService(
|
|
id: string,
|
|
params: {
|
|
starts?: string[];
|
|
stops?: string[];
|
|
contexts?: OpenClawPluginServiceContext[];
|
|
failOnStart?: boolean;
|
|
failOnStop?: boolean;
|
|
stopSpy?: () => void;
|
|
} = {},
|
|
): OpenClawPluginService {
|
|
return {
|
|
id,
|
|
start: (ctx) => {
|
|
if (params.failOnStart) {
|
|
throw new Error("start failed");
|
|
}
|
|
params.starts?.push(id.at(-1) ?? id);
|
|
params.contexts?.push(ctx);
|
|
},
|
|
stop: params.stopSpy
|
|
? () => {
|
|
params.stopSpy?.();
|
|
}
|
|
: params.stops || params.failOnStop
|
|
? () => {
|
|
if (params.failOnStop) {
|
|
throw new Error("stop failed");
|
|
}
|
|
params.stops?.push(id.at(-1) ?? id);
|
|
}
|
|
: undefined,
|
|
};
|
|
}
|
|
|
|
describe("startPluginServices", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("starts services and stops them in reverse order", async () => {
|
|
const starts: string[] = [];
|
|
const stops: string[] = [];
|
|
const contexts: OpenClawPluginServiceContext[] = [];
|
|
|
|
const config = createServiceConfig();
|
|
const handle = await startPluginServices({
|
|
registry: createRegistry([
|
|
createTrackingService("service-a", { starts, stops, contexts }),
|
|
createTrackingService("service-b", { starts, contexts }),
|
|
createTrackingService("service-c", { starts, stops, contexts }),
|
|
]),
|
|
config,
|
|
workspaceDir: "/tmp/workspace",
|
|
});
|
|
await handle.stop();
|
|
|
|
expect(starts).toEqual(["a", "b", "c"]);
|
|
expect(stops).toEqual(["c", "a"]);
|
|
expect(contexts).toHaveLength(3);
|
|
expectServiceContexts(contexts, config);
|
|
});
|
|
|
|
it("logs start/stop failures and continues", async () => {
|
|
const stopOk = vi.fn();
|
|
const stopThrows = vi.fn(() => {
|
|
throw new Error("stop failed");
|
|
});
|
|
|
|
const handle = await startPluginServices({
|
|
registry: createRegistry([
|
|
createTrackingService("service-start-fail", {
|
|
failOnStart: true,
|
|
stopSpy: vi.fn(),
|
|
}),
|
|
createTrackingService("service-ok", { stopSpy: stopOk }),
|
|
createTrackingService("service-stop-fail", { stopSpy: stopThrows }),
|
|
]),
|
|
config: createServiceConfig(),
|
|
});
|
|
|
|
await handle.stop();
|
|
|
|
expect(mockedLogger.error).toHaveBeenCalledWith(
|
|
expect.stringContaining(
|
|
"plugin service failed (service-start-fail, plugin=plugin:test, root=/plugins/test-plugin):",
|
|
),
|
|
);
|
|
expect(mockedLogger.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining("plugin service stop failed (service-stop-fail):"),
|
|
);
|
|
expect(stopOk).toHaveBeenCalledOnce();
|
|
expect(stopThrows).toHaveBeenCalledOnce();
|
|
});
|
|
});
|