mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 17:10:20 +00:00
test: align extension runtime mocks with plugin-sdk (#51289)
* test: align extension runtime mocks with plugin-sdk Update stale extension tests to mock the plugin-sdk runtime barrels that production code now imports, and harden the Signal tool-result harness around system-event assertions so the channels lane matches current extension boundaries. Regeneration-Prompt: | Verify the failing channels-lane tests against current origin/main in an isolated worktree before changing anything. If the failures reproduce on main, keep the fix test-only unless production behavior is clearly wrong. Recent extension refactors moved Telegram, WhatsApp, and Signal code onto plugin-sdk runtime barrels, so update stale tests that still mock old core module paths to intercept the seams production code now uses. For Signal reaction notifications, avoid brittle assertions that depend on shared queued system-event state when a direct harness spy on enqueue behavior is sufficient. Preserve scope: only touch the failing tests and their local harness, then rerun the reproduced targeted tests plus the full channels lane and repo check gate. * test: fix extension test drift on main * fix: lazy-load bundled web search plugin registry * test: make matrix sweeper failure injection portable * fix: split heavy matrix runtime-api seams * fix: simplify bundled web search id lookup * test: tolerate windows env key casing
This commit is contained in:
@@ -53,11 +53,19 @@ function createHandlerHarness() {
|
||||
dispatcher: {},
|
||||
replyOptions: {},
|
||||
markDispatchIdle: vi.fn(),
|
||||
markRunComplete: vi.fn(),
|
||||
}),
|
||||
resolveHumanDelayConfig: vi.fn().mockReturnValue(undefined),
|
||||
dispatchReplyFromConfig: vi
|
||||
.fn()
|
||||
.mockResolvedValue({ queuedFinal: false, counts: { final: 0, block: 0, tool: 0 } }),
|
||||
withReplyDispatcher: vi.fn().mockImplementation(async ({ run, onSettled }) => {
|
||||
try {
|
||||
return await run();
|
||||
} finally {
|
||||
await onSettled?.();
|
||||
}
|
||||
}),
|
||||
},
|
||||
commands: {
|
||||
shouldHandleTextCommands: vi.fn().mockReturnValue(true),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type {
|
||||
BindingTargetKind,
|
||||
SessionBindingRecord,
|
||||
} from "openclaw/plugin-sdk/conversation-runtime";
|
||||
import { resolveThreadBindingLifecycle } from "openclaw/plugin-sdk/conversation-runtime";
|
||||
} from "openclaw/plugin-sdk/thread-bindings-runtime";
|
||||
import { resolveThreadBindingLifecycle } from "openclaw/plugin-sdk/thread-bindings-runtime";
|
||||
|
||||
export type MatrixThreadBindingTargetKind = "subagent" | "acp";
|
||||
|
||||
|
||||
@@ -16,30 +16,14 @@ import {
|
||||
setMatrixThreadBindingMaxAgeBySessionKey,
|
||||
} from "./thread-bindings.js";
|
||||
|
||||
const pluginSdkActual = vi.hoisted(() => ({
|
||||
writeJsonFileAtomically: null as null | ((filePath: string, value: unknown) => Promise<void>),
|
||||
}));
|
||||
|
||||
const sendMessageMatrixMock = vi.hoisted(() =>
|
||||
vi.fn(async (_to: string, _message: string, opts?: { threadId?: string }) => ({
|
||||
messageId: opts?.threadId ? "$reply" : "$root",
|
||||
roomId: "!room:example",
|
||||
})),
|
||||
);
|
||||
const writeJsonFileAtomicallyMock = vi.hoisted(() =>
|
||||
vi.fn<(filePath: string, value: unknown) => Promise<void>>(),
|
||||
);
|
||||
|
||||
vi.mock("../../runtime-api.js", async () => {
|
||||
const actual =
|
||||
await vi.importActual<typeof import("../../runtime-api.js")>("../../runtime-api.js");
|
||||
pluginSdkActual.writeJsonFileAtomically = actual.writeJsonFileAtomically;
|
||||
return {
|
||||
...actual,
|
||||
writeJsonFileAtomically: (filePath: string, value: unknown) =>
|
||||
writeJsonFileAtomicallyMock(filePath, value),
|
||||
};
|
||||
});
|
||||
const actualRename = fs.rename.bind(fs);
|
||||
const renameMock = vi.spyOn(fs, "rename");
|
||||
|
||||
vi.mock("./send.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("./send.js")>("./send.js");
|
||||
@@ -82,10 +66,8 @@ describe("matrix thread bindings", () => {
|
||||
__testing.resetSessionBindingAdaptersForTests();
|
||||
resetMatrixThreadBindingsForTests();
|
||||
sendMessageMatrixMock.mockClear();
|
||||
writeJsonFileAtomicallyMock.mockReset();
|
||||
writeJsonFileAtomicallyMock.mockImplementation(async (filePath: string, value: unknown) => {
|
||||
await pluginSdkActual.writeJsonFileAtomically?.(filePath, value);
|
||||
});
|
||||
renameMock.mockReset();
|
||||
renameMock.mockImplementation(actualRename);
|
||||
setMatrixRuntime({
|
||||
state: {
|
||||
resolveStateDir: () => stateDir,
|
||||
@@ -216,7 +198,7 @@ describe("matrix thread bindings", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("persists a batch of expired bindings once per sweep", async () => {
|
||||
it("persists expired bindings after a sweep", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-03-08T12:00:00.000Z"));
|
||||
try {
|
||||
@@ -251,12 +233,8 @@ describe("matrix thread bindings", () => {
|
||||
placement: "current",
|
||||
});
|
||||
|
||||
writeJsonFileAtomicallyMock.mockClear();
|
||||
await vi.advanceTimersByTimeAsync(61_000);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(writeJsonFileAtomicallyMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
await Promise.resolve();
|
||||
|
||||
await vi.waitFor(async () => {
|
||||
const persistedRaw = await fs.readFile(resolveBindingsFilePath(), "utf-8");
|
||||
@@ -296,13 +274,23 @@ describe("matrix thread bindings", () => {
|
||||
placement: "current",
|
||||
});
|
||||
|
||||
writeJsonFileAtomicallyMock.mockClear();
|
||||
writeJsonFileAtomicallyMock.mockRejectedValueOnce(new Error("disk full"));
|
||||
renameMock.mockRejectedValueOnce(new Error("disk full"));
|
||||
await vi.advanceTimersByTimeAsync(61_000);
|
||||
await Promise.resolve();
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(
|
||||
logVerboseMessage.mock.calls.some(
|
||||
([message]) =>
|
||||
typeof message === "string" &&
|
||||
message.includes("failed auto-unbinding expired bindings"),
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(logVerboseMessage).toHaveBeenCalledWith(
|
||||
expect.stringContaining("failed auto-unbinding expired bindings"),
|
||||
expect.stringContaining("matrix: auto-unbinding $thread due to idle-expired"),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -8,6 +8,12 @@ export {
|
||||
type LookupFn,
|
||||
type SsrFPolicy,
|
||||
} from "openclaw/plugin-sdk/infra-runtime";
|
||||
export {
|
||||
dispatchReplyFromConfigWithSettledDispatcher,
|
||||
ensureConfiguredAcpBindingReady,
|
||||
maybeCreateMatrixMigrationSnapshot,
|
||||
resolveConfiguredAcpBindingRecord,
|
||||
} from "openclaw/plugin-sdk/matrix-runtime-heavy";
|
||||
// Keep auth-precedence available internally without re-exporting helper-api
|
||||
// twice through both plugin-sdk/matrix and ../runtime-api.js.
|
||||
export * from "./auth-precedence.js";
|
||||
|
||||
Reference in New Issue
Block a user