You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
taskbot/tests/unit/services/webhook-manager.test.ts

154 lines
4.7 KiB
TypeScript

import { describe, test, expect, mock, beforeEach, afterEach } from 'bun:test';
import { WebhookManager } from '../../../src/services/webhook-manager';
describe('WebhookManager', () => {
const envBackup = process.env;
beforeEach(() => {
process.env = {
...envBackup,
EVOLUTION_API_URL: 'https://test-api',
EVOLUTION_API_KEY: 'test-key',
EVOLUTION_API_INSTANCE: 'test-instance',
WEBHOOK_URL: 'https://test-webhook'
};
});
afterEach(() => {
process.env = envBackup;
});
test('should warn about missing port in internal URLs', () => {
process.env.WEBHOOK_URL = 'http://srv-captain--taskbot/webhook';
const consoleSpy = mock(() => {});
console.warn = consoleSpy;
WebhookManager['validateConfig']();
expect(consoleSpy).toHaveBeenCalledWith(
expect.stringContaining('missing port number')
);
});
test('should accept internal URLs with ports', () => {
process.env.WEBHOOK_URL = 'http://srv-captain--taskbot:3007/webhook';
const consoleSpy = mock(() => {});
console.warn = consoleSpy;
WebhookManager['validateConfig']();
expect(consoleSpy).not.toHaveBeenCalled();
});
test('should create correct config structure', () => {
const config = WebhookManager['getConfig']();
expect(config).toEqual({
webhook: {
url: 'https://test-webhook',
enabled: true,
webhook_by_events: true,
webhook_base64: true,
events: expect.any(Array)
}
});
});
test('should validate successful response', () => {
const validResponse = {
id: 'test-id',
url: 'https://test-webhook',
enabled: true,
events: ['APPLICATION_STARTUP']
};
// Mock the private validateResponse method
const mockValidate = mock(() => {});
WebhookManager['validateResponse'] = mockValidate;
WebhookManager['validateResponse'](validResponse);
expect(mockValidate).toHaveBeenCalled();
});
test('should reject disabled webhook response', () => {
const invalidResponse = {
id: 'test-id',
url: 'https://test-webhook',
enabled: false,
events: ['APPLICATION_STARTUP']
};
expect(() => {
if (!invalidResponse.enabled) {
throw new Error('Webhook not enabled');
}
}).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;
}
});
});