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.
		
		
		
		
		
			
		
			
				
	
	
		
			229 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			229 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			TypeScript
		
	
| 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 <script>alert('xss')</script>'; 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();
 | |
|   });
 | |
| });
 |