mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-17 13:00:48 +00:00
Address Greptile review: add test coverage for runServiceStart path. The error message copy-paste issue was already fixed in the DRY refactor (uses params.serviceNoun instead of hardcoded 'restart').
207 lines
5.3 KiB
TypeScript
207 lines
5.3 KiB
TypeScript
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const readConfigFileSnapshotMock = vi.fn();
|
|
const loadConfig = vi.fn(() => ({}));
|
|
|
|
const runtimeLogs: string[] = [];
|
|
const defaultRuntime = {
|
|
log: (message: string) => runtimeLogs.push(message),
|
|
error: vi.fn(),
|
|
exit: (code: number) => {
|
|
throw new Error(`__exit__:${code}`);
|
|
},
|
|
};
|
|
|
|
const service = {
|
|
label: "TestService",
|
|
loadedText: "loaded",
|
|
notLoadedText: "not loaded",
|
|
install: vi.fn(),
|
|
uninstall: vi.fn(),
|
|
stop: vi.fn(),
|
|
isLoaded: vi.fn(),
|
|
readCommand: vi.fn(),
|
|
readRuntime: vi.fn(),
|
|
restart: vi.fn(),
|
|
};
|
|
|
|
vi.mock("../../config/config.js", () => ({
|
|
loadConfig: () => loadConfig(),
|
|
readConfigFileSnapshot: () => readConfigFileSnapshotMock(),
|
|
}));
|
|
|
|
vi.mock("../../config/issue-format.js", () => ({
|
|
formatConfigIssueLines: (
|
|
issues: Array<{ path: string; message: string }>,
|
|
_prefix: string,
|
|
_opts?: unknown,
|
|
) => issues.map((i) => `${i.path}: ${i.message}`),
|
|
}));
|
|
|
|
vi.mock("../../runtime.js", () => ({
|
|
defaultRuntime,
|
|
}));
|
|
|
|
describe("runServiceRestart config pre-flight (#35862)", () => {
|
|
let runServiceRestart: typeof import("./lifecycle-core.js").runServiceRestart;
|
|
|
|
beforeAll(async () => {
|
|
({ runServiceRestart } = await import("./lifecycle-core.js"));
|
|
});
|
|
|
|
beforeEach(() => {
|
|
runtimeLogs.length = 0;
|
|
readConfigFileSnapshotMock.mockReset();
|
|
readConfigFileSnapshotMock.mockResolvedValue({
|
|
exists: true,
|
|
valid: true,
|
|
config: {},
|
|
issues: [],
|
|
});
|
|
loadConfig.mockReset();
|
|
loadConfig.mockReturnValue({});
|
|
service.isLoaded.mockClear();
|
|
service.readCommand.mockClear();
|
|
service.restart.mockClear();
|
|
service.isLoaded.mockResolvedValue(true);
|
|
service.readCommand.mockResolvedValue({ environment: {} });
|
|
service.restart.mockResolvedValue(undefined);
|
|
vi.unstubAllEnvs();
|
|
vi.stubEnv("OPENCLAW_GATEWAY_TOKEN", "");
|
|
vi.stubEnv("CLAWDBOT_GATEWAY_TOKEN", "");
|
|
});
|
|
|
|
it("aborts restart when config is invalid", async () => {
|
|
readConfigFileSnapshotMock.mockResolvedValue({
|
|
exists: true,
|
|
valid: false,
|
|
config: {},
|
|
issues: [{ path: "agents.defaults.pdfModel", message: "Unrecognized key" }],
|
|
});
|
|
|
|
await expect(
|
|
runServiceRestart({
|
|
serviceNoun: "Gateway",
|
|
service,
|
|
renderStartHints: () => [],
|
|
opts: { json: true },
|
|
}),
|
|
).rejects.toThrow("__exit__:1");
|
|
|
|
expect(service.restart).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("proceeds with restart when config is valid", async () => {
|
|
readConfigFileSnapshotMock.mockResolvedValue({
|
|
exists: true,
|
|
valid: true,
|
|
config: {},
|
|
issues: [],
|
|
});
|
|
|
|
const result = await runServiceRestart({
|
|
serviceNoun: "Gateway",
|
|
service,
|
|
renderStartHints: () => [],
|
|
opts: { json: true },
|
|
});
|
|
|
|
expect(result).toBe(true);
|
|
expect(service.restart).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("proceeds with restart when config file does not exist", async () => {
|
|
readConfigFileSnapshotMock.mockResolvedValue({
|
|
exists: false,
|
|
valid: true,
|
|
config: {},
|
|
issues: [],
|
|
});
|
|
|
|
const result = await runServiceRestart({
|
|
serviceNoun: "Gateway",
|
|
service,
|
|
renderStartHints: () => [],
|
|
opts: { json: true },
|
|
});
|
|
|
|
expect(result).toBe(true);
|
|
expect(service.restart).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("proceeds with restart when snapshot read throws", async () => {
|
|
readConfigFileSnapshotMock.mockRejectedValue(new Error("read failed"));
|
|
|
|
const result = await runServiceRestart({
|
|
serviceNoun: "Gateway",
|
|
service,
|
|
renderStartHints: () => [],
|
|
opts: { json: true },
|
|
});
|
|
|
|
expect(result).toBe(true);
|
|
expect(service.restart).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe("runServiceStart config pre-flight (#35862)", () => {
|
|
let runServiceStart: typeof import("./lifecycle-core.js").runServiceStart;
|
|
|
|
beforeAll(async () => {
|
|
({ runServiceStart } = await import("./lifecycle-core.js"));
|
|
});
|
|
|
|
beforeEach(() => {
|
|
runtimeLogs.length = 0;
|
|
readConfigFileSnapshotMock.mockReset();
|
|
readConfigFileSnapshotMock.mockResolvedValue({
|
|
exists: true,
|
|
valid: true,
|
|
config: {},
|
|
issues: [],
|
|
});
|
|
service.isLoaded.mockClear();
|
|
service.restart.mockClear();
|
|
service.isLoaded.mockResolvedValue(true);
|
|
service.restart.mockResolvedValue(undefined);
|
|
});
|
|
|
|
it("aborts start when config is invalid", async () => {
|
|
readConfigFileSnapshotMock.mockResolvedValue({
|
|
exists: true,
|
|
valid: false,
|
|
config: {},
|
|
issues: [{ path: "agents.defaults.pdfModel", message: "Unrecognized key" }],
|
|
});
|
|
|
|
await expect(
|
|
runServiceStart({
|
|
serviceNoun: "Gateway",
|
|
service,
|
|
renderStartHints: () => [],
|
|
opts: { json: true },
|
|
}),
|
|
).rejects.toThrow("__exit__:1");
|
|
|
|
expect(service.restart).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("proceeds with start when config is valid", async () => {
|
|
readConfigFileSnapshotMock.mockResolvedValue({
|
|
exists: true,
|
|
valid: true,
|
|
config: {},
|
|
issues: [],
|
|
});
|
|
|
|
await runServiceStart({
|
|
serviceNoun: "Gateway",
|
|
service,
|
|
renderStartHints: () => [],
|
|
opts: { json: true },
|
|
});
|
|
|
|
expect(service.restart).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|