chore: import maquina-comercial-pessoal history via subtree

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

git-subtree-dir: maquina-comercial-pessoal
git-subtree-mainline: f5af038e19
git-subtree-split: a9ef4d776d
This commit is contained in:
Gustavo
2026-09-28 15:50:17 -03:00
co-authored by Claude Sonnet 5
150 changed files with 33449 additions and 0 deletions
@@ -0,0 +1,146 @@
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();
});