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.
		
		
		
		
		
			
		
			
				
	
	
		
			132 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			132 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			TypeScript
		
	
| import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'bun:test';
 | |
| import { Database } from 'bun:sqlite';
 | |
| import { initializeDatabase } from '../../../src/db';
 | |
| import { WebhookServer } from '../../../src/server';
 | |
| import { ResponseQueue } from '../../../src/services/response-queue';
 | |
| import { AllowedGroups } from '../../../src/services/allowed-groups';
 | |
| import { GroupSyncService } from '../../../src/services/group-sync';
 | |
| 
 | |
| function makePayload(event: string, data: any) {
 | |
|   return {
 | |
|     event,
 | |
|     instance: 'test-instance',
 | |
|     data
 | |
|   };
 | |
| }
 | |
| 
 | |
| async function postWebhook(payload: any) {
 | |
|   const req = new Request('http://localhost/webhook', {
 | |
|     method: 'POST',
 | |
|     headers: { 'Content-Type': 'application/json' },
 | |
|     body: JSON.stringify(payload)
 | |
|   });
 | |
|   return await WebhookServer.handleRequest(req);
 | |
| }
 | |
| 
 | |
| describe('WebhookServer E2E - reacciones por comando', () => {
 | |
|   let memdb: Database;
 | |
|   const envBackup = { ...process.env };
 | |
| 
 | |
|   beforeAll(() => {
 | |
|     memdb = new Database(':memory:');
 | |
|     initializeDatabase(memdb);
 | |
|     (WebhookServer as any).dbInstance = memdb;
 | |
|     (ResponseQueue as any).dbInstance = memdb;
 | |
|     (AllowedGroups as any).dbInstance = memdb;
 | |
|     (GroupSyncService as any).dbInstance = memdb;
 | |
|   });
 | |
| 
 | |
|   afterAll(() => {
 | |
|     process.env = envBackup;
 | |
|     try { memdb.close(); } catch {}
 | |
|   });
 | |
| 
 | |
|   beforeEach(() => {
 | |
|     process.env = {
 | |
|       ...envBackup,
 | |
|       NODE_ENV: 'test',
 | |
|       REACTIONS_ENABLED: 'true',
 | |
|       REACTIONS_SCOPE: 'groups',
 | |
|       GROUP_GATING_MODE: 'enforce',
 | |
|       CHATBOT_PHONE_NUMBER: '999'
 | |
|     };
 | |
|     memdb.exec(`
 | |
|       DELETE FROM response_queue;
 | |
|       DELETE FROM task_assignments;
 | |
|       DELETE FROM tasks;
 | |
|       DELETE FROM users;
 | |
|       DELETE FROM groups;
 | |
|       DELETE FROM allowed_groups;
 | |
|     `);
 | |
|     GroupSyncService.activeGroupsCache?.clear?.();
 | |
|   });
 | |
| 
 | |
|   it('encola 🤖 en grupo allowed y activo tras /t n', async () => {
 | |
|     const groupId = 'g1@g.us';
 | |
|     // Sembrar grupo activo y allowed
 | |
|     memdb.exec(`
 | |
|       INSERT OR IGNORE INTO groups (id, community_id, name, active, archived, is_community, last_verified)
 | |
|       VALUES ('${groupId}', 'comm-1', 'G1', 1, 0, 0, strftime('%Y-%m-%d %H:%M:%f','now'))
 | |
|     `);
 | |
|     GroupSyncService.activeGroupsCache.set(groupId, 'G1');
 | |
|     AllowedGroups.setStatus(groupId, 'allowed');
 | |
| 
 | |
|     const payload = makePayload('MESSAGES_UPSERT', {
 | |
|       key: { remoteJid: groupId, id: 'MSG-OK-1', fromMe: false, participant: '600111222@s.whatsapp.net' },
 | |
|       message: { conversation: '/t n prueba e2e' }
 | |
|     });
 | |
| 
 | |
|     const res = await postWebhook(payload);
 | |
|     expect(res.status).toBe(200);
 | |
| 
 | |
|     const row = memdb.prepare(`SELECT metadata FROM response_queue WHERE metadata LIKE '%"kind":"reaction"%' ORDER BY id DESC LIMIT 1`).get() as any;
 | |
|     expect(row).toBeTruthy();
 | |
|     const meta = JSON.parse(String(row.metadata));
 | |
|     expect(meta.kind).toBe('reaction');
 | |
|     expect(meta.emoji).toBe('🤖');
 | |
|     expect(meta.chatId).toBe(groupId);
 | |
|     expect(meta.messageId).toBe('MSG-OK-1');
 | |
|   });
 | |
| 
 | |
|   it('no encola reacción en DM cuando REACTIONS_SCOPE=groups', async () => {
 | |
|     const dmJid = '600111222@s.whatsapp.net';
 | |
| 
 | |
|     const payload = makePayload('MESSAGES_UPSERT', {
 | |
|       key: { remoteJid: dmJid, id: 'MSG-DM-1', fromMe: false },
 | |
|       message: { conversation: '/t n en DM no reacciona' }
 | |
|     });
 | |
| 
 | |
|     const res = await postWebhook(payload);
 | |
|     expect(res.status).toBe(200);
 | |
| 
 | |
|     const cnt = memdb.prepare(`SELECT COUNT(*) AS c FROM response_queue WHERE metadata LIKE '%"kind":"reaction"%'`).get() as any;
 | |
|     expect(Number(cnt.c)).toBe(0);
 | |
|   });
 | |
| 
 | |
|   it('encola ⚠️ en grupo allowed y activo para comando inválido (/t x sin IDs)', async () => {
 | |
|     const groupId = 'g2@g.us';
 | |
|     memdb.exec(`
 | |
|       INSERT OR IGNORE INTO groups (id, community_id, name, active, archived, is_community, last_verified)
 | |
|       VALUES ('${groupId}', 'comm-1', 'G2', 1, 0, 0, strftime('%Y-%m-%d %H:%M:%f','now'))
 | |
|     `);
 | |
|     GroupSyncService.activeGroupsCache.set(groupId, 'G2');
 | |
|     AllowedGroups.setStatus(groupId, 'allowed');
 | |
| 
 | |
|     const payload = makePayload('MESSAGES_UPSERT', {
 | |
|       key: { remoteJid: groupId, id: 'MSG-ERR-1', fromMe: false, participant: '600111222@s.whatsapp.net' },
 | |
|       message: { conversation: '/t x' }
 | |
|     });
 | |
| 
 | |
|     const res = await postWebhook(payload);
 | |
|     expect(res.status).toBe(200);
 | |
| 
 | |
|     const row = memdb.prepare(`SELECT metadata FROM response_queue WHERE metadata LIKE '%"kind":"reaction"%' ORDER BY id DESC LIMIT 1`).get() as any;
 | |
|     expect(row).toBeTruthy();
 | |
|     const meta = JSON.parse(String(row.metadata));
 | |
|     expect(meta.kind).toBe('reaction');
 | |
|     expect(meta.emoji).toBe('⚠️');
 | |
|     expect(meta.chatId).toBe(groupId);
 | |
|     expect(meta.messageId).toBe('MSG-ERR-1');
 | |
|   });
 | |
| });
 |