test(signal): mock daemon readiness in monitor suite

This commit is contained in:
Vincent Koc 2026-03-19 06:13:56 -07:00
parent 1c1a3b6a75
commit a0445b192e
3 changed files with 22 additions and 13 deletions

View File

@ -1,9 +1,8 @@
import { describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { peekSystemEvents } from "../../../src/infra/system-events.js";
import type { SignalDaemonExitEvent } from "./daemon.js";
import { resolveAgentRoute } from "../../../src/routing/resolve-route.js";
import { normalizeE164 } from "../../../src/utils.js";
import type { SignalDaemonExitEvent } from "./daemon.js";
import {
createMockSignalDaemonHandle,
config,
@ -16,7 +15,11 @@ import {
installSignalToolResultTestHooks();
// Import after the harness registers `vi.mock(...)` for Signal internals.
const { monitorSignalProvider } = await import("./monitor.js");
vi.resetModules();
const [{ peekSystemEvents }, { monitorSignalProvider }] = await Promise.all([
import("openclaw/plugin-sdk/infra-runtime"),
import("./monitor.js"),
]);
const {
replyMock,
@ -76,6 +79,7 @@ function createAutoAbortController() {
async function runMonitorWithMocks(opts: MonitorSignalProviderOptions) {
return monitorSignalProvider({
config: config as OpenClawConfig,
waitForTransportReady: waitForTransportReadyMock as any,
...opts,
});
}

View File

@ -171,7 +171,7 @@ export function installSignalToolResultTestHooks() {
replyMock.mockReset();
updateLastRouteMock.mockReset();
streamMock.mockReset();
signalCheckMock.mockReset().mockResolvedValue({});
signalCheckMock.mockReset().mockResolvedValue({ ok: true });
signalRpcRequestMock.mockReset().mockResolvedValue({});
spawnSignalDaemonMock.mockReset().mockReturnValue(createMockSignalDaemonHandle());
readAllowFromStoreMock.mockReset().mockResolvedValue([]);

View File

@ -1,12 +1,13 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import type { SignalReactionNotificationMode } from "openclaw/plugin-sdk/config-runtime";
import type { BackoffPolicy } from "openclaw/plugin-sdk/infra-runtime";
import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
import { loadConfig } from "openclaw/plugin-sdk/config-runtime";
import {
resolveAllowlistProviderRuntimeGroupPolicy,
resolveDefaultGroupPolicy,
warnMissingProviderGroupPolicyFallbackOnce,
} from "openclaw/plugin-sdk/config-runtime";
import type { SignalReactionNotificationMode } from "openclaw/plugin-sdk/config-runtime";
import type { BackoffPolicy } from "openclaw/plugin-sdk/infra-runtime";
import { waitForTransportReady } from "openclaw/plugin-sdk/infra-runtime";
import { saveMediaBuffer } from "openclaw/plugin-sdk/media-runtime";
import {
@ -19,20 +20,19 @@ import {
resolveTextChunkLimit,
} from "openclaw/plugin-sdk/reply-runtime";
import { DEFAULT_GROUP_HISTORY_LIMIT, type HistoryEntry } from "openclaw/plugin-sdk/reply-runtime";
import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
import { createNonExitingRuntime, type RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
import { normalizeStringEntries } from "openclaw/plugin-sdk/text-runtime";
import { normalizeE164 } from "openclaw/plugin-sdk/text-runtime";
import { resolveSignalAccount } from "./accounts.js";
import { signalCheck, signalRpcRequest } from "./client.js";
import { formatSignalDaemonExit, spawnSignalDaemon, type SignalDaemonHandle } from "./daemon.js";
import { isSignalSenderAllowed, type resolveSignalSender } from "./identity.js";
import { createSignalEventHandler } from "./monitor/event-handler.js";
import type {
SignalAttachment,
SignalReactionMessage,
SignalReactionTarget,
} from "./monitor/event-handler.types.js";
import { resolveSignalAccount } from "./accounts.js";
import { signalCheck, signalRpcRequest } from "./client.js";
import { formatSignalDaemonExit, spawnSignalDaemon, type SignalDaemonHandle } from "./daemon.js";
import { isSignalSenderAllowed, type resolveSignalSender } from "./identity.js";
import { createSignalEventHandler } from "./monitor/event-handler.js";
import { sendMessageSignal } from "./send.js";
import { runSignalSseLoop } from "./sse-reconnect.js";
@ -56,6 +56,7 @@ export type MonitorSignalOpts = {
groupAllowFrom?: Array<string | number>;
mediaMaxMb?: number;
reconnectPolicy?: Partial<BackoffPolicy>;
waitForTransportReady?: typeof waitForTransportReady;
};
function resolveRuntime(opts: MonitorSignalOpts): RuntimeEnv {
@ -217,8 +218,10 @@ async function waitForSignalDaemonReady(params: {
logAfterMs: number;
logIntervalMs?: number;
runtime: RuntimeEnv;
waitForTransportReadyFn?: typeof waitForTransportReady;
}): Promise<void> {
await waitForTransportReady({
const waitForTransportReadyFn = params.waitForTransportReadyFn ?? waitForTransportReady;
await waitForTransportReadyFn({
label: "signal daemon",
timeoutMs: params.timeoutMs,
logAfterMs: params.logAfterMs,
@ -374,6 +377,7 @@ export async function monitorSignalProvider(opts: MonitorSignalOpts = {}): Promi
const mediaMaxBytes = (opts.mediaMaxMb ?? accountInfo.config.mediaMaxMb ?? 8) * 1024 * 1024;
const ignoreAttachments = opts.ignoreAttachments ?? accountInfo.config.ignoreAttachments ?? false;
const sendReadReceipts = Boolean(opts.sendReadReceipts ?? accountInfo.config.sendReadReceipts);
const waitForTransportReadyFn = opts.waitForTransportReady ?? waitForTransportReady;
const autoStart = opts.autoStart ?? accountInfo.config.autoStart ?? !accountInfo.config.httpUrl;
const startupTimeoutMs = Math.min(
@ -416,6 +420,7 @@ export async function monitorSignalProvider(opts: MonitorSignalOpts = {}): Promi
logAfterMs: 10_000,
logIntervalMs: 10_000,
runtime,
waitForTransportReadyFn,
});
const daemonExitError = daemonLifecycle.getExitError();
if (daemonExitError) {