import test from 'node:test'; import assert from 'node:assert/strict'; import { openDb, createLeadInserter } from '../src/db.mjs'; import { createRouter } from '../src/server/router.mjs'; import { registerCampaignRoutes } from '../src/server/routes/campaigns.mjs'; function setupRouter(db) { const router = createRouter(); registerCampaignRoutes(router, { db }); return router; } function fakeJson() { const calls = []; return { json: (status, data) => calls.push({ status, data }), calls }; } function inserirLead(db, dedup, produto = 'ERP', segmento = 'oficinas') { const inserir = createLeadInserter(db); inserir({ dedup_key: dedup, empresa: 'Empresa', legal_name: '', whatsapp: `551199990${dedup}`, ddd: '11', email: '', cidade: '', uf: '', segmento, segmento_label: '', interesse: '', cnpj: '', cnae: '', endereco: '', website: '', pains_json: '[]', origem: 't', telefone_valido: 1, cnpj_valido: 0, empresa_valida: 1, score: 50, classificacao: 'bom', motivo_classificacao: '', score_qualidade: 50, score_comercial: 50, prioridade: 1, produto_principal: produto, produtos_json: '[]', capture_run_id: null, captured_at: null, exported_at: null }); } test('POST /api/campaigns cria campanha com segmentos e variantes', async () => { const db = openDb(':memory:'); const router = setupRouter(db); const { handler } = router.match('POST', '/api/campaigns'); const { json, calls } = fakeJson(); await handler(null, null, { json, body: { nome: 'ERP oficinas', produto: 'ERP', segmentos: ['oficinas'], variantes: [{ mensagem: 'Ola {empresa}' }] } }); assert.equal(calls[0].status, 200); assert.equal(calls[0].data.produto, 'ERP'); assert.equal(calls[0].data.variants.length, 1); db.close(); }); test('POST /api/campaigns rejeita sem segmentos', async () => { const db = openDb(':memory:'); const router = setupRouter(db); const { handler } = router.match('POST', '/api/campaigns'); const { json, calls } = fakeJson(); await handler(null, null, { json, body: { nome: 'X', produto: 'ERP', segmentos: [], variantes: [{ mensagem: 'oi' }] } }); assert.equal(calls[0].status, 400); db.close(); }); test('GET /api/campaigns/segmentos lista segmentos distintos do produto com contagem', async () => { const db = openDb(':memory:'); inserirLead(db, '1', 'ERP', 'oficinas'); inserirLead(db, '2', 'ERP', 'oficinas'); inserirLead(db, '3', 'ERP', 'padarias'); inserirLead(db, '4', 'G-Chat', 'padarias'); const router = setupRouter(db); const { handler } = router.match('GET', '/api/campaigns/segmentos'); const { json, calls } = fakeJson(); await handler(null, null, { json, query: new URLSearchParams({ produto: 'ERP' }) }); assert.equal(calls[0].status, 200); const segmentos = calls[0].data.segmentos; assert.equal(segmentos.find((s) => s.value === 'oficinas').total, 2); assert.equal(segmentos.find((s) => s.value === 'padarias').total, 1); db.close(); }); test('fluxo completo: criar campanha, gerar fila, aprovar e checar saude', async () => { const db = openDb(':memory:'); inserirLead(db, '1', 'ERP', 'oficinas'); const router = setupRouter(db); const criar = router.match('POST', '/api/campaigns'); const respCriar = fakeJson(); await criar.handler(null, null, { json: respCriar.json, body: { nome: 'C', produto: 'ERP', segmentos: ['oficinas'], variantes: [{ mensagem: 'Ola {empresa}' }] } }); const campaignId = respCriar.calls[0].data.id; const gerarFila = router.match('POST', `/api/campaigns/${campaignId}/gerar-fila`); const respFila = fakeJson(); await gerarFila.handler(null, null, { json: respFila.json, params: { id: String(campaignId) }, body: {} }); assert.equal(respFila.calls[0].data.adicionadosNaFila, 1); const listarFila = router.match('GET', `/api/campaigns/${campaignId}/fila`); const respLista = fakeJson(); await listarFila.handler(null, null, { json: respLista.json, params: { id: String(campaignId) }, query: new URLSearchParams() }); const queueId = respLista.calls[0].data.fila[0].id; const aprovar = router.match('POST', `/api/campaigns/${campaignId}/fila/aprovar`); const respAprovar = fakeJson(); await aprovar.handler(null, null, { json: respAprovar.json, params: { id: String(campaignId) }, body: { queueIds: [queueId] } }); assert.equal(respAprovar.calls[0].data.aprovados, 1); const saude = router.match('GET', `/api/campaigns/${campaignId}/saude`); const respSaude = fakeJson(); await saude.handler(null, null, { json: respSaude.json, params: { id: String(campaignId) } }); assert.equal(respSaude.calls[0].data.pause, false); db.close(); }); test('POST /api/campaigns/leads/:leadId/estagio grava estagio atual e historico', async () => { const db = openDb(':memory:'); inserirLead(db, '1'); const lead = db.prepare('SELECT id FROM leads LIMIT 1').get(); const router = setupRouter(db); const marcar = router.match('POST', '/api/campaigns/leads/:leadId/estagio'); const respMarcar = fakeJson(); await marcar.handler(null, null, { json: respMarcar.json, params: { leadId: String(lead.id) }, body: { stage: 'interessado', note: 'ligar amanha' } }); assert.equal(respMarcar.calls[0].status, 200); const consultar = router.match('GET', '/api/campaigns/leads/:leadId/estagio'); const respConsultar = fakeJson(); await consultar.handler(null, null, { json: respConsultar.json, params: { leadId: String(lead.id) } }); assert.equal(respConsultar.calls[0].data.atual.stage, 'interessado'); assert.equal(respConsultar.calls[0].data.historico.length, 1); db.close(); }); test('POST /api/campaigns/opt-out bloqueia e DELETE libera', async () => { const db = openDb(':memory:'); const router = setupRouter(db); const adicionar = router.match('POST', '/api/campaigns/opt-out'); await adicionar.handler(null, null, { json: fakeJson().json, body: { numero: '5511999990001', motivo: 'pediu' } }); assert.ok(db.prepare('SELECT 1 FROM outreach_opt_out WHERE numero = ?').get('5511999990001')); const remover = router.match('DELETE', '/api/campaigns/opt-out/:numero'); await remover.handler(null, null, { json: fakeJson().json, params: { numero: '5511999990001' } }); assert.equal(db.prepare('SELECT 1 FROM outreach_opt_out WHERE numero = ?').get('5511999990001'), undefined); db.close(); });