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.
		
		
		
		
		
			
		
			
				
	
	
		
			109 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			109 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			TypeScript
		
	
| import { Database } from 'bun:sqlite';
 | |
| import { beforeAll, beforeEach, afterAll, describe, expect, test } from 'bun:test';
 | |
| import { initializeDatabase } from '../../../src/db';
 | |
| import { GroupSyncService } from '../../../src/services/group-sync';
 | |
| 
 | |
| describe('GroupSyncService - reconcileGroupMembers', () => {
 | |
|   let memdb: Database;
 | |
| 
 | |
|   beforeAll(() => {
 | |
|     memdb = new Database(':memory:');
 | |
|     memdb.exec('PRAGMA foreign_keys = ON;');
 | |
|     initializeDatabase(memdb);
 | |
|     // Inyectar DB en el servicio
 | |
|     GroupSyncService.dbInstance = memdb as any;
 | |
|   });
 | |
| 
 | |
|   afterAll(() => {
 | |
|     memdb.close();
 | |
|   });
 | |
| 
 | |
|   beforeEach(() => {
 | |
|     // Limpiar tablas relevantes entre tests
 | |
|     memdb.exec('DELETE FROM group_members');
 | |
|     memdb.exec('DELETE FROM users');
 | |
|     memdb.exec('DELETE FROM groups');
 | |
| 
 | |
|     // Crear grupo base activo
 | |
|     memdb.prepare(`INSERT INTO groups (id, community_id, name) VALUES (?, ?, ?)`)
 | |
|       .run('123@g.us', 'community-1', 'Grupo 123');
 | |
|   });
 | |
| 
 | |
|   function getMember(userId: string) {
 | |
|     return memdb.prepare(`
 | |
|       SELECT group_id, user_id, is_admin, is_active, first_seen_at, last_seen_at, last_role_change_at
 | |
|       FROM group_members
 | |
|       WHERE group_id = ? AND user_id = ?
 | |
|     `).get('123@g.us', userId) as any;
 | |
|   }
 | |
| 
 | |
|   test('inserta miembros y marca activos en la primera reconciliación', () => {
 | |
|     const res = GroupSyncService.reconcileGroupMembers('123@g.us', [
 | |
|       { userId: '111', isAdmin: true },
 | |
|       { userId: '222', isAdmin: false }
 | |
|     ], '2025-01-01 00:00:00.000');
 | |
| 
 | |
|     expect(res).toEqual({ added: 2, updated: 0, deactivated: 0 });
 | |
| 
 | |
|     const m1 = getMember('111');
 | |
|     const m2 = getMember('222');
 | |
| 
 | |
|     expect(m1).toBeDefined();
 | |
|     expect(m1.is_active).toBe(1);
 | |
|     expect(m1.is_admin).toBe(1);
 | |
|     expect(m1.first_seen_at).toBe('2025-01-01 00:00:00.000');
 | |
|     expect(m1.last_seen_at).toBe('2025-01-01 00:00:00.000');
 | |
| 
 | |
|     expect(m2).toBeDefined();
 | |
|     expect(m2.is_active).toBe(1);
 | |
|     expect(m2.is_admin).toBe(0);
 | |
|   });
 | |
| 
 | |
|   test('actualiza roles, desactiva ausentes y añade nuevos en reconciliaciones posteriores', () => {
 | |
|     // Primera pasada
 | |
|     GroupSyncService.reconcileGroupMembers('123@g.us', [
 | |
|       { userId: '111', isAdmin: true },
 | |
|       { userId: '222', isAdmin: false }
 | |
|     ], '2025-01-01 00:00:00.000');
 | |
| 
 | |
|     // Segunda pasada con cambios: 111 pierde admin, 222 desaparece, 333 aparece
 | |
|     const res2 = GroupSyncService.reconcileGroupMembers('123@g.us', [
 | |
|       { userId: '111', isAdmin: false },
 | |
|       { userId: '333', isAdmin: false }
 | |
|     ], '2025-01-02 00:00:00.000');
 | |
| 
 | |
|     expect(res2).toEqual({ added: 1, updated: 1, deactivated: 1 });
 | |
| 
 | |
|     const m111 = getMember('111');
 | |
|     const m222 = getMember('222');
 | |
|     const m333 = getMember('333');
 | |
| 
 | |
|     expect(m111.is_active).toBe(1);
 | |
|     expect(m111.is_admin).toBe(0);
 | |
|     expect(m111.last_role_change_at).toBe('2025-01-02 00:00:00.000');
 | |
|     expect(m222.is_active).toBe(0);
 | |
|     expect(m333.is_active).toBe(1);
 | |
|     expect(m333.is_admin).toBe(0);
 | |
|   });
 | |
| 
 | |
|   test('idempotencia: aplicar mismo snapshot no altera contadores y actualiza last_seen_at', () => {
 | |
|     GroupSyncService.reconcileGroupMembers('123@g.us', [
 | |
|       { userId: '111', isAdmin: false },
 | |
|       { userId: '333', isAdmin: false }
 | |
|     ], '2025-01-02 00:00:00.000');
 | |
| 
 | |
|     const res3 = GroupSyncService.reconcileGroupMembers('123@g.us', [
 | |
|       { userId: '111', isAdmin: false },
 | |
|       { userId: '333', isAdmin: false }
 | |
|     ], '2025-01-03 00:00:00.000');
 | |
| 
 | |
|     expect(res3).toEqual({ added: 0, updated: 0, deactivated: 0 });
 | |
| 
 | |
|     const m111 = getMember('111');
 | |
|     const m333 = getMember('333');
 | |
| 
 | |
|     expect(m111.last_seen_at).toBe('2025-01-03 00:00:00.000');
 | |
|     expect(m333.last_seen_at).toBe('2025-01-03 00:00:00.000');
 | |
|   });
 | |
| });
 |