import { describe, test, expect, beforeEach, afterEach, mock } from 'bun:test'; import { WebhookServer } from '../../src/server'; import { ResponseQueue } from '../../src/services/response-queue'; // Mock the ResponseQueue let mockAdd: any; beforeEach(() => { mockAdd = mock(() => Promise.resolve()); ResponseQueue.add = mockAdd; }); describe('WebhookServer', () => { const envBackup = process.env; beforeEach(() => { process.env = { ...envBackup, INSTANCE_NAME: 'test-instance', NODE_ENV: 'test' }; }); afterEach(() => { process.env = envBackup; }); const createTestRequest = (payload: any) => new Request('http://localhost:3007', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); test('should reject non-POST requests', async () => { const request = new Request('http://localhost:3007', { method: 'GET' }); const response = await WebhookServer.handleRequest(request); expect(response.status).toBe(405); }); test('should require JSON content type', async () => { const request = new Request('http://localhost:3007', { method: 'POST', headers: { 'Content-Type': 'text/plain' } }); const response = await WebhookServer.handleRequest(request); expect(response.status).toBe(400); }); test('should validate payload structure', async () => { const invalidPayloads = [ {}, { event: null }, { event: 'messages.upsert', instance: null } ]; for (const payload of invalidPayloads) { const request = createTestRequest(payload); const response = await WebhookServer.handleRequest(request); expect(response.status).toBe(400); } }); test('should verify instance name', async () => { const payload = { event: 'messages.upsert', instance: 'wrong-instance', data: {} }; const request = createTestRequest(payload); const response = await WebhookServer.handleRequest(request); expect(response.status).toBe(403); }); test('should handle valid messages.upsert', async () => { const payload = { event: 'messages.upsert', instance: 'test-instance', data: { key: { remoteJid: 'group-id@g.us', participant: 'sender-id@s.whatsapp.net' }, message: { conversation: '/tarea nueva Test' } } }; const request = createTestRequest(payload); const response = await WebhookServer.handleRequest(request); expect(response.status).toBe(200); expect(mockAdd).toHaveBeenCalled(); }); test('should ignore empty message content', async () => { const payload = { event: 'messages.upsert', instance: 'test-instance', data: { key: { remoteJid: 'group-id@g.us', participant: 'sender-id@s.whatsapp.net' }, message: { conversation: '' } } }; const request = createTestRequest(payload); const response = await WebhookServer.handleRequest(request); expect(response.status).toBe(200); expect(mockAdd).not.toHaveBeenCalled(); }); test('should handle very long messages', async () => { const longMessage = '/tarea nueva ' + 'A'.repeat(5000); const payload = { event: 'messages.upsert', instance: 'test-instance', data: { key: { remoteJid: 'group-id@g.us', participant: 'sender-id@s.whatsapp.net' }, message: { conversation: longMessage } } }; const request = createTestRequest(payload); const response = await WebhookServer.handleRequest(request); expect(response.status).toBe(200); expect(mockAdd).toHaveBeenCalled(); }); test('should handle messages with special characters and emojis', async () => { const specialMessage = '/tarea nueva Test 😊 你好 @#$%^&*()'; const payload = { event: 'messages.upsert', instance: 'test-instance', data: { key: { remoteJid: 'group-id@g.us', participant: 'sender-id@s.whatsapp.net' }, message: { conversation: specialMessage } } }; const request = createTestRequest(payload); const response = await WebhookServer.handleRequest(request); expect(response.status).toBe(200); expect(mockAdd).toHaveBeenCalled(); }); test('should ignore non-/tarea commands', async () => { const payload = { event: 'messages.upsert', instance: 'test-instance', data: { key: { remoteJid: 'group-id@g.us', participant: 'sender-id@s.whatsapp.net' }, message: { conversation: '/othercommand test' } } }; const request = createTestRequest(payload); const response = await WebhookServer.handleRequest(request); expect(response.status).toBe(200); expect(mockAdd).not.toHaveBeenCalled(); }); test('should ignore message with mentions but no command', async () => { const payload = { event: 'messages.upsert', instance: 'test-instance', data: { key: { remoteJid: 'group-id@g.us', participant: 'sender-id@s.whatsapp.net' }, message: { conversation: 'Hello everyone!', contextInfo: { mentionedJid: ['1234567890@s.whatsapp.net'] } } } }; const request = createTestRequest(payload); const response = await WebhookServer.handleRequest(request); expect(response.status).toBe(200); expect(mockAdd).not.toHaveBeenCalled(); }); test('should ignore media attachment messages', async () => { const payload = { event: 'messages.upsert', instance: 'test-instance', data: { key: { remoteJid: 'group-id@g.us', participant: 'sender-id@s.whatsapp.net' }, message: { imageMessage: { caption: 'This is an image' } } } }; const request = createTestRequest(payload); const response = await WebhookServer.handleRequest(request); expect(response.status).toBe(200); expect(mockAdd).not.toHaveBeenCalled(); }); test('should handle XSS/SQL injection attempts', async () => { const maliciousMessage = `/tarea nueva '; DROP TABLE tasks; --`; const payload = { event: 'messages.upsert', instance: 'test-instance', data: { key: { remoteJid: 'group-id@g.us', participant: 'sender-id@s.whatsapp.net' }, message: { conversation: maliciousMessage } } }; const request = createTestRequest(payload); const response = await WebhookServer.handleRequest(request); expect(response.status).toBe(200); expect(mockAdd).toHaveBeenCalled(); }); });