mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-30 04:36:04 +00:00
chore: Fix types in tests 3/N.
This commit is contained in:
@@ -73,7 +73,8 @@ function expectChannels(call: Record<string, unknown>, channel: string) {
|
||||
}
|
||||
|
||||
function readAgentCommandCall(fromEnd = 1) {
|
||||
return vi.mocked(agentCommand).mock.calls.at(-fromEnd)?.[0] as Record<string, unknown>;
|
||||
const calls = vi.mocked(agentCommand).mock.calls as unknown[][];
|
||||
return (calls.at(-fromEnd)?.[0] ?? {}) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function expectAgentRoutingCall(params: {
|
||||
@@ -272,7 +273,8 @@ describe("gateway server agent", () => {
|
||||
test("agent routes bare /new through session reset before running greeting prompt", async () => {
|
||||
await writeMainSessionEntry({ sessionId: "sess-main-before-reset" });
|
||||
const spy = vi.mocked(agentCommand);
|
||||
const callsBefore = spy.mock.calls.length;
|
||||
const calls = spy.mock.calls as unknown[][];
|
||||
const callsBefore = calls.length;
|
||||
const res = await rpcReq(ws, "agent", {
|
||||
message: "/new",
|
||||
sessionKey: "main",
|
||||
@@ -280,8 +282,8 @@ describe("gateway server agent", () => {
|
||||
});
|
||||
expect(res.ok).toBe(true);
|
||||
|
||||
await vi.waitFor(() => expect(spy.mock.calls.length).toBeGreaterThan(callsBefore));
|
||||
const call = spy.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
||||
await vi.waitFor(() => expect(calls.length).toBeGreaterThan(callsBefore));
|
||||
const call = (calls.at(-1)?.[0] ?? {}) as Record<string, unknown>;
|
||||
expect(call.message).toBe(BARE_SESSION_RESET_PROMPT);
|
||||
expect(typeof call.sessionId).toBe("string");
|
||||
expect(call.sessionId).not.toBe("sess-main-before-reset");
|
||||
@@ -399,10 +401,7 @@ describe("gateway server agent", () => {
|
||||
});
|
||||
|
||||
const evt = await finalChatP;
|
||||
const payload =
|
||||
evt.payload && typeof evt.payload === "object"
|
||||
? (evt.payload as Record<string, unknown>)
|
||||
: {};
|
||||
const payload = evt.payload && typeof evt.payload === "object" ? evt.payload : {};
|
||||
expect(payload.sessionKey).toBe("main");
|
||||
expect(payload.runId).toBe("run-auto-1");
|
||||
|
||||
|
||||
@@ -52,7 +52,8 @@ describe("gateway server chat", () => {
|
||||
|
||||
const spy = vi.mocked(getReplyFromConfig);
|
||||
spy.mockClear();
|
||||
const callsBeforeSanitized = spy.mock.calls.length;
|
||||
const spyCalls = spy.mock.calls as unknown[][];
|
||||
const callsBeforeSanitized = spyCalls.length;
|
||||
const sanitizedRes = await rpcReq(ws, "chat.send", {
|
||||
sessionKey: "main",
|
||||
message: "Cafe\u0301\u0007\tline",
|
||||
@@ -60,8 +61,8 @@ describe("gateway server chat", () => {
|
||||
});
|
||||
expect(sanitizedRes.ok).toBe(true);
|
||||
|
||||
await waitFor(() => spy.mock.calls.length > callsBeforeSanitized);
|
||||
const ctx = spy.mock.calls.at(-1)?.[0] as
|
||||
await waitFor(() => spyCalls.length > callsBeforeSanitized);
|
||||
const ctx = spyCalls.at(-1)?.[0] as
|
||||
| { Body?: string; RawBody?: string; BodyForCommands?: string }
|
||||
| undefined;
|
||||
expect(ctx?.Body).toBe("Café\tline");
|
||||
@@ -99,8 +100,9 @@ describe("gateway server chat", () => {
|
||||
|
||||
const spy = vi.mocked(getReplyFromConfig);
|
||||
spy.mockClear();
|
||||
const spyCalls = spy.mock.calls as unknown[][];
|
||||
testState.agentConfig = { timeoutSeconds: 123 };
|
||||
const callsBeforeTimeout = spy.mock.calls.length;
|
||||
const callsBeforeTimeout = spyCalls.length;
|
||||
const timeoutRes = await rpcReq(ws, "chat.send", {
|
||||
sessionKey: "main",
|
||||
message: "hello",
|
||||
@@ -108,13 +110,13 @@ describe("gateway server chat", () => {
|
||||
});
|
||||
expect(timeoutRes.ok).toBe(true);
|
||||
|
||||
await waitFor(() => spy.mock.calls.length > callsBeforeTimeout);
|
||||
const timeoutCall = spy.mock.calls.at(-1)?.[1] as { runId?: string } | undefined;
|
||||
await waitFor(() => spyCalls.length > callsBeforeTimeout);
|
||||
const timeoutCall = spyCalls.at(-1)?.[1] as { runId?: string } | undefined;
|
||||
expect(timeoutCall?.runId).toBe("idem-timeout-1");
|
||||
testState.agentConfig = undefined;
|
||||
|
||||
spy.mockClear();
|
||||
const callsBeforeSession = spy.mock.calls.length;
|
||||
const callsBeforeSession = spyCalls.length;
|
||||
const sessionRes = await rpcReq(ws, "chat.send", {
|
||||
sessionKey: "agent:main:subagent:abc",
|
||||
message: "hello",
|
||||
@@ -122,8 +124,8 @@ describe("gateway server chat", () => {
|
||||
});
|
||||
expect(sessionRes.ok).toBe(true);
|
||||
|
||||
await waitFor(() => spy.mock.calls.length > callsBeforeSession);
|
||||
const sessionCall = spy.mock.calls.at(-1)?.[0] as { SessionKey?: string } | undefined;
|
||||
await waitFor(() => spyCalls.length > callsBeforeSession);
|
||||
const sessionCall = spyCalls.at(-1)?.[0] as { SessionKey?: string } | undefined;
|
||||
expect(sessionCall?.SessionKey).toBe("agent:main:subagent:abc");
|
||||
|
||||
const sendPolicyDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-gw-"));
|
||||
@@ -198,7 +200,7 @@ describe("gateway server chat", () => {
|
||||
testState.sessionConfig = undefined;
|
||||
|
||||
spy.mockClear();
|
||||
const callsBeforeImage = spy.mock.calls.length;
|
||||
const callsBeforeImage = spyCalls.length;
|
||||
const pngB64 =
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/woAAn8B9FD5fHAAAAAASUVORK5CYII=";
|
||||
|
||||
@@ -228,13 +230,13 @@ describe("gateway server chat", () => {
|
||||
expect(imgRes.ok).toBe(true);
|
||||
expect(imgRes.payload?.runId).toBeDefined();
|
||||
|
||||
await waitFor(() => spy.mock.calls.length > callsBeforeImage, 8000);
|
||||
const imgOpts = spy.mock.calls.at(-1)?.[1] as
|
||||
await waitFor(() => spyCalls.length > callsBeforeImage, 8000);
|
||||
const imgOpts = spyCalls.at(-1)?.[1] as
|
||||
| { images?: Array<{ type: string; data: string; mimeType: string }> }
|
||||
| undefined;
|
||||
expect(imgOpts?.images).toEqual([{ type: "image", data: pngB64, mimeType: "image/png" }]);
|
||||
|
||||
const callsBeforeImageOnly = spy.mock.calls.length;
|
||||
const callsBeforeImageOnly = spyCalls.length;
|
||||
const reqIdOnly = "chat-img-only";
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
@@ -261,8 +263,8 @@ describe("gateway server chat", () => {
|
||||
expect(imgOnlyRes.ok).toBe(true);
|
||||
expect(imgOnlyRes.payload?.runId).toBeDefined();
|
||||
|
||||
await waitFor(() => spy.mock.calls.length > callsBeforeImageOnly, 8000);
|
||||
const imgOnlyOpts = spy.mock.calls.at(-1)?.[1] as
|
||||
await waitFor(() => spyCalls.length > callsBeforeImageOnly, 8000);
|
||||
const imgOnlyOpts = spyCalls.at(-1)?.[1] as
|
||||
| { images?: Array<{ type: string; data: string; mimeType: string }> }
|
||||
| undefined;
|
||||
expect(imgOnlyOpts?.images).toEqual([{ type: "image", data: pngB64, mimeType: "image/png" }]);
|
||||
@@ -410,10 +412,7 @@ describe("gateway server chat", () => {
|
||||
});
|
||||
|
||||
const evt = await agentEvtP;
|
||||
const payload =
|
||||
evt.payload && typeof evt.payload === "object"
|
||||
? (evt.payload as Record<string, unknown>)
|
||||
: {};
|
||||
const payload = evt.payload && typeof evt.payload === "object" ? evt.payload : {};
|
||||
expect(payload.sessionKey).toBe("main");
|
||||
expect(payload.stream).toBe("assistant");
|
||||
}
|
||||
|
||||
@@ -10,6 +10,16 @@ installGatewayTestHooks({ scope: "suite" });
|
||||
|
||||
let harness: GatewayServerHarness;
|
||||
|
||||
type GatewayFrame = {
|
||||
type?: string;
|
||||
id?: string;
|
||||
ok?: boolean;
|
||||
event?: string;
|
||||
payload?: Record<string, unknown> | null;
|
||||
seq?: number;
|
||||
stateVersion?: { presence?: number; [key: string]: unknown };
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
harness = await startGatewayServerHarness();
|
||||
});
|
||||
@@ -22,10 +32,16 @@ describe("gateway server health/presence", () => {
|
||||
test("connect + health + presence + status succeed", { timeout: 60_000 }, async () => {
|
||||
const { ws } = await harness.openClient();
|
||||
|
||||
const healthP = onceMessage(ws, (o) => o.type === "res" && o.id === "health1");
|
||||
const statusP = onceMessage(ws, (o) => o.type === "res" && o.id === "status1");
|
||||
const presenceP = onceMessage(ws, (o) => o.type === "res" && o.id === "presence1");
|
||||
const channelsP = onceMessage(ws, (o) => o.type === "res" && o.id === "channels1");
|
||||
const healthP = onceMessage<GatewayFrame>(ws, (o) => o.type === "res" && o.id === "health1");
|
||||
const statusP = onceMessage<GatewayFrame>(ws, (o) => o.type === "res" && o.id === "status1");
|
||||
const presenceP = onceMessage<GatewayFrame>(
|
||||
ws,
|
||||
(o) => o.type === "res" && o.id === "presence1",
|
||||
);
|
||||
const channelsP = onceMessage<GatewayFrame>(
|
||||
ws,
|
||||
(o) => o.type === "res" && o.id === "channels1",
|
||||
);
|
||||
|
||||
const sendReq = (id: string, method: string) =>
|
||||
ws.send(JSON.stringify({ type: "req", id, method }));
|
||||
@@ -62,12 +78,6 @@ describe("gateway server health/presence", () => {
|
||||
event: string;
|
||||
payload?: HeartbeatPayload | null;
|
||||
};
|
||||
type ResFrame = {
|
||||
type: "res";
|
||||
id: string;
|
||||
ok: boolean;
|
||||
payload?: unknown;
|
||||
};
|
||||
|
||||
const { ws } = await harness.openClient();
|
||||
|
||||
@@ -87,7 +97,7 @@ describe("gateway server health/presence", () => {
|
||||
method: "last-heartbeat",
|
||||
}),
|
||||
);
|
||||
const last = await onceMessage<ResFrame>(ws, (o) => o.type === "res" && o.id === "hb-last");
|
||||
const last = await onceMessage<GatewayFrame>(ws, (o) => o.type === "res" && o.id === "hb-last");
|
||||
expect(last.ok).toBe(true);
|
||||
const lastPayload = last.payload as HeartbeatPayload | null | undefined;
|
||||
expect(lastPayload?.status).toBe("sent");
|
||||
@@ -101,7 +111,7 @@ describe("gateway server health/presence", () => {
|
||||
params: { enabled: false },
|
||||
}),
|
||||
);
|
||||
const toggle = await onceMessage<ResFrame>(
|
||||
const toggle = await onceMessage<GatewayFrame>(
|
||||
ws,
|
||||
(o) => o.type === "res" && o.id === "hb-toggle-off",
|
||||
);
|
||||
@@ -114,7 +124,10 @@ describe("gateway server health/presence", () => {
|
||||
test("presence events carry seq + stateVersion", { timeout: 8000 }, async () => {
|
||||
const { ws } = await harness.openClient();
|
||||
|
||||
const presenceEventP = onceMessage(ws, (o) => o.type === "event" && o.event === "presence");
|
||||
const presenceEventP = onceMessage<GatewayFrame>(
|
||||
ws,
|
||||
(o) => o.type === "event" && o.event === "presence",
|
||||
);
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "req",
|
||||
@@ -127,7 +140,8 @@ describe("gateway server health/presence", () => {
|
||||
const evt = await presenceEventP;
|
||||
expect(typeof evt.seq).toBe("number");
|
||||
expect(evt.stateVersion?.presence).toBeGreaterThan(0);
|
||||
expect(Array.isArray(evt.payload?.presence)).toBe(true);
|
||||
const evtPayload = evt.payload as { presence?: unknown } | undefined;
|
||||
expect(Array.isArray(evtPayload?.presence)).toBe(true);
|
||||
|
||||
ws.close();
|
||||
});
|
||||
@@ -136,7 +150,7 @@ describe("gateway server health/presence", () => {
|
||||
const { ws } = await harness.openClient();
|
||||
|
||||
const runId = randomUUID();
|
||||
const evtPromise = onceMessage(
|
||||
const evtPromise = onceMessage<GatewayFrame>(
|
||||
ws,
|
||||
(o) =>
|
||||
o.type === "event" &&
|
||||
@@ -146,9 +160,11 @@ describe("gateway server health/presence", () => {
|
||||
);
|
||||
emitAgentEvent({ runId, stream: "lifecycle", data: { msg: "hi" } });
|
||||
const evt = await evtPromise;
|
||||
expect(evt.payload.runId).toBe(runId);
|
||||
const payload = evt.payload as Record<string, unknown> | undefined;
|
||||
expect(payload?.runId).toBe(runId);
|
||||
expect(typeof evt.seq).toBe("number");
|
||||
expect(evt.payload.data.msg).toBe("hi");
|
||||
const data = payload?.data as Record<string, unknown> | undefined;
|
||||
expect(data?.msg).toBe("hi");
|
||||
|
||||
ws.close();
|
||||
});
|
||||
@@ -156,10 +172,15 @@ describe("gateway server health/presence", () => {
|
||||
test("shutdown event is broadcast on close", { timeout: 8000 }, async () => {
|
||||
const localHarness = await startGatewayServerHarness();
|
||||
const { ws } = await localHarness.openClient();
|
||||
const shutdownP = onceMessage(ws, (o) => o.type === "event" && o.event === "shutdown", 5000);
|
||||
const shutdownP = onceMessage<GatewayFrame>(
|
||||
ws,
|
||||
(o) => o.type === "event" && o.event === "shutdown",
|
||||
5000,
|
||||
);
|
||||
await localHarness.close();
|
||||
const evt = await shutdownP;
|
||||
expect(evt.payload?.reason).toBeDefined();
|
||||
const evtPayload = evt.payload as { reason?: unknown } | undefined;
|
||||
expect(evtPayload?.reason).toBeDefined();
|
||||
});
|
||||
|
||||
test("presence broadcast reaches multiple clients", { timeout: 8000 }, async () => {
|
||||
@@ -169,7 +190,7 @@ describe("gateway server health/presence", () => {
|
||||
harness.openClient(),
|
||||
]);
|
||||
const waits = clients.map(({ ws }) =>
|
||||
onceMessage(ws, (o) => o.type === "event" && o.event === "presence"),
|
||||
onceMessage<GatewayFrame>(ws, (o) => o.type === "event" && o.event === "presence"),
|
||||
);
|
||||
clients[0].ws.send(
|
||||
JSON.stringify({
|
||||
@@ -181,7 +202,8 @@ describe("gateway server health/presence", () => {
|
||||
);
|
||||
const events = await Promise.all(waits);
|
||||
for (const evt of events) {
|
||||
expect(evt.payload?.presence?.length).toBeGreaterThan(0);
|
||||
const evtPayload = evt.payload as { presence?: unknown[] } | undefined;
|
||||
expect(evtPayload?.presence?.length).toBeGreaterThan(0);
|
||||
expect(typeof evt.seq).toBe("number");
|
||||
}
|
||||
for (const { ws } of clients) {
|
||||
@@ -206,7 +228,11 @@ describe("gateway server health/presence", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const presenceP = onceMessage(ws, (o) => o.type === "res" && o.id === "fingerprint", 4000);
|
||||
const presenceP = onceMessage<GatewayFrame>(
|
||||
ws,
|
||||
(o) => o.type === "res" && o.id === "fingerprint",
|
||||
4000,
|
||||
);
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "req",
|
||||
@@ -247,7 +273,11 @@ describe("gateway server health/presence", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const presenceP = onceMessage(ws, (o) => o.type === "res" && o.id === "cli-presence", 4000);
|
||||
const presenceP = onceMessage<GatewayFrame>(
|
||||
ws,
|
||||
(o) => o.type === "res" && o.id === "cli-presence",
|
||||
4000,
|
||||
);
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "req",
|
||||
@@ -257,7 +287,7 @@ describe("gateway server health/presence", () => {
|
||||
);
|
||||
|
||||
const presenceRes = await presenceP;
|
||||
const entries = presenceRes.payload as Array<Record<string, unknown>>;
|
||||
const entries = (presenceRes.payload ?? []) as Array<Record<string, unknown>>;
|
||||
expect(entries.some((e) => e.instanceId === cliId)).toBe(false);
|
||||
|
||||
ws.close();
|
||||
|
||||
@@ -296,9 +296,20 @@ export async function occupyPort(): Promise<{
|
||||
});
|
||||
}
|
||||
|
||||
export function onceMessage<T = unknown>(
|
||||
type GatewayTestMessage = {
|
||||
type?: string;
|
||||
id?: string;
|
||||
ok?: boolean;
|
||||
event?: string;
|
||||
payload?: Record<string, unknown> | null;
|
||||
seq?: number;
|
||||
stateVersion?: Record<string, unknown>;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
export function onceMessage<T extends GatewayTestMessage = GatewayTestMessage>(
|
||||
ws: WebSocket,
|
||||
filter: (obj: unknown) => boolean,
|
||||
filter: (obj: T) => boolean,
|
||||
// Full-suite runs can saturate the event loop (581+ files). Keep this high
|
||||
// enough to avoid flaky RPC timeouts, but still fail fast when a response
|
||||
// never arrives.
|
||||
@@ -312,12 +323,12 @@ export function onceMessage<T = unknown>(
|
||||
reject(new Error(`closed ${code}: ${reason.toString()}`));
|
||||
};
|
||||
const handler = (data: WebSocket.RawData) => {
|
||||
const obj = JSON.parse(rawDataToString(data));
|
||||
const obj = JSON.parse(rawDataToString(data)) as T;
|
||||
if (filter(obj)) {
|
||||
clearTimeout(timer);
|
||||
ws.off("message", handler);
|
||||
ws.off("close", closeHandler);
|
||||
resolve(obj as T);
|
||||
resolve(obj);
|
||||
}
|
||||
};
|
||||
ws.on("message", handler);
|
||||
|
||||
@@ -7,7 +7,7 @@ type SlackProviderMonitor = (params: {
|
||||
abortSignal: AbortSignal;
|
||||
}) => Promise<unknown>;
|
||||
|
||||
const slackTestState: {
|
||||
type SlackTestState = {
|
||||
config: Record<string, unknown>;
|
||||
sendMock: Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
replyMock: Mock<(...args: unknown[]) => unknown>;
|
||||
@@ -15,7 +15,9 @@ const slackTestState: {
|
||||
reactMock: Mock<(...args: unknown[]) => unknown>;
|
||||
readAllowFromStoreMock: Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
upsertPairingRequestMock: Mock<(...args: unknown[]) => Promise<unknown>>;
|
||||
} = vi.hoisted(() => ({
|
||||
};
|
||||
|
||||
const slackTestState: SlackTestState = vi.hoisted(() => ({
|
||||
config: {} as Record<string, unknown>,
|
||||
sendMock: vi.fn(),
|
||||
replyMock: vi.fn(),
|
||||
@@ -25,7 +27,26 @@ const slackTestState: {
|
||||
upsertPairingRequestMock: vi.fn(),
|
||||
}));
|
||||
|
||||
export const getSlackTestState: () => void = () => slackTestState;
|
||||
export const getSlackTestState = (): SlackTestState => slackTestState;
|
||||
|
||||
type SlackClient = {
|
||||
auth: { test: Mock<(...args: unknown[]) => Promise<Record<string, unknown>>> };
|
||||
conversations: {
|
||||
info: Mock<(...args: unknown[]) => Promise<Record<string, unknown>>>;
|
||||
replies: Mock<(...args: unknown[]) => Promise<Record<string, unknown>>>;
|
||||
};
|
||||
users: {
|
||||
info: Mock<(...args: unknown[]) => Promise<{ user: { profile: { display_name: string } } }>>;
|
||||
};
|
||||
assistant: {
|
||||
threads: {
|
||||
setStatus: Mock<(...args: unknown[]) => Promise<{ ok: boolean }>>;
|
||||
};
|
||||
};
|
||||
reactions: {
|
||||
add: (...args: unknown[]) => unknown;
|
||||
};
|
||||
};
|
||||
|
||||
export const getSlackHandlers = () =>
|
||||
(
|
||||
@@ -34,8 +55,7 @@ export const getSlackHandlers = () =>
|
||||
}
|
||||
).__slackHandlers;
|
||||
|
||||
export const getSlackClient = () =>
|
||||
(globalThis as { __slackClient?: Record<string, unknown> }).__slackClient;
|
||||
export const getSlackClient = () => (globalThis as { __slackClient?: SlackClient }).__slackClient;
|
||||
|
||||
export const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
|
||||
|
||||
|
||||
@@ -63,6 +63,10 @@ describe("monitorSlackProvider tool results", () => {
|
||||
};
|
||||
}
|
||||
|
||||
function firstReplyCtx(): { WasMentioned?: boolean } {
|
||||
return (replyMock.mock.calls[0]?.[0] ?? {}) as { WasMentioned?: boolean };
|
||||
}
|
||||
|
||||
async function runDirectMessageEvent(ts: string, extraEvent: Record<string, unknown> = {}) {
|
||||
await runSlackMessageOnce(monitorSlackProvider, {
|
||||
event: makeSlackMessageEvent({ ts, ...extraEvent }),
|
||||
@@ -168,7 +172,7 @@ describe("monitorSlackProvider tool results", () => {
|
||||
};
|
||||
|
||||
let capturedCtx: { Body?: string; RawBody?: string; CommandBody?: string } = {};
|
||||
replyMock.mockImplementation(async (ctx) => {
|
||||
replyMock.mockImplementation(async (ctx: unknown) => {
|
||||
capturedCtx = ctx ?? {};
|
||||
return undefined;
|
||||
});
|
||||
@@ -221,7 +225,7 @@ describe("monitorSlackProvider tool results", () => {
|
||||
};
|
||||
|
||||
const capturedCtx: Array<{ Body?: string }> = [];
|
||||
replyMock.mockImplementation(async (ctx) => {
|
||||
replyMock.mockImplementation(async (ctx: unknown) => {
|
||||
capturedCtx.push(ctx ?? {});
|
||||
return undefined;
|
||||
});
|
||||
@@ -274,7 +278,8 @@ describe("monitorSlackProvider tool results", () => {
|
||||
});
|
||||
|
||||
it("updates assistant thread status when replies start", async () => {
|
||||
replyMock.mockImplementation(async (_ctx, opts) => {
|
||||
replyMock.mockImplementation(async (...args: unknown[]) => {
|
||||
const opts = (args[1] ?? {}) as { onReplyStart?: () => Promise<void> | void };
|
||||
await opts?.onReplyStart?.();
|
||||
return { text: "final reply" };
|
||||
});
|
||||
@@ -325,7 +330,7 @@ describe("monitorSlackProvider tool results", () => {
|
||||
});
|
||||
|
||||
expect(replyMock).toHaveBeenCalledTimes(1);
|
||||
expect(replyMock.mock.calls[0][0].WasMentioned).toBe(true);
|
||||
expect(firstReplyCtx().WasMentioned).toBe(true);
|
||||
}
|
||||
|
||||
it("accepts channel messages when mentionPatterns match", async () => {
|
||||
@@ -358,7 +363,7 @@ describe("monitorSlackProvider tool results", () => {
|
||||
});
|
||||
|
||||
expect(replyMock).toHaveBeenCalledTimes(1);
|
||||
expect(replyMock.mock.calls[0][0].WasMentioned).toBe(true);
|
||||
expect(firstReplyCtx().WasMentioned).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts channel messages without mention when channels.slack.requireMention is false", async () => {
|
||||
@@ -380,7 +385,7 @@ describe("monitorSlackProvider tool results", () => {
|
||||
});
|
||||
|
||||
expect(replyMock).toHaveBeenCalledTimes(1);
|
||||
expect(replyMock.mock.calls[0][0].WasMentioned).toBe(false);
|
||||
expect(firstReplyCtx().WasMentioned).toBe(false);
|
||||
expect(sendMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -395,7 +400,7 @@ describe("monitorSlackProvider tool results", () => {
|
||||
});
|
||||
|
||||
expect(replyMock).toHaveBeenCalledTimes(1);
|
||||
expect(replyMock.mock.calls[0][0].WasMentioned).toBe(true);
|
||||
expect(firstReplyCtx().WasMentioned).toBe(true);
|
||||
});
|
||||
|
||||
it("threads replies when incoming message is in a thread", async () => {
|
||||
@@ -478,12 +483,15 @@ describe("monitorSlackProvider tool results", () => {
|
||||
});
|
||||
|
||||
it("replies with pairing code when dmPolicy is pairing and no allowFrom is set", async () => {
|
||||
const currentConfig = slackTestState.config as {
|
||||
channels?: { slack?: Record<string, unknown> };
|
||||
};
|
||||
slackTestState.config = {
|
||||
...slackTestState.config,
|
||||
...currentConfig,
|
||||
channels: {
|
||||
...slackTestState.config.channels,
|
||||
...currentConfig.channels,
|
||||
slack: {
|
||||
...slackTestState.config.channels?.slack,
|
||||
...currentConfig.channels?.slack,
|
||||
dm: { enabled: true, policy: "pairing", allowFrom: [] },
|
||||
},
|
||||
},
|
||||
@@ -496,17 +504,20 @@ describe("monitorSlackProvider tool results", () => {
|
||||
expect(replyMock).not.toHaveBeenCalled();
|
||||
expect(upsertPairingRequestMock).toHaveBeenCalled();
|
||||
expect(sendMock).toHaveBeenCalledTimes(1);
|
||||
expect(String(sendMock.mock.calls[0]?.[1] ?? "")).toContain("Your Slack user id: U1");
|
||||
expect(String(sendMock.mock.calls[0]?.[1] ?? "")).toContain("Pairing code: PAIRCODE");
|
||||
expect(sendMock.mock.calls[0]?.[1]).toContain("Your Slack user id: U1");
|
||||
expect(sendMock.mock.calls[0]?.[1]).toContain("Pairing code: PAIRCODE");
|
||||
});
|
||||
|
||||
it("does not resend pairing code when a request is already pending", async () => {
|
||||
const currentConfig = slackTestState.config as {
|
||||
channels?: { slack?: Record<string, unknown> };
|
||||
};
|
||||
slackTestState.config = {
|
||||
...slackTestState.config,
|
||||
...currentConfig,
|
||||
channels: {
|
||||
...slackTestState.config.channels,
|
||||
...currentConfig.channels,
|
||||
slack: {
|
||||
...slackTestState.config.channels?.slack,
|
||||
...currentConfig.channels?.slack,
|
||||
dm: { enabled: true, policy: "pairing", allowFrom: [] },
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user