mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-22 22:52:03 +00:00
fix(ci): restore channel approval and lifecycle harnesses
This commit is contained in:
@@ -7,14 +7,12 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { peekSystemEvents, resetSystemEventsForTest } from "../../../../src/infra/system-events.js";
|
||||
import { expectPairingReplyText } from "../../../../test/helpers/pairing-reply.js";
|
||||
import {
|
||||
enqueueSystemEventMock,
|
||||
readAllowFromStoreMock,
|
||||
resetDiscordComponentRuntimeMocks,
|
||||
upsertPairingRequestMock,
|
||||
} from "../test-support/component-runtime.js";
|
||||
import {
|
||||
resolveAgentComponentRoute,
|
||||
resolveComponentInteractionContext,
|
||||
} from "./agent-components-helpers.js";
|
||||
import { resolveComponentInteractionContext } from "./agent-components-helpers.js";
|
||||
import { createAgentComponentButton, createAgentSelectMenu } from "./agent-components.js";
|
||||
|
||||
describe("agent components", () => {
|
||||
@@ -32,31 +30,6 @@ describe("agent components", () => {
|
||||
});
|
||||
|
||||
const createCfg = (): OpenClawConfig => ({}) as OpenClawConfig;
|
||||
|
||||
const resolvedDefaultDmSessionKey = () =>
|
||||
resolveAgentComponentRoute({
|
||||
ctx: { cfg: createCfg(), accountId: "default" },
|
||||
rawGuildId: undefined,
|
||||
memberRoleIds: [],
|
||||
isDirectMessage: true,
|
||||
isGroupDm: false,
|
||||
userId: "123456789",
|
||||
channelId: "dm-channel",
|
||||
parentId: undefined,
|
||||
}).sessionKey;
|
||||
|
||||
const resolvedDefaultGroupDmSessionKey = () =>
|
||||
resolveAgentComponentRoute({
|
||||
ctx: { cfg: createCfg(), accountId: "default" },
|
||||
rawGuildId: undefined,
|
||||
memberRoleIds: [],
|
||||
isDirectMessage: false,
|
||||
isGroupDm: true,
|
||||
userId: "123456789",
|
||||
channelId: "group-dm-channel",
|
||||
parentId: undefined,
|
||||
}).sessionKey;
|
||||
|
||||
const createBaseDmInteraction = (overrides: Record<string, unknown> = {}) => {
|
||||
const reply = vi.fn().mockResolvedValue(undefined);
|
||||
const defer = vi.fn().mockResolvedValue(undefined);
|
||||
@@ -208,8 +181,8 @@ describe("agent components", () => {
|
||||
content: "You are not authorized to use this button.",
|
||||
ephemeral: true,
|
||||
});
|
||||
expect(peekSystemEvents(resolvedDefaultGroupDmSessionKey())).toEqual([]);
|
||||
expect(peekSystemEvents(resolvedDefaultDmSessionKey())).toEqual([]);
|
||||
expect(peekSystemEvents(defaultGroupDmSessionKey)).toEqual([]);
|
||||
expect(peekSystemEvents(defaultDmSessionKey)).toEqual([]);
|
||||
expect(readAllowFromStoreMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -231,10 +204,13 @@ describe("agent components", () => {
|
||||
|
||||
expect(defer).not.toHaveBeenCalled();
|
||||
expect(reply).toHaveBeenCalledWith({ content: "✓", ephemeral: true });
|
||||
expect(peekSystemEvents(resolvedDefaultGroupDmSessionKey())).toEqual([
|
||||
expect(enqueueSystemEventMock).toHaveBeenCalledWith(
|
||||
"[Discord component: hello clicked by Alice#1234 (123456789)]",
|
||||
]);
|
||||
expect(peekSystemEvents(resolvedDefaultDmSessionKey())).toEqual([]);
|
||||
expect.objectContaining({
|
||||
sessionKey: defaultGroupDmSessionKey,
|
||||
}),
|
||||
);
|
||||
expect(peekSystemEvents(defaultDmSessionKey)).toEqual([]);
|
||||
expect(readAllowFromStoreMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -251,9 +227,12 @@ describe("agent components", () => {
|
||||
|
||||
expect(defer).not.toHaveBeenCalled();
|
||||
expect(reply).toHaveBeenCalledWith({ content: "✓", ephemeral: true });
|
||||
expect(peekSystemEvents(resolvedDefaultDmSessionKey())).toEqual([
|
||||
expect(enqueueSystemEventMock).toHaveBeenCalledWith(
|
||||
"[Discord component: hello clicked by Alice#1234 (123456789)]",
|
||||
]);
|
||||
expect.objectContaining({
|
||||
sessionKey: defaultDmSessionKey,
|
||||
}),
|
||||
);
|
||||
expect(upsertPairingRequestMock).not.toHaveBeenCalled();
|
||||
expect(readAllowFromStoreMock).toHaveBeenCalledWith("discord", "default");
|
||||
});
|
||||
@@ -271,9 +250,12 @@ describe("agent components", () => {
|
||||
|
||||
expect(defer).not.toHaveBeenCalled();
|
||||
expect(reply).toHaveBeenCalledWith({ content: "✓", ephemeral: true });
|
||||
expect(peekSystemEvents(resolvedDefaultDmSessionKey())).toEqual([
|
||||
expect(enqueueSystemEventMock).toHaveBeenCalledWith(
|
||||
"[Discord component: hello clicked by Alice#1234 (123456789)]",
|
||||
]);
|
||||
expect.objectContaining({
|
||||
sessionKey: defaultDmSessionKey,
|
||||
}),
|
||||
);
|
||||
expect(readAllowFromStoreMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -293,7 +275,7 @@ describe("agent components", () => {
|
||||
content: "DM interactions are disabled.",
|
||||
ephemeral: true,
|
||||
});
|
||||
expect(peekSystemEvents(resolvedDefaultDmSessionKey())).toEqual([]);
|
||||
expect(peekSystemEvents(defaultDmSessionKey)).toEqual([]);
|
||||
expect(readAllowFromStoreMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -311,9 +293,12 @@ describe("agent components", () => {
|
||||
|
||||
expect(defer).not.toHaveBeenCalled();
|
||||
expect(reply).toHaveBeenCalledWith({ content: "✓", ephemeral: true });
|
||||
expect(peekSystemEvents(resolvedDefaultDmSessionKey())).toEqual([
|
||||
expect(enqueueSystemEventMock).toHaveBeenCalledWith(
|
||||
"[Discord select menu: hello interacted by Alice#1234 (123456789) (selected: alpha)]",
|
||||
]);
|
||||
expect.objectContaining({
|
||||
sessionKey: defaultDmSessionKey,
|
||||
}),
|
||||
);
|
||||
expect(readAllowFromStoreMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -330,9 +315,12 @@ describe("agent components", () => {
|
||||
|
||||
expect(defer).not.toHaveBeenCalled();
|
||||
expect(reply).toHaveBeenCalledWith({ content: "✓", ephemeral: true });
|
||||
expect(peekSystemEvents(resolvedDefaultDmSessionKey())).toEqual([
|
||||
expect(enqueueSystemEventMock).toHaveBeenCalledWith(
|
||||
"[Discord component: hello_cid clicked by Alice#1234 (123456789)]",
|
||||
]);
|
||||
expect.objectContaining({
|
||||
sessionKey: defaultDmSessionKey,
|
||||
}),
|
||||
);
|
||||
expect(readAllowFromStoreMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -349,9 +337,12 @@ describe("agent components", () => {
|
||||
|
||||
expect(defer).not.toHaveBeenCalled();
|
||||
expect(reply).toHaveBeenCalledWith({ content: "✓", ephemeral: true });
|
||||
expect(peekSystemEvents(resolvedDefaultDmSessionKey())).toEqual([
|
||||
expect(enqueueSystemEventMock).toHaveBeenCalledWith(
|
||||
"[Discord component: hello%2G clicked by Alice#1234 (123456789)]",
|
||||
]);
|
||||
expect.objectContaining({
|
||||
sessionKey: defaultDmSessionKey,
|
||||
}),
|
||||
);
|
||||
expect(readAllowFromStoreMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,11 +33,11 @@ function resolveMatrixExecApprovalConfig(params: {
|
||||
const account = resolveMatrixAccount(params);
|
||||
const config = account.config.execApprovals;
|
||||
if (!config) {
|
||||
return { enabled: false } as const;
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
...config,
|
||||
enabled: account.enabled && account.configured && config.enabled === true,
|
||||
enabled: account.enabled && account.configured ? config.enabled : false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ function countMatrixExecApprovalEligibleAccounts(params: {
|
||||
request: ApprovalRequest;
|
||||
}): number {
|
||||
return listMatrixAccountIds(params.cfg).filter((accountId) => {
|
||||
const account = resolveMatrixAccount({ cfg, accountId });
|
||||
const account = resolveMatrixAccount({ cfg: params.cfg, accountId });
|
||||
if (!account.enabled || !account.configured) {
|
||||
return false;
|
||||
}
|
||||
@@ -56,13 +56,13 @@ function countMatrixExecApprovalEligibleAccounts(params: {
|
||||
});
|
||||
return (
|
||||
isChannelExecApprovalClientEnabledFromConfig({
|
||||
enabled: config.enabled,
|
||||
enabled: config?.enabled,
|
||||
approverCount: getMatrixExecApprovalApprovers({ cfg: params.cfg, accountId }).length,
|
||||
}) &&
|
||||
matchesApprovalRequestFilters({
|
||||
request: params.request.request,
|
||||
agentFilter: config.agentFilter,
|
||||
sessionFilter: config.sessionFilter,
|
||||
agentFilter: config?.agentFilter,
|
||||
sessionFilter: config?.sessionFilter,
|
||||
})
|
||||
);
|
||||
}).length;
|
||||
|
||||
@@ -107,10 +107,12 @@ describe("mattermost websocket monitor", () => {
|
||||
});
|
||||
|
||||
it("retries when first attempt errors before open and next attempt succeeds", async () => {
|
||||
const abort = new AbortController();
|
||||
const reconnectDelays: number[] = [];
|
||||
const onError = vi.fn();
|
||||
const patches: Array<Record<string, unknown>> = [];
|
||||
const sockets: FakeWebSocket[] = [];
|
||||
let disconnects = 0;
|
||||
|
||||
const connectOnce = createMattermostConnectOnce({
|
||||
wsUrl: "wss://example.invalid/api/v4/websocket",
|
||||
@@ -121,8 +123,15 @@ describe("mattermost websocket monitor", () => {
|
||||
return () => seq++;
|
||||
})(),
|
||||
onPosted: async () => {},
|
||||
abortSignal: abort.signal,
|
||||
statusSink: (patch) => {
|
||||
patches.push(patch as Record<string, unknown>);
|
||||
if (patch.lastDisconnect) {
|
||||
disconnects++;
|
||||
if (disconnects >= 2) {
|
||||
abort.abort();
|
||||
}
|
||||
}
|
||||
},
|
||||
webSocketFactory: () => {
|
||||
const socket = new FakeWebSocket();
|
||||
@@ -135,23 +144,19 @@ describe("mattermost websocket monitor", () => {
|
||||
return;
|
||||
}
|
||||
socket.emitOpen();
|
||||
socket.emitClose(1000);
|
||||
});
|
||||
return socket;
|
||||
},
|
||||
});
|
||||
|
||||
const run = runWithReconnect(connectOnce, {
|
||||
await runWithReconnect(connectOnce, {
|
||||
abortSignal: abort.signal,
|
||||
initialDelayMs: 1,
|
||||
onError,
|
||||
onReconnect: (delay) => reconnectDelays.push(delay),
|
||||
shouldReconnect: ({ attempt, outcome }) => outcome === "rejected" && attempt === 0,
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(sockets).toHaveLength(2));
|
||||
await vi.waitFor(() => expect(sockets[1]?.sent).toHaveLength(1));
|
||||
sockets[1]?.emitClose(1000);
|
||||
await run;
|
||||
|
||||
expect(sockets).toHaveLength(2);
|
||||
expect(sockets[0].closeCalls).toBe(1);
|
||||
expect(sockets[1].sent).toHaveLength(1);
|
||||
|
||||
@@ -39,7 +39,7 @@ export function resolveTelegramExecApprovalConfig(params: {
|
||||
}
|
||||
return {
|
||||
...config,
|
||||
enabled: account.enabled && account.tokenSource !== "none" && config.enabled === true,
|
||||
enabled: account.enabled && account.tokenSource !== "none" ? config.enabled : false,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
createImageUpdate,
|
||||
createLifecycleMonitorSetup,
|
||||
expectImageLifecycleDelivery,
|
||||
settleAsyncWork,
|
||||
} from "../test-support/lifecycle-test-support.js";
|
||||
import {
|
||||
getUpdatesMock,
|
||||
@@ -96,12 +95,11 @@ describe("Zalo polling image handling", () => {
|
||||
abortSignal: abort.signal,
|
||||
});
|
||||
|
||||
await settleAsyncWork();
|
||||
await vi.waitFor(() => expect(sendMessageMock).toHaveBeenCalledTimes(1));
|
||||
expect(fetchRemoteMediaMock).not.toHaveBeenCalled();
|
||||
expect(saveMediaBufferMock).not.toHaveBeenCalled();
|
||||
expect(finalizeInboundContextMock).not.toHaveBeenCalled();
|
||||
expect(recordInboundSessionMock).not.toHaveBeenCalled();
|
||||
expect(sendMessageMock).toHaveBeenCalledTimes(1);
|
||||
|
||||
abort.abort();
|
||||
await run;
|
||||
|
||||
@@ -122,8 +122,21 @@ export function createImageLifecycleCore() {
|
||||
path: "/tmp/zalo-photo.jpg",
|
||||
contentType: "image/jpeg",
|
||||
}));
|
||||
const readAllowFromStoreMock = vi.fn(async () => [] as string[]);
|
||||
const upsertPairingRequestMock = vi.fn(async () => ({ code: "PAIRCODE", created: true }));
|
||||
const core = {
|
||||
logging: {
|
||||
shouldLogVerbose: vi.fn(
|
||||
() => false,
|
||||
) as unknown as PluginRuntime["logging"]["shouldLogVerbose"],
|
||||
},
|
||||
channel: {
|
||||
pairing: {
|
||||
readAllowFromStore:
|
||||
readAllowFromStoreMock as unknown as PluginRuntime["channel"]["pairing"]["readAllowFromStore"],
|
||||
upsertPairingRequest:
|
||||
upsertPairingRequestMock as unknown as PluginRuntime["channel"]["pairing"]["upsertPairingRequest"],
|
||||
},
|
||||
routing: {
|
||||
resolveAgentRoute: vi.fn(() => ({
|
||||
agentId: "main",
|
||||
@@ -184,6 +197,8 @@ export function createImageLifecycleCore() {
|
||||
recordInboundSessionMock,
|
||||
fetchRemoteMediaMock,
|
||||
saveMediaBufferMock,
|
||||
readAllowFromStoreMock,
|
||||
upsertPairingRequestMock,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user