test: añade tests de alias @lid y resolución en group-sync
Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>pull/1/head
parent
9b57662a6b
commit
8d1d992248
@ -0,0 +1,85 @@
|
||||
import { describe, it, beforeEach, afterEach, expect } from 'bun:test';
|
||||
import { Database } from 'bun:sqlite';
|
||||
import { initializeDatabase } from '../../../src/db';
|
||||
import { IdentityService } from '../../../src/services/identity';
|
||||
import { WebhookServer } from '../../../src/server';
|
||||
import { GroupSyncService } from '../../../src/services/group-sync';
|
||||
|
||||
const ORIGINAL_FETCH = globalThis.fetch;
|
||||
const envBackup = { ...process.env };
|
||||
|
||||
describe('Alias @lid ↔ número: aprendizaje y uso en sync de miembros', () => {
|
||||
let memdb: Database;
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = { ...envBackup };
|
||||
memdb = new Database(':memory:');
|
||||
memdb.exec('PRAGMA foreign_keys = ON;');
|
||||
initializeDatabase(memdb);
|
||||
|
||||
// Inyectar DB en servicios implicados
|
||||
(IdentityService as any).dbInstance = memdb;
|
||||
(WebhookServer as any).dbInstance = memdb;
|
||||
(GroupSyncService as any).dbInstance = memdb;
|
||||
|
||||
// Limpiar caché de grupos para evitar interferencias
|
||||
GroupSyncService.activeGroupsCache.clear();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
globalThis.fetch = ORIGINAL_FETCH;
|
||||
process.env = envBackup;
|
||||
memdb.close();
|
||||
});
|
||||
|
||||
it('aprende alias desde participantAlt y luego resuelve miembros con solo @lid', async () => {
|
||||
// 1) Simular mensaje de grupo que trae participant (alias @lid) y participantAlt (número real)
|
||||
process.env.NODE_ENV = 'test'; // evita efectos de red/timers
|
||||
const payload = {
|
||||
key: {
|
||||
remoteJid: '12345-678@g.us',
|
||||
participant: 'userXYZ@lid',
|
||||
participantAlt: '666777888@s.whatsapp.net',
|
||||
fromMe: false
|
||||
},
|
||||
message: { conversation: 'hola' }
|
||||
};
|
||||
|
||||
await WebhookServer.handleMessageUpsert(payload);
|
||||
|
||||
// Verificar que el alias fue aprendido
|
||||
const resolved = IdentityService.resolveAliasOrNull('userXYZ@lid');
|
||||
expect(resolved).toBe('666777888');
|
||||
|
||||
// Añadir un segundo alias manualmente para cubrir varios miembros
|
||||
IdentityService.upsertAlias('abc@lid', '999000111@s.whatsapp.net', 'test-seed');
|
||||
expect(IdentityService.resolveAliasOrNull('abc@lid')).toBe('999000111');
|
||||
|
||||
// 2) Ahora simular la Evolution API devolviendo solo IDs con @lid en /group/participants
|
||||
process.env.NODE_ENV = 'development'; // evitar early-return en fetchGroupMembersFromAPI
|
||||
process.env.EVOLUTION_API_URL = 'http://evolution.test';
|
||||
process.env.EVOLUTION_API_INSTANCE = 'instance-1';
|
||||
process.env.EVOLUTION_API_KEY = 'apikey';
|
||||
|
||||
globalThis.fetch = async (url: RequestInfo | URL) => {
|
||||
if (String(url).includes('/group/participants/')) {
|
||||
const body = {
|
||||
participants: [
|
||||
{ id: 'userXYZ@lid', admin: 'admin' }, // debe resolverse a 666777888 (admin)
|
||||
{ id: 'abc@lid', admin: null } // debe resolverse a 999000111 (no admin)
|
||||
]
|
||||
};
|
||||
return new Response(JSON.stringify(body), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
||||
}
|
||||
return new Response('not found', { status: 404 });
|
||||
};
|
||||
|
||||
const out = await (GroupSyncService as any).fetchGroupMembersFromAPI('12345-678@g.us');
|
||||
const map = new Map(out.map((x: any) => [x.userId, x.isAdmin]));
|
||||
|
||||
expect(map.has('666777888')).toBe(true);
|
||||
expect(map.has('999000111')).toBe(true);
|
||||
expect(map.get('666777888')).toBe(true); // admin resuelto
|
||||
expect(map.get('999000111')).toBe(false); // no admin
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,70 @@
|
||||
import { describe, it, beforeEach, afterEach, expect } from 'bun:test';
|
||||
import { Database } from 'bun:sqlite';
|
||||
import { initializeDatabase } from '../../../src/db';
|
||||
import { ResponseQueue } from '../../../src/services/response-queue';
|
||||
import { IdentityService } from '../../../src/services/identity';
|
||||
|
||||
const ORIGINAL_FETCH = globalThis.fetch;
|
||||
const envBackup = { ...process.env };
|
||||
|
||||
describe('ResponseQueue - resolución de menciones con alias (@lid)', () => {
|
||||
let memdb: Database;
|
||||
let captured: { url?: string; payload?: any } = {};
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {
|
||||
...envBackup,
|
||||
EVOLUTION_API_URL: 'http://evolution.test',
|
||||
EVOLUTION_API_INSTANCE: 'instance-1',
|
||||
EVOLUTION_API_KEY: 'apikey'
|
||||
};
|
||||
|
||||
memdb = new Database(':memory:');
|
||||
memdb.exec('PRAGMA foreign_keys = ON;');
|
||||
initializeDatabase(memdb);
|
||||
|
||||
(IdentityService as any).dbInstance = memdb;
|
||||
(ResponseQueue as any).dbInstance = memdb;
|
||||
|
||||
// Sembrar alias: userXYZ@lid -> 666777888
|
||||
IdentityService.upsertAlias('userXYZ@lid', '666777888@s.whatsapp.net', 'test');
|
||||
|
||||
// Stub fetch para capturar el payload enviado
|
||||
globalThis.fetch = async (url: RequestInfo | URL, init?: RequestInit) => {
|
||||
captured.url = String(url);
|
||||
try {
|
||||
captured.payload = init?.body ? JSON.parse(String(init.body)) : null;
|
||||
} catch {
|
||||
captured.payload = null;
|
||||
}
|
||||
return new Response(JSON.stringify({ ok: true }), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
globalThis.fetch = ORIGINAL_FETCH;
|
||||
process.env = envBackup;
|
||||
memdb.close();
|
||||
});
|
||||
|
||||
it('construye mentioned con números reales, deduplicados', async () => {
|
||||
const item = {
|
||||
id: 1,
|
||||
recipient: '123456789', // número normalizado
|
||||
message: 'hola @user',
|
||||
metadata: JSON.stringify({ mentioned: ['userXYZ@lid', '666777888@s.whatsapp.net'] }),
|
||||
attempts: 0
|
||||
};
|
||||
|
||||
const res = await ResponseQueue.sendOne(item as any);
|
||||
expect(res.ok).toBe(true);
|
||||
|
||||
expect(captured.url?.includes('/message/sendText/instance-1')).toBe(true);
|
||||
expect(captured.payload).toBeDefined();
|
||||
expect(captured.payload.number).toBe('123456789');
|
||||
expect(Array.isArray(captured.payload.mentioned)).toBe(true);
|
||||
|
||||
// Debe contener solo una entrada, resuelta a número real @s.whatsapp.net y sin duplicados
|
||||
expect(captured.payload.mentioned.sort()).toEqual(['666777888@s.whatsapp.net']);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue