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.
taskbot/tests/unit/services/group-sync.members.test.ts

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');
});
});