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,302 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { createLeadInserter, openDb } from '../src/db.mjs';
import { createRouter } from '../src/server/router.mjs';
import { registerWhatsAppRoutes } from '../src/server/routes/whatsapp.mjs';
function setup({ qr = 'raw-qr', checkNumber } = {}) {
const db = openDb(':memory:');
const calls = [];
const whatsapp = {
getStatus: () => ({ state: 'QR_REQUIRED', connected: false, qrExpiresAt: '2026-07-28T12:00:00.000Z' }),
getQr: () => qr,
connect: async () => { calls.push('connect'); },
refreshQr: async () => { calls.push('refreshQr'); },
checkNumber: checkNumber || (async (numero) => ({
numero,
status: 'confirmado',
registered: true,
resolvedNumber: '557599091780',
variant: 'sem_nono_digito',
candidatesChecked: 2
})),
disconnect: async (options) => { calls.push(options); }
};
const router = createRouter();
registerWhatsAppRoutes(router, {
db,
whatsapp,
toDataURL: async (value) => `data:image/png;base64,${value === 'raw-qr' ? 'encoded' : 'bad'}`
});
return { db, router, calls };
}
async function invoke(router, method, pathname, context = {}) {
const responses = [];
const matched = router.match(method, pathname);
assert.ok(matched, `rota ausente: ${method} ${pathname}`);
await matched.handler(null, null, {
params: matched.params,
body: null,
json: (status, data) => responses.push({ status, data }),
...context
});
return responses[0];
}
test('status pode ser consultado por qualquer usuario autenticado sem segredos', async () => {
const { router } = setup();
const response = await invoke(router, 'GET', '/api/whatsapp/status', { user: { role: 'comercial' } });
assert.equal(response.status, 200);
assert.equal(response.data.state, 'QR_REQUIRED');
assert.equal(JSON.stringify(response.data).includes('raw-qr'), false);
assert.equal(JSON.stringify(response.data).includes('auth'), false);
});
test('acoes WhatsApp rejeitam usuario nao-admin', async () => {
const { router, calls } = setup();
for (const [method, pathname] of [
['POST', '/api/whatsapp/connect'],
['POST', '/api/whatsapp/refresh-qr'],
['POST', '/api/whatsapp/check-number'],
['GET', '/api/whatsapp/qr'],
['POST', '/api/whatsapp/disconnect'],
['POST', '/api/whatsapp/logout']
]) {
const response = await invoke(router, method, pathname, { user: { role: 'comercial' } });
assert.equal(response.status, 403);
}
assert.deepEqual(calls, []);
});
test('admin diagnostica numero sem enviar mensagem', async () => {
const { db, router } = setup({
checkNumber: async () => ({
numero: '5511888880000',
status: 'erro_temporario',
registered: true,
resolvedNumber: '557599091780',
variant: 'sem_nono_digito',
candidatesChecked: 2
})
});
const response = await invoke(router, 'POST', '/api/whatsapp/check-number', {
user: { role: 'admin' }, body: { numero: '5575999091780' }
});
assert.equal(response.status, 200);
assert.equal(response.data.numero, '5575999091780');
assert.equal(response.data.status, 'confirmado');
assert.equal(response.data.registered, true);
assert.equal(response.data.resolvedNumber, '557599091780');
assert.equal(response.data.variant, 'sem_nono_digito');
assert.equal(response.data.candidatesChecked, 2);
assert.match(response.data.checked_at, /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/);
assert.deepEqual(
{ ...db.prepare('SELECT numero, status, numero_resolvido, variante FROM whatsapp_number_verifications').get() },
{
numero: '5575999091780',
status: 'confirmado',
numero_resolvido: '557599091780',
variante: 'sem_nono_digito'
}
);
db.close();
});
test('diagnostico definitivo nao cadastrado retorna 422 e persiste o resultado', async () => {
const { db, router } = setup({
checkNumber: async (numero) => ({
numero,
status: 'nao_cadastrado',
registered: false,
resolvedNumber: null,
variant: null,
candidatesChecked: 2
})
});
const response = await invoke(router, 'POST', '/api/whatsapp/check-number', {
user: { role: 'admin' }, body: { numero: '5511999990001' }
});
assert.equal(response.status, 422);
assert.equal(response.data.status, 'nao_cadastrado');
assert.match(response.data.checked_at, /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/);
assert.equal(db.prepare('SELECT status FROM whatsapp_number_verifications').get().status, 'nao_cadastrado');
db.close();
});
test('timeout de diagnostico retorna 504 e persiste erro temporario solicitado', async () => {
const { db, router } = setup({
checkNumber: async () => {
throw Object.assign(new Error('consulta excedeu o prazo'), {
statusCode: 504,
code: 'WHATSAPP_LOOKUP_TIMEOUT',
status: 'erro_temporario',
registered: false,
resolvedNumber: null,
variant: null,
candidatesChecked: 2
});
}
});
const response = await invoke(router, 'POST', '/api/whatsapp/check-number', {
user: { role: 'admin' }, body: { numero: '5511999990001' }
});
assert.equal(response.status, 504);
assert.match(response.data.erro, /prazo/);
assert.deepEqual(
{ ...db.prepare('SELECT numero, status, erro FROM whatsapp_number_verifications').get() },
{ numero: '5511999990001', status: 'erro_temporario', erro: 'consulta excedeu o prazo' }
);
db.close();
});
test('erro definitivo sem statusCode retorna 422 e ignora numero conflitante do provedor', async () => {
const { db, router } = setup({
checkNumber: async () => {
throw Object.assign(new Error('numero nao cadastrado'), {
numero: '5511888880000',
status: 'nao_cadastrado',
registered: false,
resolvedNumber: null,
variant: null
});
}
});
const response = await invoke(router, 'POST', '/api/whatsapp/check-number', {
user: { role: 'admin' }, body: { numero: '+55 (11) 99999-0001' }
});
assert.equal(response.status, 422);
assert.deepEqual(
{ ...db.prepare('SELECT numero, status FROM whatsapp_number_verifications').get() },
{ numero: '5511999990001', status: 'nao_cadastrado' }
);
db.close();
});
test('check-number valida leadId opcional antes de consultar provedor ou cache', async () => {
let providerCalls = 0;
const { db, router } = setup({
checkNumber: async () => {
providerCalls += 1;
return { registered: true };
}
});
createLeadInserter(db)({
dedup_key: 'lead-check-1', empresa: 'Empresa', whatsapp: '5511999990001', cidade: 'Santos',
produto_principal: 'ERP', produtos_json: '[]', pains_json: '[]', origem: 'teste',
telefone_valido: 1, cnpj_valido: 0, empresa_valida: 1, score: 0, classificacao: 'ruim'
});
db.exec(`
CREATE TRIGGER fail_verification_cache BEFORE INSERT ON whatsapp_number_verifications
BEGIN SELECT RAISE(FAIL, 'cache nao deveria ser tocado'); END;
`);
for (const leadId of [true, '1', '01', '1e0', 1.5, 0, -1, NaN, 999]) {
const response = await invoke(router, 'POST', '/api/whatsapp/check-number', {
user: { role: 'admin' }, body: { leadId, numero: '5511999990001' }
});
assert.deepEqual(response, { status: 404, data: { erro: 'lead nao encontrado' } });
}
assert.equal(providerCalls, 0);
assert.equal(db.prepare('SELECT COUNT(*) total FROM whatsapp_number_verifications').get().total, 0);
db.close();
});
test('check-number usa numero do corpo mesmo com contexto de lead valido', async () => {
let checkedNumber;
const { db, router } = setup({
checkNumber: async (numero) => {
checkedNumber = numero;
return { registered: false, resolvedNumber: null, variant: null, candidatesChecked: 2 };
}
});
createLeadInserter(db)({
dedup_key: 'lead-check-2', empresa: 'Empresa', whatsapp: '5511888880000', cidade: 'Santos',
produto_principal: 'ERP', produtos_json: '[]', pains_json: '[]', origem: 'teste',
telefone_valido: 1, cnpj_valido: 0, empresa_valida: 1, score: 0, classificacao: 'ruim'
});
const response = await invoke(router, 'POST', '/api/whatsapp/check-number', {
user: { role: 'admin' }, body: { leadId: 1, numero: '5511999990001' }
});
assert.equal(response.status, 422);
assert.equal(checkedNumber, '5511999990001');
assert.equal(response.data.numero, '5511999990001');
assert.equal(db.prepare('SELECT numero FROM whatsapp_number_verifications').get().numero, '5511999990001');
db.close();
});
test('falha de cache no nao cadastrado preserva 422, JSON e timestamp nulo', async () => {
const { db, router } = setup({
checkNumber: async () => ({ registered: false, resolvedNumber: null, variant: null, candidatesChecked: 2 })
});
db.exec(`
CREATE TRIGGER fail_verification_cache BEFORE INSERT ON whatsapp_number_verifications
BEGIN SELECT RAISE(FAIL, 'cache indisponivel'); END;
`);
const response = await invoke(router, 'POST', '/api/whatsapp/check-number', {
user: { role: 'admin' }, body: { numero: '5511999990001' }
});
assert.equal(response.status, 422);
assert.equal(response.data.status, 'nao_cadastrado');
assert.equal(response.data.checked_at, null);
assert.match(response.data.aviso, /verificacao local/i);
assert.doesNotMatch(response.data.aviso, /tente.*enviar|reenvie/i);
db.close();
});
test('falha de cache no timeout de lookup preserva 504, JSON e timestamp nulo', async () => {
const { db, router } = setup({
checkNumber: async () => {
throw Object.assign(new Error('consulta excedeu o prazo'), {
statusCode: 504,
status: 'erro_temporario',
registered: false,
resolvedNumber: null,
variant: null,
candidatesChecked: 2
});
}
});
db.exec(`
CREATE TRIGGER fail_verification_cache BEFORE INSERT ON whatsapp_number_verifications
BEGIN SELECT RAISE(FAIL, 'cache indisponivel'); END;
`);
const response = await invoke(router, 'POST', '/api/whatsapp/check-number', {
user: { role: 'admin' }, body: { numero: '5511999990001' }
});
assert.equal(response.status, 504);
assert.equal(response.data.status, 'erro_temporario');
assert.equal(response.data.checked_at, null);
assert.match(response.data.erro, /prazo/);
assert.match(response.data.aviso, /verificacao local/i);
assert.doesNotMatch(response.data.aviso, /tente.*enviar|reenvie/i);
db.close();
});
test('admin conecta, obtem QR vigente como data URL e encerra ou remove sessao', async () => {
const { router, calls } = setup();
const user = { role: 'admin' };
assert.equal((await invoke(router, 'POST', '/api/whatsapp/connect', { user })).status, 202);
assert.equal((await invoke(router, 'POST', '/api/whatsapp/refresh-qr', { user })).status, 202);
const qr = await invoke(router, 'GET', '/api/whatsapp/qr', { user });
assert.deepEqual(qr, {
status: 200,
data: { qr: 'data:image/png;base64,encoded', expiresAt: '2026-07-28T12:00:00.000Z' }
});
assert.equal((await invoke(router, 'POST', '/api/whatsapp/disconnect', { user })).status, 200);
assert.equal((await invoke(router, 'POST', '/api/whatsapp/logout', { user })).status, 200);
assert.deepEqual(calls, ['connect', 'refreshQr', { logout: false }, { logout: true }]);
});
test('QR expirado retorna conflito sem chamar gerador', async () => {
const { router } = setup({ qr: null });
const response = await invoke(router, 'GET', '/api/whatsapp/qr', { user: { role: 'admin' } });
assert.equal(response.status, 409);
assert.match(response.data.erro, /QR/);
});