|  |  | import { describe, test, expect, beforeEach, afterEach, beforeAll, afterAll } from 'bun:test';
 | 
						
						
						
							|  |  | import { Database } from 'bun:sqlite';
 | 
						
						
						
							|  |  | import { WebhookServer } from '../../src/server';
 | 
						
						
						
							|  |  | import { ResponseQueue } from '../../src/services/response-queue';
 | 
						
						
						
							|  |  | import { GroupSyncService } from '../../src/services/group-sync';
 | 
						
						
						
							|  |  | import { initializeDatabase, ensureUserExists } from '../../src/db';
 | 
						
						
						
							|  |  | import { TaskService } from '../../src/tasks/service';
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Simulated ResponseQueue for testing (in-memory array)
 | 
						
						
						
							|  |  | let simulatedQueue: any[] = [];
 | 
						
						
						
							|  |  | let originalAdd: any;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | class SimulatedResponseQueue {
 | 
						
						
						
							|  |  |   static async add(responses: any[]) {
 | 
						
						
						
							|  |  |     simulatedQueue.push(...responses);
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   static getQueue() {
 | 
						
						
						
							|  |  |     return simulatedQueue;
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   static clear() {
 | 
						
						
						
							|  |  |     simulatedQueue = [];
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | // Test database instance
 | 
						
						
						
							|  |  | let testDb: Database;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | beforeAll(() => {
 | 
						
						
						
							|  |  |   // Create in-memory test database
 | 
						
						
						
							|  |  |   testDb = new Database(':memory:');
 | 
						
						
						
							|  |  |   // Initialize schema
 | 
						
						
						
							|  |  |   initializeDatabase(testDb);
 | 
						
						
						
							|  |  |   // Guardar implementación original de ResponseQueue.add para restaurar después
 | 
						
						
						
							|  |  |   originalAdd = (ResponseQueue as any).add;
 | 
						
						
						
							|  |  | });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | afterAll(() => {
 | 
						
						
						
							|  |  |   (ResponseQueue as any).add = originalAdd;
 | 
						
						
						
							|  |  |   // Close the test database
 | 
						
						
						
							|  |  |   testDb.close();
 | 
						
						
						
							|  |  | });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | beforeEach(() => {
 | 
						
						
						
							|  |  |   // Clear simulated queue
 | 
						
						
						
							|  |  |   SimulatedResponseQueue.clear();
 | 
						
						
						
							|  |  |   
 | 
						
						
						
							|  |  |   // Replace ResponseQueue with simulated version
 | 
						
						
						
							|  |  |   (ResponseQueue as any).add = SimulatedResponseQueue.add;
 | 
						
						
						
							|  |  |   
 | 
						
						
						
							|  |  |   // Inject testDb for WebhookServer to use
 | 
						
						
						
							|  |  |   WebhookServer.dbInstance = testDb;
 | 
						
						
						
							|  |  |   
 | 
						
						
						
							|  |  |   // Inject testDb for GroupSyncService to use
 | 
						
						
						
							|  |  |   GroupSyncService.dbInstance = testDb;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // Inject testDb for TaskService to use
 | 
						
						
						
							|  |  |   (TaskService as any).dbInstance = testDb;
 | 
						
						
						
							|  |  |   
 | 
						
						
						
							|  |  |   // Ensure database is initialized (recreates tables if dropped)
 | 
						
						
						
							|  |  |   initializeDatabase(testDb);
 | 
						
						
						
							|  |  |   
 | 
						
						
						
							|  |  |   // Reset database state between tests
 | 
						
						
						
							|  |  |   testDb.exec('DELETE FROM task_assignments');
 | 
						
						
						
							|  |  |   testDb.exec('DELETE FROM tasks');
 | 
						
						
						
							|  |  |   testDb.exec('DELETE FROM users');
 | 
						
						
						
							|  |  |   testDb.exec('DELETE FROM groups');
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // Insert test data for active group
 | 
						
						
						
							|  |  |   testDb.exec(`
 | 
						
						
						
							|  |  |     INSERT OR IGNORE INTO groups (id, community_id, name, active)
 | 
						
						
						
							|  |  |     VALUES ('group-id@g.us', 'test-community', 'Test Group', 1)
 | 
						
						
						
							|  |  |   `);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // Populate active groups cache with test data
 | 
						
						
						
							|  |  |   GroupSyncService['cacheActiveGroups']();
 | 
						
						
						
							|  |  | });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  | describe('WebhookServer', () => {
 | 
						
						
						
							|  |  |   const envBackup = process.env;
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   beforeEach(() => {
 | 
						
						
						
							|  |  |     process.env = { 
 | 
						
						
						
							|  |  |       ...envBackup, 
 | 
						
						
						
							|  |  |       INSTANCE_NAME: 'test-instance',
 | 
						
						
						
							|  |  |       NODE_ENV: 'test'
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   afterEach(() => {
 | 
						
						
						
							|  |  |     process.env = envBackup;
 | 
						
						
						
							|  |  |     (ResponseQueue as any).add = originalAdd;
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   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 invalidPayload of invalidPayloads) {
 | 
						
						
						
							|  |  |       const request = createTestRequest(invalidPayload);
 | 
						
						
						
							|  |  |       const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |       expect(response.status).toBe(400);
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   test('should verify instance name', async () => {
 | 
						
						
						
							|  |  |     process.env.TEST_VERIFY_INSTANCE = 'true';
 | 
						
						
						
							|  |  |     const payload = {
 | 
						
						
						
							|  |  |       event: 'messages.upsert',
 | 
						
						
						
							|  |  |       instance: 'wrong-instance',
 | 
						
						
						
							|  |  |       data: {}
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  |     const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |     const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |     expect(response.status).toBe(403);
 | 
						
						
						
							|  |  |     delete process.env.TEST_VERIFY_INSTANCE;
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   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(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   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(SimulatedResponseQueue.getQueue().length).toBe(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   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(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   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(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   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(SimulatedResponseQueue.getQueue().length).toBe(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   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(SimulatedResponseQueue.getQueue().length).toBe(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   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(SimulatedResponseQueue.getQueue().length).toBe(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   test('should process command from extendedTextMessage', async () => {
 | 
						
						
						
							|  |  |     const payload = {
 | 
						
						
						
							|  |  |       event: 'messages.upsert',
 | 
						
						
						
							|  |  |       instance: 'test-instance',
 | 
						
						
						
							|  |  |       data: {
 | 
						
						
						
							|  |  |         key: {
 | 
						
						
						
							|  |  |           remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |           participant: 'sender-id@s.whatsapp.net'
 | 
						
						
						
							|  |  |         },
 | 
						
						
						
							|  |  |         message: { 
 | 
						
						
						
							|  |  |           extendedTextMessage: { text: '/t n Test ext' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  |     const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |     const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |     expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |     expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   test('should process command from image caption when caption starts with a command', 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: '/t n From caption' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  |     const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |     const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |     expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |     expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   test('should handle requests on configured port', async () => {
 | 
						
						
						
							|  |  |     const originalPort = process.env.PORT;
 | 
						
						
						
							|  |  |     process.env.PORT = '3007';
 | 
						
						
						
							|  |  |     // Satisfacer validación de entorno en start()
 | 
						
						
						
							|  |  |     const prevEnv = {
 | 
						
						
						
							|  |  |       EVOLUTION_API_URL: process.env.EVOLUTION_API_URL,
 | 
						
						
						
							|  |  |       EVOLUTION_API_KEY: process.env.EVOLUTION_API_KEY,
 | 
						
						
						
							|  |  |       EVOLUTION_API_INSTANCE: process.env.EVOLUTION_API_INSTANCE,
 | 
						
						
						
							|  |  |       CHATBOT_PHONE_NUMBER: process.env.CHATBOT_PHONE_NUMBER,
 | 
						
						
						
							|  |  |       WEBHOOK_URL: process.env.WEBHOOK_URL
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  |     process.env.EVOLUTION_API_URL = 'http://localhost:3000';
 | 
						
						
						
							|  |  |     process.env.EVOLUTION_API_KEY = 'test-key';
 | 
						
						
						
							|  |  |     process.env.EVOLUTION_API_INSTANCE = 'test-instance';
 | 
						
						
						
							|  |  |     process.env.CHATBOT_PHONE_NUMBER = '9999999999';
 | 
						
						
						
							|  |  |     process.env.WEBHOOK_URL = 'http://localhost:3007';
 | 
						
						
						
							|  |  |     
 | 
						
						
						
							|  |  |     try {
 | 
						
						
						
							|  |  |       const server = await WebhookServer.start();
 | 
						
						
						
							|  |  |       const response = await fetch('http://localhost:3007/health');
 | 
						
						
						
							|  |  |       expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |       server.stop();
 | 
						
						
						
							|  |  |     } finally {
 | 
						
						
						
							|  |  |       process.env.PORT = originalPort;
 | 
						
						
						
							|  |  |       process.env.EVOLUTION_API_URL = prevEnv.EVOLUTION_API_URL;
 | 
						
						
						
							|  |  |       process.env.EVOLUTION_API_KEY = prevEnv.EVOLUTION_API_KEY;
 | 
						
						
						
							|  |  |       process.env.EVOLUTION_API_INSTANCE = prevEnv.EVOLUTION_API_INSTANCE;
 | 
						
						
						
							|  |  |       process.env.CHATBOT_PHONE_NUMBER = prevEnv.CHATBOT_PHONE_NUMBER;
 | 
						
						
						
							|  |  |       process.env.WEBHOOK_URL = prevEnv.WEBHOOK_URL;
 | 
						
						
						
							|  |  |     }
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   function getFutureDate(days: number): string {
 | 
						
						
						
							|  |  |     const date = new Date();
 | 
						
						
						
							|  |  |     date.setDate(date.getDate() + days);
 | 
						
						
						
							|  |  |     return date.toISOString().split('T')[0];
 | 
						
						
						
							|  |  |   }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   describe('/tarea command logging', () => {
 | 
						
						
						
							|  |  |     test('should log basic /tarea command', async () => {
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |             participant: 'user123@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/tarea test' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       await WebhookServer.handleRequest(createTestRequest(payload));
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       // Check that a response was queued (indicating command processing)
 | 
						
						
						
							|  |  |       expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     test('should log command with due date', async () => {
 | 
						
						
						
							|  |  |       const futureDate = getFutureDate(3); // Get date 3 days in future
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |             participant: 'user123@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { 
 | 
						
						
						
							|  |  |             conversation: `/tarea nueva Finish project @user2 ${futureDate}`,
 | 
						
						
						
							|  |  |             contextInfo: {
 | 
						
						
						
							|  |  |               mentionedJid: ['user2@s.whatsapp.net']
 | 
						
						
						
							|  |  |             }
 | 
						
						
						
							|  |  |           }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       await WebhookServer.handleRequest(createTestRequest(payload));
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       // Verify command processing by checking queue
 | 
						
						
						
							|  |  |       expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   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(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   test('should handle multiple dates in command (use last one as due date)', async () => {
 | 
						
						
						
							|  |  |     const futureDate1 = getFutureDate(3);
 | 
						
						
						
							|  |  |     const futureDate2 = getFutureDate(5);
 | 
						
						
						
							|  |  |     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 task ${futureDate1} some text ${futureDate2}` }
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  |     
 | 
						
						
						
							|  |  |     await WebhookServer.handleRequest(createTestRequest(payload));
 | 
						
						
						
							|  |  |     
 | 
						
						
						
							|  |  |     expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   test('should ignore past dates as due dates', async () => {
 | 
						
						
						
							|  |  |     const pastDate = '2020-01-01';
 | 
						
						
						
							|  |  |     const futureDate = getFutureDate(2);
 | 
						
						
						
							|  |  |     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 task ${pastDate} more text ${futureDate}` }
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  |     
 | 
						
						
						
							|  |  |     await WebhookServer.handleRequest(createTestRequest(payload));
 | 
						
						
						
							|  |  |     
 | 
						
						
						
							|  |  |     expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   test('should handle multiple past dates correctly', async () => {
 | 
						
						
						
							|  |  |     const pastDate1 = '2020-01-01';
 | 
						
						
						
							|  |  |     const pastDate2 = '2021-01-01';
 | 
						
						
						
							|  |  |     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 task ${pastDate1} and ${pastDate2}` }
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  |     
 | 
						
						
						
							|  |  |     await WebhookServer.handleRequest(createTestRequest(payload));
 | 
						
						
						
							|  |  |     
 | 
						
						
						
							|  |  |     expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   test('should handle mixed valid and invalid date formats', async () => {
 | 
						
						
						
							|  |  |     const futureDate = getFutureDate(2);
 | 
						
						
						
							|  |  |     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 task 2023-13-01 (invalid) ${futureDate} 25/12/2023 (invalid)` }
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  |     
 | 
						
						
						
							|  |  |     await WebhookServer.handleRequest(createTestRequest(payload));
 | 
						
						
						
							|  |  |     
 | 
						
						
						
							|  |  |     expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   test('should normalize sender ID before processing', async () => {
 | 
						
						
						
							|  |  |     const payload = {
 | 
						
						
						
							|  |  |       event: 'messages.upsert',
 | 
						
						
						
							|  |  |       instance: 'test-instance',
 | 
						
						
						
							|  |  |       data: {
 | 
						
						
						
							|  |  |         key: {
 | 
						
						
						
							|  |  |           remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |           participant: '1234567890:12@s.whatsapp.net' // ID with participant
 | 
						
						
						
							|  |  |         },
 | 
						
						
						
							|  |  |         message: { conversation: '/tarea nueva Test' }
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  |     const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |     const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |     expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |     expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   test('should ignore messages with invalid sender ID', async () => {
 | 
						
						
						
							|  |  |     const payload = {
 | 
						
						
						
							|  |  |       event: 'messages.upsert',
 | 
						
						
						
							|  |  |       instance: 'test-instance',
 | 
						
						
						
							|  |  |       data: {
 | 
						
						
						
							|  |  |         key: {
 | 
						
						
						
							|  |  |           remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |           participant: 'invalid-id!' // Invalid ID
 | 
						
						
						
							|  |  |         },
 | 
						
						
						
							|  |  |         message: { conversation: '/tarea nueva Test' }
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  |     const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |     const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |     expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |     expect(SimulatedResponseQueue.getQueue().length).toBe(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   test('should ensure user exists and use normalized ID', async () => {
 | 
						
						
						
							|  |  |     const payload = {
 | 
						
						
						
							|  |  |       event: 'messages.upsert',
 | 
						
						
						
							|  |  |       instance: 'test-instance',
 | 
						
						
						
							|  |  |       data: {
 | 
						
						
						
							|  |  |         key: {
 | 
						
						
						
							|  |  |           remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |           participant: '1234567890@s.whatsapp.net'
 | 
						
						
						
							|  |  |         },
 | 
						
						
						
							|  |  |         message: { conversation: '/tarea nueva Test' }
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  |     const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |     const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |     expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |     expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |     
 | 
						
						
						
							|  |  |     // Verify user was created in real database
 | 
						
						
						
							|  |  |     const user = testDb.query("SELECT * FROM users WHERE id = ?").get('1234567890');
 | 
						
						
						
							|  |  |     expect(user).toBeDefined();
 | 
						
						
						
							|  |  |     expect(user.id).toBe('1234567890');
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   test('should ignore messages if user creation fails', async () => {
 | 
						
						
						
							|  |  |     const payload = {
 | 
						
						
						
							|  |  |       event: 'messages.upsert',
 | 
						
						
						
							|  |  |       instance: 'test-instance',
 | 
						
						
						
							|  |  |       data: {
 | 
						
						
						
							|  |  |         key: {
 | 
						
						
						
							|  |  |           remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |           participant: null // Invalid participant
 | 
						
						
						
							|  |  |         },
 | 
						
						
						
							|  |  |         message: { conversation: '/tarea nueva Test' }
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     };
 | 
						
						
						
							|  |  |     const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |     const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |     expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |     expect(SimulatedResponseQueue.getQueue().length).toBe(0);
 | 
						
						
						
							|  |  |     
 | 
						
						
						
							|  |  |     // Verify no user was created
 | 
						
						
						
							|  |  |     const userCount = testDb.query("SELECT COUNT(*) as count FROM users").get();
 | 
						
						
						
							|  |  |     expect(userCount.count).toBe(0);
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   // Integration tests with real database
 | 
						
						
						
							|  |  |   describe('User validation in handleMessageUpsert', () => {
 | 
						
						
						
							|  |  |     test('should proceed with valid user', async () => {
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |             participant: '1234567890@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/tarea nueva Test' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |       const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |       expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |       expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       // Verify user was created in real database
 | 
						
						
						
							|  |  |       const user = testDb.query("SELECT * FROM users WHERE id = ?").get('1234567890');
 | 
						
						
						
							|  |  |       expect(user).toBeDefined();
 | 
						
						
						
							|  |  |       expect(user.id).toBe('1234567890');
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     test('should ignore message if user validation fails', async () => {
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |             participant: 'invalid!user@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/tarea nueva Test' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |       const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |       expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |       expect(SimulatedResponseQueue.getQueue().length).toBe(0);
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       // Verify no user was created
 | 
						
						
						
							|  |  |       const userCount = testDb.query("SELECT COUNT(*) as count FROM users").get();
 | 
						
						
						
							|  |  |       expect(userCount.count).toBe(0);
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     test('should handle database errors during user validation', async () => {
 | 
						
						
						
							|  |  |       // Force a database error by corrupting the database state
 | 
						
						
						
							|  |  |       testDb.exec('DROP TABLE users');
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |             participant: '1234567890@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/tarea nueva Test' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |       const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |       expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |       expect(SimulatedResponseQueue.getQueue().length).toBe(0);
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       // Reinitialize database for subsequent tests (force full migration)
 | 
						
						
						
							|  |  |       testDb.exec('DROP TABLE IF EXISTS schema_migrations');
 | 
						
						
						
							|  |  |       initializeDatabase(testDb);
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     test('should integrate user validation completely in handleMessageUpsert with valid user', async () => {
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |             participant: '1234567890@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/tarea nueva Test' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |       const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |       expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |       expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       // Verify user was created/updated in database
 | 
						
						
						
							|  |  |       const user = testDb.query("SELECT * FROM users WHERE id = ?").get('1234567890');
 | 
						
						
						
							|  |  |       expect(user).toBeDefined();
 | 
						
						
						
							|  |  |       expect(user.id).toBe('1234567890');
 | 
						
						
						
							|  |  |       expect(user.first_seen).toBeDefined();
 | 
						
						
						
							|  |  |       expect(user.last_seen).toBeDefined();
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     test('should use normalized ID in command service', async () => {
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |             participant: '1234567890:12@s.whatsapp.net' // Raw ID with participant
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/tarea nueva Test' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |       await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       // Verify that a response was queued, indicating command processing
 | 
						
						
						
							|  |  |       expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     test('should handle end-to-end flow with valid user and command processing', async () => {
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |             participant: '1234567890@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/tarea nueva Test task' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |       const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |       expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       // Verify user was created/updated
 | 
						
						
						
							|  |  |       const user = testDb.query("SELECT * FROM users WHERE id = ?").get('1234567890');
 | 
						
						
						
							|  |  |       expect(user).toBeDefined();
 | 
						
						
						
							|  |  |       
 | 
						
						
						
							|  |  |       // Verify that a response was queued
 | 
						
						
						
							|  |  |       expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   describe('Group validation in handleMessageUpsert', () => {
 | 
						
						
						
							|  |  |     test('should ignore messages from inactive groups', async () => {
 | 
						
						
						
							|  |  |       // Insert inactive group
 | 
						
						
						
							|  |  |       testDb.exec(`
 | 
						
						
						
							|  |  |         INSERT OR REPLACE INTO groups (id, community_id, name, active)
 | 
						
						
						
							|  |  |         VALUES ('inactive-group@g.us', 'test-community', 'Inactive Group', 0)
 | 
						
						
						
							|  |  |       `);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: 'inactive-group@g.us',
 | 
						
						
						
							|  |  |             participant: '1234567890@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/tarea nueva Test' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |       const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |       expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |       expect(SimulatedResponseQueue.getQueue().length).toBe(0);
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     test('should proceed with messages from active groups', async () => {
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |             participant: '1234567890@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/tarea nueva Test' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |       const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |       expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |       expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     test('should accept /t alias and process command', async () => {
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |             participant: '1234567890@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/t n Tarea alias hoy' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |       const response = await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  |       expect(response.status).toBe(200);
 | 
						
						
						
							|  |  |       expect(SimulatedResponseQueue.getQueue().length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     test('should never send responses to the group (DM only policy)', async () => {
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |             participant: '1234567890@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/t n Probar silencio grupo mañana' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       const request = createTestRequest(payload);
 | 
						
						
						
							|  |  |       await WebhookServer.handleRequest(request);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       const out = SimulatedResponseQueue.getQueue();
 | 
						
						
						
							|  |  |       expect(out.length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |       for (const r of out) {
 | 
						
						
						
							|  |  |         expect(r.recipient.endsWith('@g.us')).toBe(false);
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |   describe('Advanced listings via WebhookServer', () => {
 | 
						
						
						
							|  |  |     test('should process "/t ver sin" in group as DM-only with pagination line', async () => {
 | 
						
						
						
							|  |  |       // 12 sin dueño en el grupo activo
 | 
						
						
						
							|  |  |       for (let i = 1; i <= 12; i++) {
 | 
						
						
						
							|  |  |         TaskService.createTask({
 | 
						
						
						
							|  |  |           description: `Sin dueño ${i}`,
 | 
						
						
						
							|  |  |           due_date: '2025-12-31',
 | 
						
						
						
							|  |  |           group_id: 'group-id@g.us',
 | 
						
						
						
							|  |  |           created_by: '9999999999',
 | 
						
						
						
							|  |  |         });
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |       // 2 asignadas (no deben aparecer en "sin")
 | 
						
						
						
							|  |  |       TaskService.createTask({
 | 
						
						
						
							|  |  |         description: 'Asignada 1',
 | 
						
						
						
							|  |  |         due_date: '2025-10-10',
 | 
						
						
						
							|  |  |         group_id: 'group-id@g.us',
 | 
						
						
						
							|  |  |         created_by: '1111111111',
 | 
						
						
						
							|  |  |       }, [{ user_id: '1234567890', assigned_by: '1111111111' }]);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       TaskService.createTask({
 | 
						
						
						
							|  |  |         description: 'Asignada 2',
 | 
						
						
						
							|  |  |         due_date: '2025-10-11',
 | 
						
						
						
							|  |  |         group_id: 'group-id@g.us',
 | 
						
						
						
							|  |  |         created_by: '1111111111',
 | 
						
						
						
							|  |  |       }, [{ user_id: '1234567890', assigned_by: '1111111111' }]);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |             participant: '1234567890@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/t ver sin' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       const response = await WebhookServer.handleRequest(createTestRequest(payload));
 | 
						
						
						
							|  |  |       expect(response.status).toBe(200);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       const out = SimulatedResponseQueue.getQueue();
 | 
						
						
						
							|  |  |       expect(out.length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |       for (const r of out) {
 | 
						
						
						
							|  |  |         expect(r.recipient.endsWith('@g.us')).toBe(false);
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  |       const msg = out.map(x => x.message).join('\n');
 | 
						
						
						
							|  |  |       expect(msg).toContain('sin responsable');
 | 
						
						
						
							|  |  |       expect(msg).toContain('… y 2 más');
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     test('should process "/t ver sin" in DM returning instruction', async () => {
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: '1234567890@s.whatsapp.net', // DM
 | 
						
						
						
							|  |  |             participant: '1234567890@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/t ver sin' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       const response = await WebhookServer.handleRequest(createTestRequest(payload));
 | 
						
						
						
							|  |  |       expect(response.status).toBe(200);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       const out = SimulatedResponseQueue.getQueue();
 | 
						
						
						
							|  |  |       expect(out.length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |       const msg = out.map(x => x.message).join('\n');
 | 
						
						
						
							|  |  |       expect(msg).toContain('Este comando se usa en grupos');
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     test('should process "/t ver todos" in group showing "Tus tareas" + "Sin dueño (grupo actual)" with pagination in unassigned section', async () => {
 | 
						
						
						
							|  |  |       // Tus tareas (2 asignadas)
 | 
						
						
						
							|  |  |       TaskService.createTask({
 | 
						
						
						
							|  |  |         description: 'Mi Tarea 1',
 | 
						
						
						
							|  |  |         due_date: '2025-10-10',
 | 
						
						
						
							|  |  |         group_id: 'group-id@g.us',
 | 
						
						
						
							|  |  |         created_by: '2222222222',
 | 
						
						
						
							|  |  |       }, [{ user_id: '1234567890', assigned_by: '2222222222' }]);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       TaskService.createTask({
 | 
						
						
						
							|  |  |         description: 'Mi Tarea 2',
 | 
						
						
						
							|  |  |         due_date: '2025-10-11',
 | 
						
						
						
							|  |  |         group_id: 'group-id@g.us',
 | 
						
						
						
							|  |  |         created_by: '2222222222',
 | 
						
						
						
							|  |  |       }, [{ user_id: '1234567890', assigned_by: '2222222222' }]);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       // 12 sin dueño para provocar paginación
 | 
						
						
						
							|  |  |       for (let i = 1; i <= 12; i++) {
 | 
						
						
						
							|  |  |         TaskService.createTask({
 | 
						
						
						
							|  |  |           description: `Sin dueño ${i}`,
 | 
						
						
						
							|  |  |           due_date: '2025-12-31',
 | 
						
						
						
							|  |  |           group_id: 'group-id@g.us',
 | 
						
						
						
							|  |  |           created_by: '9999999999',
 | 
						
						
						
							|  |  |         });
 | 
						
						
						
							|  |  |       }
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: 'group-id@g.us',
 | 
						
						
						
							|  |  |             participant: '1234567890@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/t ver todos' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       const response = await WebhookServer.handleRequest(createTestRequest(payload));
 | 
						
						
						
							|  |  |       expect(response.status).toBe(200);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       const out = SimulatedResponseQueue.getQueue();
 | 
						
						
						
							|  |  |       expect(out.length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |       const msg = out.map(x => x.message).join('\n');
 | 
						
						
						
							|  |  |       expect(msg).toContain('Tus tareas');
 | 
						
						
						
							|  |  |       expect(msg).toContain('sin responsable');
 | 
						
						
						
							|  |  |       expect(msg).toContain('… y 2 más');
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |     test('should process "/t ver todos" in DM showing "Tus tareas" + instructive note', async () => {
 | 
						
						
						
							|  |  |       TaskService.createTask({
 | 
						
						
						
							|  |  |         description: 'Mi Tarea A',
 | 
						
						
						
							|  |  |         due_date: '2025-11-20',
 | 
						
						
						
							|  |  |         group_id: 'group-2@g.us',
 | 
						
						
						
							|  |  |         created_by: '1111111111',
 | 
						
						
						
							|  |  |       }, [{ user_id: '1234567890', assigned_by: '1111111111' }]);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       const payload = {
 | 
						
						
						
							|  |  |         event: 'messages.upsert',
 | 
						
						
						
							|  |  |         instance: 'test-instance',
 | 
						
						
						
							|  |  |         data: {
 | 
						
						
						
							|  |  |           key: {
 | 
						
						
						
							|  |  |             remoteJid: '1234567890@s.whatsapp.net', // DM
 | 
						
						
						
							|  |  |             participant: '1234567890@s.whatsapp.net'
 | 
						
						
						
							|  |  |           },
 | 
						
						
						
							|  |  |           message: { conversation: '/t ver todos' }
 | 
						
						
						
							|  |  |         }
 | 
						
						
						
							|  |  |       };
 | 
						
						
						
							|  |  |       const response = await WebhookServer.handleRequest(createTestRequest(payload));
 | 
						
						
						
							|  |  |       expect(response.status).toBe(200);
 | 
						
						
						
							|  |  | 
 | 
						
						
						
							|  |  |       const out = SimulatedResponseQueue.getQueue();
 | 
						
						
						
							|  |  |       expect(out.length).toBeGreaterThan(0);
 | 
						
						
						
							|  |  |       const msg = out.map(x => x.message).join('\n');
 | 
						
						
						
							|  |  |       expect(msg).toContain('Tus tareas');
 | 
						
						
						
							|  |  |       expect(msg).toContain('ℹ️ Para ver tareas sin responsable');
 | 
						
						
						
							|  |  |     });
 | 
						
						
						
							|  |  |   });
 | 
						
						
						
							|  |  | });
 |