test: dedupe slack message event tests

This commit is contained in:
Peter Steinberger 2026-03-13 21:37:46 +00:00
parent c8898034f9
commit 67f7d1e65f
2 changed files with 92 additions and 93 deletions

View File

@ -57,6 +57,30 @@ async function writeGatewayScript(env: Record<string, string>, port = 18789) {
"utf8",
);
}
async function writeStartupFallbackEntry(env: Record<string, string>) {
const startupEntryPath = resolveStartupEntryPath(env);
await fs.mkdir(path.dirname(startupEntryPath), { recursive: true });
await fs.writeFile(startupEntryPath, "@echo off\r\n", "utf8");
return startupEntryPath;
}
function expectStartupFallbackSpawn(env: Record<string, string>) {
expect(spawn).toHaveBeenCalledWith(
"cmd.exe",
["/d", "/s", "/c", quoteCmdScriptArg(resolveTaskScriptPath(env))],
expect.objectContaining({ detached: true, stdio: "ignore", windowsHide: true }),
);
}
function addStartupFallbackMissingResponses(
extraResponses: Array<{ code: number; stdout: string; stderr: string }> = [],
) {
schtasksResponses.push(
{ code: 0, stdout: "", stderr: "" },
{ code: 1, stdout: "", stderr: "not found" },
...extraResponses,
);
}
beforeEach(() => {
resetSchtasksBaseMocks();
spawn.mockClear();
@ -119,22 +143,14 @@ describe("Windows startup fallback", () => {
});
await expect(fs.access(resolveStartupEntryPath(env))).resolves.toBeUndefined();
expect(spawn).toHaveBeenCalledWith(
"cmd.exe",
["/d", "/s", "/c", quoteCmdScriptArg(resolveTaskScriptPath(env))],
expect.objectContaining({ detached: true, stdio: "ignore", windowsHide: true }),
);
expectStartupFallbackSpawn(env);
});
});
it("treats an installed Startup-folder launcher as loaded", async () => {
await withWindowsEnv("openclaw-win-startup-", async ({ env }) => {
schtasksResponses.push(
{ code: 0, stdout: "", stderr: "" },
{ code: 1, stdout: "", stderr: "not found" },
);
await fs.mkdir(path.dirname(resolveStartupEntryPath(env)), { recursive: true });
await fs.writeFile(resolveStartupEntryPath(env), "@echo off\r\n", "utf8");
addStartupFallbackMissingResponses();
await writeStartupFallbackEntry(env);
await expect(isScheduledTaskInstalled({ env })).resolves.toBe(true);
});
@ -142,12 +158,8 @@ describe("Windows startup fallback", () => {
it("reports runtime from the gateway listener when using the Startup fallback", async () => {
await withWindowsEnv("openclaw-win-startup-", async ({ env }) => {
schtasksResponses.push(
{ code: 0, stdout: "", stderr: "" },
{ code: 1, stdout: "", stderr: "not found" },
);
await fs.mkdir(path.dirname(resolveStartupEntryPath(env)), { recursive: true });
await fs.writeFile(resolveStartupEntryPath(env), "@echo off\r\n", "utf8");
addStartupFallbackMissingResponses();
await writeStartupFallbackEntry(env);
inspectPortUsage.mockResolvedValue({
port: 18789,
status: "busy",
@ -164,14 +176,11 @@ describe("Windows startup fallback", () => {
it("restarts the Startup fallback by killing the current pid and relaunching the entry", async () => {
await withWindowsEnv("openclaw-win-startup-", async ({ env }) => {
schtasksResponses.push(
addStartupFallbackMissingResponses([
{ code: 0, stdout: "", stderr: "" },
{ code: 1, stdout: "", stderr: "not found" },
{ code: 0, stdout: "", stderr: "" },
{ code: 1, stdout: "", stderr: "not found" },
);
await fs.mkdir(path.dirname(resolveStartupEntryPath(env)), { recursive: true });
await fs.writeFile(resolveStartupEntryPath(env), "@echo off\r\n", "utf8");
]);
await writeStartupFallbackEntry(env);
inspectPortUsage.mockResolvedValue({
port: 18789,
status: "busy",
@ -184,11 +193,7 @@ describe("Windows startup fallback", () => {
outcome: "completed",
});
expect(killProcessTree).toHaveBeenCalledWith(5151, { graceMs: 300 });
expect(spawn).toHaveBeenCalledWith(
"cmd.exe",
["/d", "/s", "/c", quoteCmdScriptArg(resolveTaskScriptPath(env))],
expect.objectContaining({ detached: true, stdio: "ignore", windowsHide: true }),
);
expectStartupFallbackSpawn(env);
});
});
@ -196,8 +201,7 @@ describe("Windows startup fallback", () => {
await withWindowsEnv("openclaw-win-startup-", async ({ env }) => {
schtasksResponses.push({ code: 0, stdout: "", stderr: "" });
await writeGatewayScript(env);
await fs.mkdir(path.dirname(resolveStartupEntryPath(env)), { recursive: true });
await fs.writeFile(resolveStartupEntryPath(env), "@echo off\r\n", "utf8");
await writeStartupFallbackEntry(env);
inspectPortUsage
.mockResolvedValueOnce({
port: 18789,

View File

@ -18,6 +18,7 @@ vi.mock("../../../pairing/pairing-store.js", () => ({
type MessageHandler = (args: { event: Record<string, unknown>; body: unknown }) => Promise<void>;
type AppMentionHandler = MessageHandler;
type RegisteredEventName = "message" | "app_mention";
type MessageCase = {
overrides?: SlackSystemEventTestOverrides;
@ -25,7 +26,7 @@ type MessageCase = {
body?: unknown;
};
function createMessageHandlers(overrides?: SlackSystemEventTestOverrides) {
function createHandlers(eventName: RegisteredEventName, overrides?: SlackSystemEventTestOverrides) {
const harness = createSlackSystemEventTestHarness(overrides);
const handleSlackMessage = vi.fn(async () => {});
registerSlackMessageEvents({
@ -33,22 +34,14 @@ function createMessageHandlers(overrides?: SlackSystemEventTestOverrides) {
handleSlackMessage,
});
return {
handler: harness.getHandler("message") as MessageHandler | null,
handler: harness.getHandler(eventName) as MessageHandler | null,
handleSlackMessage,
};
}
function createAppMentionHandlers(overrides?: SlackSystemEventTestOverrides) {
const harness = createSlackSystemEventTestHarness(overrides);
const handleSlackMessage = vi.fn(async () => {});
registerSlackMessageEvents({
ctx: harness.ctx,
handleSlackMessage,
});
return {
handler: harness.getHandler("app_mention") as AppMentionHandler | null,
handleSlackMessage,
};
function resetMessageMocks(): void {
messageQueueMock.mockClear();
messageAllowMock.mockReset().mockResolvedValue([]);
}
function makeChangedEvent(overrides?: { channel?: string; user?: string }) {
@ -89,10 +82,40 @@ function makeThreadBroadcastEvent(overrides?: { channel?: string; user?: string
};
}
function makeAppMentionEvent(overrides?: {
channel?: string;
channelType?: "channel" | "group" | "im" | "mpim";
ts?: string;
}) {
return {
type: "app_mention",
channel: overrides?.channel ?? "C123",
channel_type: overrides?.channelType ?? "channel",
user: "U1",
text: "<@U_BOT> hello",
ts: overrides?.ts ?? "123.456",
};
}
async function invokeRegisteredHandler(input: {
eventName: RegisteredEventName;
overrides?: SlackSystemEventTestOverrides;
event: Record<string, unknown>;
body?: unknown;
}) {
resetMessageMocks();
const { handler, handleSlackMessage } = createHandlers(input.eventName, input.overrides);
expect(handler).toBeTruthy();
await handler!({
event: input.event,
body: input.body ?? {},
});
return { handleSlackMessage };
}
async function runMessageCase(input: MessageCase = {}): Promise<void> {
messageQueueMock.mockClear();
messageAllowMock.mockReset().mockResolvedValue([]);
const { handler } = createMessageHandlers(input.overrides);
resetMessageMocks();
const { handler } = createHandlers("message", input.overrides);
expect(handler).toBeTruthy();
await handler!({
event: (input.event ?? makeChangedEvent()) as Record<string, unknown>,
@ -151,12 +174,9 @@ describe("registerSlackMessageEvents", () => {
});
it("passes regular message events to the message handler", async () => {
messageQueueMock.mockClear();
messageAllowMock.mockReset().mockResolvedValue([]);
const { handler, handleSlackMessage } = createMessageHandlers({ dmPolicy: "open" });
expect(handler).toBeTruthy();
await handler!({
const { handleSlackMessage } = await invokeRegisteredHandler({
eventName: "message",
overrides: { dmPolicy: "open" },
event: {
type: "message",
channel: "D1",
@ -164,7 +184,6 @@ describe("registerSlackMessageEvents", () => {
text: "hello",
ts: "123.456",
},
body: {},
});
expect(handleSlackMessage).toHaveBeenCalledTimes(1);
@ -172,9 +191,8 @@ describe("registerSlackMessageEvents", () => {
});
it("handles channel and group messages via the unified message handler", async () => {
messageQueueMock.mockClear();
messageAllowMock.mockReset().mockResolvedValue([]);
const { handler, handleSlackMessage } = createMessageHandlers({
resetMessageMocks();
const { handler, handleSlackMessage } = createHandlers("message", {
dmPolicy: "open",
channelType: "channel",
});
@ -206,23 +224,18 @@ describe("registerSlackMessageEvents", () => {
});
it("applies subtype system-event handling for channel messages", async () => {
messageQueueMock.mockClear();
messageAllowMock.mockReset().mockResolvedValue([]);
const { handler, handleSlackMessage } = createMessageHandlers({
dmPolicy: "open",
channelType: "channel",
});
expect(handler).toBeTruthy();
// message_changed events from channels arrive via the generic "message"
// handler with channel_type:"channel" — not a separate event type.
await handler!({
const { handleSlackMessage } = await invokeRegisteredHandler({
eventName: "message",
overrides: {
dmPolicy: "open",
channelType: "channel",
},
event: {
...makeChangedEvent({ channel: "C1", user: "U1" }),
channel_type: "channel",
},
body: {},
});
expect(handleSlackMessage).not.toHaveBeenCalled();
@ -230,38 +243,20 @@ describe("registerSlackMessageEvents", () => {
});
it("skips app_mention events for DM channel ids even with contradictory channel_type", async () => {
const { handler, handleSlackMessage } = createAppMentionHandlers({ dmPolicy: "open" });
expect(handler).toBeTruthy();
await handler!({
event: {
type: "app_mention",
channel: "D123",
channel_type: "channel",
user: "U1",
text: "<@U_BOT> hello",
ts: "123.456",
},
body: {},
const { handleSlackMessage } = await invokeRegisteredHandler({
eventName: "app_mention",
overrides: { dmPolicy: "open" },
event: makeAppMentionEvent({ channel: "D123", channelType: "channel" }),
});
expect(handleSlackMessage).not.toHaveBeenCalled();
});
it("routes app_mention events from channels to the message handler", async () => {
const { handler, handleSlackMessage } = createAppMentionHandlers({ dmPolicy: "open" });
expect(handler).toBeTruthy();
await handler!({
event: {
type: "app_mention",
channel: "C123",
channel_type: "channel",
user: "U1",
text: "<@U_BOT> hello",
ts: "123.789",
},
body: {},
const { handleSlackMessage } = await invokeRegisteredHandler({
eventName: "app_mention",
overrides: { dmPolicy: "open" },
event: makeAppMentionEvent({ channel: "C123", channelType: "channel", ts: "123.789" }),
});
expect(handleSlackMessage).toHaveBeenCalledTimes(1);