test: Retry SSE webhook setup on 404 (#28961)

Co-authored-by: Danny Martini <danny@n8n.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Iván Ovejero
2026-04-29 15:15:49 +00:00
committed by GitHub
co-authored by Danny Martini Claude Opus 4.7
parent 40da23f688
commit 334ce39f65
@@ -2,12 +2,15 @@ import type { APIResponse } from '@playwright/test';
import * as http from 'http';
import * as https from 'https';
import { nanoid } from 'nanoid';
import { setTimeout as wait } from 'node:timers/promises';
import type { ApiHelpers } from './api-helper';
import { N8N_AUTH_COOKIE } from '../config/constants';
type HttpMethod = 'GET' | 'POST' | 'DELETE';
class SseNotFoundError extends Error {}
interface SseConnection {
sessionId: string;
postUrl: string;
@@ -176,6 +179,29 @@ export class McpApiHelper {
* @returns McpSession with sessionId and postUrl for sending messages
*/
async sseSetup(
path: string,
options?: {
headers?: Record<string, string>;
maxNotFoundRetries?: number;
notFoundRetryDelayMs?: number;
},
): Promise<McpSession> {
const maxNotFoundRetries = options?.maxNotFoundRetries ?? 5;
const notFoundRetryDelayMs = options?.notFoundRetryDelayMs ?? 500;
for (let attempt = 0; attempt <= maxNotFoundRetries; attempt++) {
try {
return await this.attemptSseSetup(path, options);
} catch (error) {
if (!(error instanceof SseNotFoundError) || attempt === maxNotFoundRetries) throw error;
await wait(notFoundRetryDelayMs);
}
}
// unreachable: the catch above rethrows on the final attempt. Here to satisfy TS.
throw new Error('SSE setup: retry loop exhausted');
}
private async attemptSseSetup(
path: string,
options?: { headers?: Record<string, string> },
): Promise<McpSession> {
@@ -216,6 +242,14 @@ export class McpApiHelper {
headers,
},
(res) => {
if (res.statusCode === 404) {
// Drain the response so the socket is released back to the pool across retries.
res.resume();
clearTimeout(timeout);
reject(new SseNotFoundError(`SSE setup got 404 for ${path}`));
return;
}
let buffer = '';
let resolved = false;