fix(gateway): clear stale Slack socket state after disconnect (#39083)

* fix(gateway): restore stale-socket recovery

* test(slack): cover clean socket disconnect status
This commit is contained in:
Tak Hoffman
2026-03-07 12:37:32 -06:00
committed by GitHub
parent fbb9bb08c5
commit 52e7d4295e
4 changed files with 77 additions and 3 deletions

View File

@@ -38,6 +38,38 @@ describe("slack socket reconnect helpers", () => {
);
});
it("clears connected state when socket mode disconnects", () => {
const setStatus = vi.fn();
const err = new Error("dns down");
__testing.publishSlackDisconnectedStatus(setStatus, err);
expect(setStatus).toHaveBeenCalledTimes(1);
expect(setStatus).toHaveBeenCalledWith({
connected: false,
lastDisconnect: {
at: expect.any(Number),
error: "dns down",
},
lastError: "dns down",
});
});
it("clears connected state without error when socket mode disconnects cleanly", () => {
const setStatus = vi.fn();
__testing.publishSlackDisconnectedStatus(setStatus);
expect(setStatus).toHaveBeenCalledTimes(1);
expect(setStatus).toHaveBeenCalledWith({
connected: false,
lastDisconnect: {
at: expect.any(Number),
},
lastError: null,
});
});
it("resolves disconnect waiter on socket disconnect event", async () => {
const client = new FakeEmitter();
const app = { receiver: { client } };

View File

@@ -77,6 +77,22 @@ function publishSlackConnectedStatus(setStatus?: (next: Record<string, unknown>)
});
}
function publishSlackDisconnectedStatus(
setStatus?: (next: Record<string, unknown>) => void,
error?: unknown,
) {
if (!setStatus) {
return;
}
const at = Date.now();
const message = error ? formatUnknownError(error) : undefined;
setStatus({
connected: false,
lastDisconnect: message ? { at, error: message } : { at },
lastError: message ?? null,
});
}
export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
const cfg = opts.config ?? loadConfig();
const runtime: RuntimeEnv = opts.runtime ?? createNonExitingRuntime();
@@ -440,6 +456,7 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
if (opts.abortSignal?.aborted) {
break;
}
publishSlackDisconnectedStatus(opts.setStatus, disconnect.error);
// Bail immediately on non-recoverable auth errors during reconnect too.
if (disconnect.error && isNonRecoverableSlackAuthError(disconnect.error)) {
@@ -495,6 +512,7 @@ export { isNonRecoverableSlackAuthError } from "./reconnect-policy.js";
export const __testing = {
publishSlackConnectedStatus,
publishSlackDisconnectedStatus,
resolveSlackRuntimeGroupPolicy: resolveOpenProviderRuntimeGroupPolicy,
resolveDefaultGroupPolicy,
getSocketEmitter,