|
|
|
@ -80,4 +80,74 @@ describe('WebhookManager', () => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}).toThrow();
|
|
|
|
}).toThrow();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test('should retry registration in background until success', async () => {
|
|
|
|
|
|
|
|
// Acelerar reintentos
|
|
|
|
|
|
|
|
process.env.WEBHOOK_RETRY_INITIAL_DELAY_MS = '10';
|
|
|
|
|
|
|
|
process.env.WEBHOOK_RETRY_MAX_DELAY_MS = '20';
|
|
|
|
|
|
|
|
process.env.WEBHOOK_RETRY_DEGRADED_INTERVAL_MS = '50';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const originalFetch: any = globalThis.fetch;
|
|
|
|
|
|
|
|
let setCalls = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const fetchSpy = mock(async (input: any, init?: any) => {
|
|
|
|
|
|
|
|
const url = typeof input === 'string' ? input : input.url;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Self-test of our own webhook endpoint
|
|
|
|
|
|
|
|
if (url === process.env.WEBHOOK_URL) {
|
|
|
|
|
|
|
|
return new Response('ok', { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Register webhook endpoint (first fails, then succeeds)
|
|
|
|
|
|
|
|
if (typeof url === 'string' && url.includes('/webhook/set/')) {
|
|
|
|
|
|
|
|
setCalls++;
|
|
|
|
|
|
|
|
if (setCalls < 2) {
|
|
|
|
|
|
|
|
return new Response('unavailable', { status: 503, statusText: 'Service Unavailable' });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const body = JSON.stringify({
|
|
|
|
|
|
|
|
id: 'test-id',
|
|
|
|
|
|
|
|
url: process.env.WEBHOOK_URL,
|
|
|
|
|
|
|
|
enabled: true,
|
|
|
|
|
|
|
|
events: ['APPLICATION_STARTUP']
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
return new Response(body, { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Verify webhook endpoint (returns enabled)
|
|
|
|
|
|
|
|
if (typeof url === 'string' && url.includes('/webhook/find/')) {
|
|
|
|
|
|
|
|
const body = JSON.stringify({
|
|
|
|
|
|
|
|
enabled: true,
|
|
|
|
|
|
|
|
url: process.env.WEBHOOK_URL,
|
|
|
|
|
|
|
|
events: ['APPLICATION_STARTUP']
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
return new Response(body, { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return new Response('not found', { status: 404 });
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
|
|
|
globalThis.fetch = fetchSpy;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
// Asegurar que no haya un lazo previo corriendo
|
|
|
|
|
|
|
|
try { (WebhookManager as any).stopAutoEnsure(); } catch {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
WebhookManager['startAutoEnsure']();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Esperar a que el lazo logre activar el webhook (o agote timeout)
|
|
|
|
|
|
|
|
const deadline = Date.now() + 2000;
|
|
|
|
|
|
|
|
while (WebhookManager['autoEnsureActive'] && Date.now() < deadline) {
|
|
|
|
|
|
|
|
await new Promise(res => setTimeout(res, 20));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
expect(WebhookManager['autoEnsureActive']).toBe(false);
|
|
|
|
|
|
|
|
expect(fetchSpy).toHaveBeenCalled();
|
|
|
|
|
|
|
|
expect(setCalls).toBeGreaterThan(1);
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
try { WebhookManager['stopAutoEnsure'](); } catch {}
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
|
|
|
globalThis.fetch = originalFetch;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|