test: añade tests unitarios para ResponseQueue y task_origins
Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>main
parent
db8d22c04c
commit
1a93e7aee0
@ -0,0 +1,55 @@
|
||||
import { describe, it, expect, beforeAll, beforeEach } from 'bun:test';
|
||||
import { Database } from 'bun:sqlite';
|
||||
import { initializeDatabase } from '../../../src/db';
|
||||
import { TaskService } from '../../../src/tasks/service';
|
||||
import { CommandService } from '../../../src/services/command';
|
||||
import { GroupSyncService } from '../../../src/services/group-sync';
|
||||
|
||||
describe('CommandService - inserta task_origins al crear en grupo con messageId', () => {
|
||||
let memdb: Database;
|
||||
|
||||
beforeAll(() => {
|
||||
memdb = new Database(':memory:');
|
||||
initializeDatabase(memdb);
|
||||
(TaskService as any).dbInstance = memdb;
|
||||
(CommandService as any).dbInstance = memdb;
|
||||
|
||||
// Sembrar grupo activo y cache
|
||||
memdb.exec(`
|
||||
INSERT OR IGNORE INTO groups (id, community_id, name, active, archived, is_community, last_verified)
|
||||
VALUES ('g1@g.us', 'comm-1', 'G1', 1, 0, 0, strftime('%Y-%m-%d %H:%M:%f','now'))
|
||||
`);
|
||||
try { (GroupSyncService as any).dbInstance = memdb; } catch {}
|
||||
GroupSyncService.activeGroupsCache?.clear?.();
|
||||
GroupSyncService.activeGroupsCache?.set?.('g1@g.us', 'G1');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
process.env.NODE_ENV = 'test';
|
||||
memdb.exec('DELETE FROM task_assignments; DELETE FROM tasks; DELETE FROM task_origins;');
|
||||
});
|
||||
|
||||
it('crea tarea en grupo y registra (task_id, chat_id, message_id)', async () => {
|
||||
const sender = '600111222';
|
||||
const res = await CommandService.handle({
|
||||
sender,
|
||||
groupId: 'g1@g.us',
|
||||
message: '/t n pruebas origen 2099-01-05',
|
||||
mentions: [],
|
||||
messageId: 'MSG-ORIG-1'
|
||||
});
|
||||
|
||||
expect(res.length).toBeGreaterThan(0);
|
||||
|
||||
const t = memdb.prepare(`SELECT id FROM tasks ORDER BY id DESC LIMIT 1`).get() as any;
|
||||
expect(t).toBeTruthy();
|
||||
const row = memdb.prepare(`
|
||||
SELECT task_id, chat_id, message_id FROM task_origins WHERE task_id = ?
|
||||
`).get(Number(t.id)) as any;
|
||||
|
||||
expect(row).toBeTruthy();
|
||||
expect(Number(row.task_id)).toBe(Number(t.id));
|
||||
expect(String(row.chat_id)).toBe('g1@g.us');
|
||||
expect(String(row.message_id)).toBe('MSG-ORIG-1');
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,82 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
|
||||
import { Database } from 'bun:sqlite';
|
||||
import { initializeDatabase } from '../../../src/db';
|
||||
import { ResponseQueue } from '../../../src/services/response-queue';
|
||||
|
||||
const ORIGINAL_FETCH = globalThis.fetch;
|
||||
const envBackup = { ...process.env };
|
||||
|
||||
describe('ResponseQueue - jobs de reacción (enqueue + sendOne)', () => {
|
||||
let memdb: Database;
|
||||
let captured: { url?: string; payload?: any } = {};
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = {
|
||||
...envBackup,
|
||||
NODE_ENV: 'test',
|
||||
EVOLUTION_API_URL: 'http://evolution.test',
|
||||
EVOLUTION_API_INSTANCE: 'instance-1',
|
||||
EVOLUTION_API_KEY: 'apikey',
|
||||
RQ_REACTIONS_MAX_ATTEMPTS: '3',
|
||||
};
|
||||
|
||||
memdb = new Database(':memory:');
|
||||
memdb.exec('PRAGMA foreign_keys = ON;');
|
||||
initializeDatabase(memdb);
|
||||
|
||||
(ResponseQueue as any).dbInstance = memdb;
|
||||
|
||||
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' }
|
||||
});
|
||||
};
|
||||
|
||||
memdb.exec('DELETE FROM response_queue');
|
||||
captured = {};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
globalThis.fetch = ORIGINAL_FETCH;
|
||||
process.env = envBackup;
|
||||
try { memdb.close(); } catch {}
|
||||
});
|
||||
|
||||
it('enqueueReaction aplica idempotencia por (chatId, messageId, emoji) en ventana 24h', async () => {
|
||||
await ResponseQueue.enqueueReaction('123@g.us', 'MSG-1', '🤖');
|
||||
await ResponseQueue.enqueueReaction('123@g.us', 'MSG-1', '🤖'); // duplicado → ignorar
|
||||
|
||||
const cnt = memdb.prepare(`SELECT COUNT(*) AS c FROM response_queue`).get() as any;
|
||||
expect(Number(cnt.c)).toBe(1);
|
||||
|
||||
// Mismo chat y mensaje, emoji distinto → debe insertar
|
||||
await ResponseQueue.enqueueReaction('123@g.us', 'MSG-1', '⚠️');
|
||||
const cnt2 = memdb.prepare(`SELECT COUNT(*) AS c FROM response_queue`).get() as any;
|
||||
expect(Number(cnt2.c)).toBe(2);
|
||||
});
|
||||
|
||||
it('sendOne con metadata.kind === "reaction" usa /message/sendReaction y payload esperado', async () => {
|
||||
const item = {
|
||||
id: 42,
|
||||
recipient: '123@g.us',
|
||||
message: '', // no se usa para reaction
|
||||
attempts: 0,
|
||||
metadata: JSON.stringify({ kind: 'reaction', emoji: '🤖', chatId: '123@g.us', messageId: 'MSG-99' }),
|
||||
};
|
||||
|
||||
const res = await ResponseQueue.sendOne(item as any);
|
||||
expect(res.ok).toBe(true);
|
||||
|
||||
expect(captured.url?.includes('/message/sendReaction/instance-1')).toBe(true);
|
||||
expect(captured.payload).toBeDefined();
|
||||
expect(captured.payload.reaction).toBe('🤖');
|
||||
expect(captured.payload.key).toEqual({ remoteJid: '123@g.us', fromMe: true, id: 'MSG-99' });
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue