mirror of https://github.com/openclaw/openclaw.git
refactor: share cron and zalo monitor test helpers
This commit is contained in:
parent
99b274592d
commit
41c9e3ead0
|
|
@ -32,6 +32,41 @@ async function waitForPollingLoopStart(): Promise<void> {
|
|||
await vi.waitFor(() => expect(getUpdatesMock).toHaveBeenCalledTimes(1));
|
||||
}
|
||||
|
||||
const TEST_ACCOUNT = {
|
||||
accountId: "default",
|
||||
config: {},
|
||||
} as unknown as ResolvedZaloAccount;
|
||||
|
||||
const TEST_CONFIG = {} as OpenClawConfig;
|
||||
|
||||
function createLifecycleRuntime() {
|
||||
return {
|
||||
log: vi.fn<(message: string) => void>(),
|
||||
error: vi.fn<(message: string) => void>(),
|
||||
};
|
||||
}
|
||||
|
||||
async function startLifecycleMonitor(
|
||||
options: {
|
||||
useWebhook?: boolean;
|
||||
webhookSecret?: string;
|
||||
webhookUrl?: string;
|
||||
} = {},
|
||||
) {
|
||||
const { monitorZaloProvider } = await import("./monitor.js");
|
||||
const abort = new AbortController();
|
||||
const runtime = createLifecycleRuntime();
|
||||
const run = monitorZaloProvider({
|
||||
token: "test-token",
|
||||
account: TEST_ACCOUNT,
|
||||
config: TEST_CONFIG,
|
||||
runtime,
|
||||
abortSignal: abort.signal,
|
||||
...options,
|
||||
});
|
||||
return { abort, runtime, run };
|
||||
}
|
||||
|
||||
describe("monitorZaloProvider lifecycle", () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
|
@ -39,26 +74,9 @@ describe("monitorZaloProvider lifecycle", () => {
|
|||
});
|
||||
|
||||
it("stays alive in polling mode until abort", async () => {
|
||||
const { monitorZaloProvider } = await import("./monitor.js");
|
||||
const abort = new AbortController();
|
||||
const runtime = {
|
||||
log: vi.fn<(message: string) => void>(),
|
||||
error: vi.fn<(message: string) => void>(),
|
||||
};
|
||||
const account = {
|
||||
accountId: "default",
|
||||
config: {},
|
||||
} as unknown as ResolvedZaloAccount;
|
||||
const config = {} as OpenClawConfig;
|
||||
|
||||
let settled = false;
|
||||
const run = monitorZaloProvider({
|
||||
token: "test-token",
|
||||
account,
|
||||
config,
|
||||
runtime,
|
||||
abortSignal: abort.signal,
|
||||
}).then(() => {
|
||||
const { abort, runtime, run } = await startLifecycleMonitor();
|
||||
const monitoredRun = run.then(() => {
|
||||
settled = true;
|
||||
});
|
||||
|
||||
|
|
@ -70,7 +88,7 @@ describe("monitorZaloProvider lifecycle", () => {
|
|||
expect(settled).toBe(false);
|
||||
|
||||
abort.abort();
|
||||
await run;
|
||||
await monitoredRun;
|
||||
|
||||
expect(settled).toBe(true);
|
||||
expect(runtime.log).toHaveBeenCalledWith(
|
||||
|
|
@ -84,25 +102,7 @@ describe("monitorZaloProvider lifecycle", () => {
|
|||
result: { url: "https://example.com/hooks/zalo" },
|
||||
});
|
||||
|
||||
const { monitorZaloProvider } = await import("./monitor.js");
|
||||
const abort = new AbortController();
|
||||
const runtime = {
|
||||
log: vi.fn<(message: string) => void>(),
|
||||
error: vi.fn<(message: string) => void>(),
|
||||
};
|
||||
const account = {
|
||||
accountId: "default",
|
||||
config: {},
|
||||
} as unknown as ResolvedZaloAccount;
|
||||
const config = {} as OpenClawConfig;
|
||||
|
||||
const run = monitorZaloProvider({
|
||||
token: "test-token",
|
||||
account,
|
||||
config,
|
||||
runtime,
|
||||
abortSignal: abort.signal,
|
||||
});
|
||||
const { abort, runtime, run } = await startLifecycleMonitor();
|
||||
|
||||
await waitForPollingLoopStart();
|
||||
|
||||
|
|
@ -120,25 +120,7 @@ describe("monitorZaloProvider lifecycle", () => {
|
|||
const { ZaloApiError } = await import("./api.js");
|
||||
getWebhookInfoMock.mockRejectedValueOnce(new ZaloApiError("Not Found", 404, "Not Found"));
|
||||
|
||||
const { monitorZaloProvider } = await import("./monitor.js");
|
||||
const abort = new AbortController();
|
||||
const runtime = {
|
||||
log: vi.fn<(message: string) => void>(),
|
||||
error: vi.fn<(message: string) => void>(),
|
||||
};
|
||||
const account = {
|
||||
accountId: "default",
|
||||
config: {},
|
||||
} as unknown as ResolvedZaloAccount;
|
||||
const config = {} as OpenClawConfig;
|
||||
|
||||
const run = monitorZaloProvider({
|
||||
token: "test-token",
|
||||
account,
|
||||
config,
|
||||
runtime,
|
||||
abortSignal: abort.signal,
|
||||
});
|
||||
const { abort, runtime, run } = await startLifecycleMonitor();
|
||||
|
||||
await waitForPollingLoopStart();
|
||||
|
||||
|
|
@ -165,29 +147,13 @@ describe("monitorZaloProvider lifecycle", () => {
|
|||
}),
|
||||
);
|
||||
|
||||
const { monitorZaloProvider } = await import("./monitor.js");
|
||||
const abort = new AbortController();
|
||||
const runtime = {
|
||||
log: vi.fn<(message: string) => void>(),
|
||||
error: vi.fn<(message: string) => void>(),
|
||||
};
|
||||
const account = {
|
||||
accountId: "default",
|
||||
config: {},
|
||||
} as unknown as ResolvedZaloAccount;
|
||||
const config = {} as OpenClawConfig;
|
||||
|
||||
let settled = false;
|
||||
const run = monitorZaloProvider({
|
||||
token: "test-token",
|
||||
account,
|
||||
config,
|
||||
runtime,
|
||||
abortSignal: abort.signal,
|
||||
const { abort, runtime, run } = await startLifecycleMonitor({
|
||||
useWebhook: true,
|
||||
webhookUrl: "https://example.com/hooks/zalo",
|
||||
webhookSecret: "supersecret", // pragma: allowlist secret
|
||||
}).then(() => {
|
||||
});
|
||||
const monitoredRun = run.then(() => {
|
||||
settled = true;
|
||||
});
|
||||
|
||||
|
|
@ -202,7 +168,7 @@ describe("monitorZaloProvider lifecycle", () => {
|
|||
expect(registry.httpRoutes).toHaveLength(1);
|
||||
|
||||
resolveDeleteWebhook?.();
|
||||
await run;
|
||||
await monitoredRun;
|
||||
|
||||
expect(settled).toBe(true);
|
||||
expect(registry.httpRoutes).toHaveLength(0);
|
||||
|
|
|
|||
|
|
@ -14,169 +14,102 @@ import {
|
|||
|
||||
const runCronIsolatedAgentTurn = await loadRunCronIsolatedAgentTurn();
|
||||
|
||||
const OPENAI_GPT4_MODEL = "openai/gpt-4";
|
||||
|
||||
function mockSuccessfulModelFallback() {
|
||||
runWithModelFallbackMock.mockImplementation(async ({ provider, model, run }) => {
|
||||
await run(provider, model);
|
||||
return {
|
||||
result: {
|
||||
payloads: [{ text: "ok" }],
|
||||
meta: { agentMeta: { usage: { input: 10, output: 20 } } },
|
||||
},
|
||||
provider,
|
||||
model,
|
||||
attempts: [],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function runFastModeCase(params: {
|
||||
configFastMode: boolean;
|
||||
expectedFastMode: boolean;
|
||||
message: string;
|
||||
sessionFastMode?: boolean;
|
||||
}) {
|
||||
const baseSession = makeCronSession();
|
||||
resolveCronSessionMock.mockReturnValue(
|
||||
params.sessionFastMode === undefined
|
||||
? baseSession
|
||||
: makeCronSession({
|
||||
sessionEntry: {
|
||||
...baseSession.sessionEntry,
|
||||
fastMode: params.sessionFastMode,
|
||||
},
|
||||
}),
|
||||
);
|
||||
mockSuccessfulModelFallback();
|
||||
|
||||
const result = await runCronIsolatedAgentTurn(
|
||||
makeIsolatedAgentTurnParams({
|
||||
cfg: {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
[OPENAI_GPT4_MODEL]: {
|
||||
params: {
|
||||
fastMode: params.configFastMode,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
job: makeIsolatedAgentTurnJob({
|
||||
payload: {
|
||||
kind: "agentTurn",
|
||||
message: params.message,
|
||||
model: OPENAI_GPT4_MODEL,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.status).toBe("ok");
|
||||
expect(runEmbeddedPiAgentMock).toHaveBeenCalledOnce();
|
||||
expect(runEmbeddedPiAgentMock.mock.calls[0][0]).toMatchObject({
|
||||
provider: "openai",
|
||||
model: "gpt-4",
|
||||
fastMode: params.expectedFastMode,
|
||||
});
|
||||
}
|
||||
|
||||
describe("runCronIsolatedAgentTurn — fast mode", () => {
|
||||
setupRunCronIsolatedAgentTurnSuite();
|
||||
|
||||
it("passes config-driven fast mode into embedded cron runs", async () => {
|
||||
const cronSession = makeCronSession();
|
||||
resolveCronSessionMock.mockReturnValue(cronSession);
|
||||
|
||||
runWithModelFallbackMock.mockImplementation(async ({ provider, model, run }) => {
|
||||
await run(provider, model);
|
||||
return {
|
||||
result: {
|
||||
payloads: [{ text: "ok" }],
|
||||
meta: { agentMeta: { usage: { input: 10, output: 20 } } },
|
||||
},
|
||||
provider,
|
||||
model,
|
||||
attempts: [],
|
||||
};
|
||||
});
|
||||
|
||||
const result = await runCronIsolatedAgentTurn(
|
||||
makeIsolatedAgentTurnParams({
|
||||
cfg: {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openai/gpt-4": {
|
||||
params: {
|
||||
fastMode: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
job: makeIsolatedAgentTurnJob({
|
||||
payload: {
|
||||
kind: "agentTurn",
|
||||
await runFastModeCase({
|
||||
configFastMode: true,
|
||||
expectedFastMode: true,
|
||||
message: "test fast mode",
|
||||
model: "openai/gpt-4",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.status).toBe("ok");
|
||||
expect(runEmbeddedPiAgentMock).toHaveBeenCalledOnce();
|
||||
expect(runEmbeddedPiAgentMock.mock.calls[0][0]).toMatchObject({
|
||||
provider: "openai",
|
||||
model: "gpt-4",
|
||||
fastMode: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("honors session fastMode=false over config fastMode=true", async () => {
|
||||
const cronSession = makeCronSession({
|
||||
sessionEntry: {
|
||||
...makeCronSession().sessionEntry,
|
||||
fastMode: false,
|
||||
},
|
||||
});
|
||||
resolveCronSessionMock.mockReturnValue(cronSession);
|
||||
|
||||
runWithModelFallbackMock.mockImplementation(async ({ provider, model, run }) => {
|
||||
await run(provider, model);
|
||||
return {
|
||||
result: {
|
||||
payloads: [{ text: "ok" }],
|
||||
meta: { agentMeta: { usage: { input: 10, output: 20 } } },
|
||||
},
|
||||
provider,
|
||||
model,
|
||||
attempts: [],
|
||||
};
|
||||
});
|
||||
|
||||
const result = await runCronIsolatedAgentTurn(
|
||||
makeIsolatedAgentTurnParams({
|
||||
cfg: {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openai/gpt-4": {
|
||||
params: {
|
||||
fastMode: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
job: makeIsolatedAgentTurnJob({
|
||||
payload: {
|
||||
kind: "agentTurn",
|
||||
await runFastModeCase({
|
||||
configFastMode: true,
|
||||
expectedFastMode: false,
|
||||
message: "test fast mode override",
|
||||
model: "openai/gpt-4",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.status).toBe("ok");
|
||||
expect(runEmbeddedPiAgentMock).toHaveBeenCalledOnce();
|
||||
expect(runEmbeddedPiAgentMock.mock.calls[0][0]).toMatchObject({
|
||||
provider: "openai",
|
||||
model: "gpt-4",
|
||||
fastMode: false,
|
||||
sessionFastMode: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("honors session fastMode=true over config fastMode=false", async () => {
|
||||
const cronSession = makeCronSession({
|
||||
sessionEntry: {
|
||||
...makeCronSession().sessionEntry,
|
||||
fastMode: true,
|
||||
},
|
||||
});
|
||||
resolveCronSessionMock.mockReturnValue(cronSession);
|
||||
|
||||
runWithModelFallbackMock.mockImplementation(async ({ provider, model, run }) => {
|
||||
await run(provider, model);
|
||||
return {
|
||||
result: {
|
||||
payloads: [{ text: "ok" }],
|
||||
meta: { agentMeta: { usage: { input: 10, output: 20 } } },
|
||||
},
|
||||
provider,
|
||||
model,
|
||||
attempts: [],
|
||||
};
|
||||
});
|
||||
|
||||
const result = await runCronIsolatedAgentTurn(
|
||||
makeIsolatedAgentTurnParams({
|
||||
cfg: {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openai/gpt-4": {
|
||||
params: {
|
||||
fastMode: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
job: makeIsolatedAgentTurnJob({
|
||||
payload: {
|
||||
kind: "agentTurn",
|
||||
await runFastModeCase({
|
||||
configFastMode: false,
|
||||
expectedFastMode: true,
|
||||
message: "test fast mode session override",
|
||||
model: "openai/gpt-4",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.status).toBe("ok");
|
||||
expect(runEmbeddedPiAgentMock).toHaveBeenCalledOnce();
|
||||
expect(runEmbeddedPiAgentMock.mock.calls[0][0]).toMatchObject({
|
||||
provider: "openai",
|
||||
model: "gpt-4",
|
||||
fastMode: true,
|
||||
sessionFastMode: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue