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));
|
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", () => {
|
describe("monitorZaloProvider lifecycle", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
|
|
@ -39,26 +74,9 @@ describe("monitorZaloProvider lifecycle", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("stays alive in polling mode until abort", async () => {
|
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;
|
let settled = false;
|
||||||
const run = monitorZaloProvider({
|
const { abort, runtime, run } = await startLifecycleMonitor();
|
||||||
token: "test-token",
|
const monitoredRun = run.then(() => {
|
||||||
account,
|
|
||||||
config,
|
|
||||||
runtime,
|
|
||||||
abortSignal: abort.signal,
|
|
||||||
}).then(() => {
|
|
||||||
settled = true;
|
settled = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -70,7 +88,7 @@ describe("monitorZaloProvider lifecycle", () => {
|
||||||
expect(settled).toBe(false);
|
expect(settled).toBe(false);
|
||||||
|
|
||||||
abort.abort();
|
abort.abort();
|
||||||
await run;
|
await monitoredRun;
|
||||||
|
|
||||||
expect(settled).toBe(true);
|
expect(settled).toBe(true);
|
||||||
expect(runtime.log).toHaveBeenCalledWith(
|
expect(runtime.log).toHaveBeenCalledWith(
|
||||||
|
|
@ -84,25 +102,7 @@ describe("monitorZaloProvider lifecycle", () => {
|
||||||
result: { url: "https://example.com/hooks/zalo" },
|
result: { url: "https://example.com/hooks/zalo" },
|
||||||
});
|
});
|
||||||
|
|
||||||
const { monitorZaloProvider } = await import("./monitor.js");
|
const { abort, runtime, run } = await startLifecycleMonitor();
|
||||||
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,
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitForPollingLoopStart();
|
await waitForPollingLoopStart();
|
||||||
|
|
||||||
|
|
@ -120,25 +120,7 @@ describe("monitorZaloProvider lifecycle", () => {
|
||||||
const { ZaloApiError } = await import("./api.js");
|
const { ZaloApiError } = await import("./api.js");
|
||||||
getWebhookInfoMock.mockRejectedValueOnce(new ZaloApiError("Not Found", 404, "Not Found"));
|
getWebhookInfoMock.mockRejectedValueOnce(new ZaloApiError("Not Found", 404, "Not Found"));
|
||||||
|
|
||||||
const { monitorZaloProvider } = await import("./monitor.js");
|
const { abort, runtime, run } = await startLifecycleMonitor();
|
||||||
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,
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitForPollingLoopStart();
|
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;
|
let settled = false;
|
||||||
const run = monitorZaloProvider({
|
const { abort, runtime, run } = await startLifecycleMonitor({
|
||||||
token: "test-token",
|
|
||||||
account,
|
|
||||||
config,
|
|
||||||
runtime,
|
|
||||||
abortSignal: abort.signal,
|
|
||||||
useWebhook: true,
|
useWebhook: true,
|
||||||
webhookUrl: "https://example.com/hooks/zalo",
|
webhookUrl: "https://example.com/hooks/zalo",
|
||||||
webhookSecret: "supersecret", // pragma: allowlist secret
|
webhookSecret: "supersecret", // pragma: allowlist secret
|
||||||
}).then(() => {
|
});
|
||||||
|
const monitoredRun = run.then(() => {
|
||||||
settled = true;
|
settled = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -202,7 +168,7 @@ describe("monitorZaloProvider lifecycle", () => {
|
||||||
expect(registry.httpRoutes).toHaveLength(1);
|
expect(registry.httpRoutes).toHaveLength(1);
|
||||||
|
|
||||||
resolveDeleteWebhook?.();
|
resolveDeleteWebhook?.();
|
||||||
await run;
|
await monitoredRun;
|
||||||
|
|
||||||
expect(settled).toBe(true);
|
expect(settled).toBe(true);
|
||||||
expect(registry.httpRoutes).toHaveLength(0);
|
expect(registry.httpRoutes).toHaveLength(0);
|
||||||
|
|
|
||||||
|
|
@ -14,169 +14,102 @@ import {
|
||||||
|
|
||||||
const runCronIsolatedAgentTurn = await loadRunCronIsolatedAgentTurn();
|
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", () => {
|
describe("runCronIsolatedAgentTurn — fast mode", () => {
|
||||||
setupRunCronIsolatedAgentTurnSuite();
|
setupRunCronIsolatedAgentTurnSuite();
|
||||||
|
|
||||||
it("passes config-driven fast mode into embedded cron runs", async () => {
|
it("passes config-driven fast mode into embedded cron runs", async () => {
|
||||||
const cronSession = makeCronSession();
|
await runFastModeCase({
|
||||||
resolveCronSessionMock.mockReturnValue(cronSession);
|
configFastMode: true,
|
||||||
|
expectedFastMode: true,
|
||||||
runWithModelFallbackMock.mockImplementation(async ({ provider, model, run }) => {
|
message: "test fast mode",
|
||||||
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",
|
|
||||||
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 () => {
|
it("honors session fastMode=false over config fastMode=true", async () => {
|
||||||
const cronSession = makeCronSession({
|
await runFastModeCase({
|
||||||
sessionEntry: {
|
configFastMode: true,
|
||||||
...makeCronSession().sessionEntry,
|
expectedFastMode: false,
|
||||||
fastMode: false,
|
message: "test fast mode override",
|
||||||
},
|
sessionFastMode: 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",
|
|
||||||
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,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("honors session fastMode=true over config fastMode=false", async () => {
|
it("honors session fastMode=true over config fastMode=false", async () => {
|
||||||
const cronSession = makeCronSession({
|
await runFastModeCase({
|
||||||
sessionEntry: {
|
configFastMode: false,
|
||||||
...makeCronSession().sessionEntry,
|
expectedFastMode: true,
|
||||||
fastMode: true,
|
message: "test fast mode session override",
|
||||||
},
|
sessionFastMode: 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",
|
|
||||||
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,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue