import test from 'node:test'; import assert from 'node:assert/strict'; import { openDb, saveWhatsAppVerification } from '../src/db.mjs'; import { seedCampaigns } from '../src/outreach/campaign-store.mjs'; import { runOutreachBatch } from '../src/outreach/run.mjs'; import { renderTemplate } from '../src/outreach/templates.mjs'; function addScheduledQueue(db, scheduledAt = '2026-07-29T12:00:00.000Z') { seedCampaigns(db); const campaign = db.prepare('SELECT * FROM outreach_campaigns ORDER BY id LIMIT 1').get(); db.prepare("UPDATE outreach_campaigns SET status = 'running' WHERE id = ?").run(campaign.id); const leadId = Number(db.prepare( `INSERT INTO leads (dedup_key, empresa, whatsapp, segmento, telefone_valido) VALUES ('queue-lead', 'Empresa Fila', '5511999990001', 'restaurantes', 1)` ).run().lastInsertRowid); saveWhatsAppVerification(db, { numero: '5511999990001', status: 'confirmado' }); const variantId = db.prepare( 'SELECT id FROM outreach_campaign_variants WHERE campaign_id = ? ORDER BY id LIMIT 1' ).get(campaign.id).id; return Number(db.prepare( `INSERT INTO outreach_campaign_queue (campaign_id, lead_id, variant_id, produto, numero, segmento, rendered_message, selection_reason, status, scheduled_at) VALUES (?, ?, ?, ?, '5511999990001', 'restaurantes', 'Mensagem aprovada e imutavel', 'teste', 'scheduled', ?)` ).run(campaign.id, leadId, variantId, campaign.produto, scheduledAt).lastInsertRowid); } function whatsappFake() { const sent = []; return { sent, getStatus: () => ({ state: 'READY' }), sendText: async (args) => { const context = await args.beforeSend(); sent.push({ number: args.number, message: args.message }); const result = { messageId: 'queue-message-1' }; await args.afterSend(result, null, context); return result; } }; } test('renderTemplate inclui produto no contexto pessoal', () => { assert.equal(renderTemplate('{empresa}: {produto}', { empresa: 'Loja', produto: 'ERP' }), 'Loja: ERP'); }); test('runOutreachBatch consome somente uma linha aprovada e vencida da fila', async () => { const db = openDb(':memory:'); const queueId = addScheduledQueue(db); const whatsapp = whatsappFake(); const result = await runOutreachBatch({ db, whatsapp, now: new Date('2026-07-29T12:00:00.000Z'), log: () => {} }); assert.deepEqual(result, { tentados: 1, enviados: 1, erros: 0, pulados: 0 }); assert.deepEqual(whatsapp.sent, [{ number: '5511999990001', message: 'Mensagem aprovada e imutavel' }]); assert.equal(db.prepare('SELECT status FROM outreach_campaign_queue WHERE id = ?').get(queueId).status, 'sent'); assert.deepEqual( { ...db.prepare( 'SELECT mensagem_enviada, provider_message_id, status, canal, origem FROM outreach_sends' ).get() }, { mensagem_enviada: 'Mensagem aprovada e imutavel', provider_message_id: 'queue-message-1', status: 'aceito', canal: 'local', origem: 'automatico' } ); db.close(); }); test('runOutreachBatch sem WhatsApp READY preserva fila e registra adiamento', async () => { const db = openDb(':memory:'); const queueId = addScheduledQueue(db); const logs = []; let sends = 0; const result = await runOutreachBatch({ db, whatsapp: { getStatus: () => ({ state: 'DISCONNECTED' }), sendText: async () => { sends += 1; } }, now: new Date('2026-07-29T12:00:00.000Z'), log: (line) => logs.push(line) }); assert.deepEqual(result, { tentados: 0, enviados: 0, erros: 0, pulados: 0 }); assert.equal(sends, 0); assert.equal(db.prepare('SELECT status FROM outreach_campaign_queue WHERE id = ?').get(queueId).status, 'scheduled'); assert.match(logs[0], /nao esta conectado/i); db.close(); }); test('runOutreachBatch nunca seleciona lead cru sem fila supervisionada', async () => { const db = openDb(':memory:'); db.prepare( `INSERT INTO leads (dedup_key, empresa, whatsapp, segmento, telefone_valido, produto_principal) VALUES ('raw-lead', 'Lead Cru', '5511999990002', 'restaurantes', 1, 'G-Delivery')` ).run(); saveWhatsAppVerification(db, { numero: '5511999990002', status: 'confirmado' }); const whatsapp = whatsappFake(); const result = await runOutreachBatch({ db, whatsapp, now: new Date('2026-07-29T12:00:00.000Z'), log: () => {} }); assert.deepEqual(result, { tentados: 0, enviados: 0, erros: 0, pulados: 0 }); assert.equal(whatsapp.sent.length, 0); assert.equal(db.prepare('SELECT COUNT(*) AS total FROM outreach_sends').get().total, 0); db.close(); }); test('runOutreachBatch respeita cancelamento antes da reserva', async () => { const db = openDb(':memory:'); const queueId = addScheduledQueue(db); const whatsapp = whatsappFake(); const controller = new AbortController(); controller.abort(); const result = await runOutreachBatch({ db, whatsapp, now: new Date('2026-07-29T12:00:00.000Z'), signal: controller.signal, log: () => {} }); assert.deepEqual(result, { tentados: 0, enviados: 0, erros: 0, pulados: 0 }); assert.equal(db.prepare('SELECT status FROM outreach_campaign_queue WHERE id = ?').get(queueId).status, 'scheduled'); assert.equal(whatsapp.sent.length, 0); db.close(); });