El bot de Luisa puebla la BD vía los 4 EPs HTTP (no SQL directo): conversacion, perfil, calificacion, intento. Actualiza el handoff de Simón en consecuencia (qué EP usa para cada cosa, enums/tipos a alinear, ya no necesita acceso a BD). Añade api-docs/smoke-bot-eps.mjs: crea un lead de prueba, ejerce los 4 EPs por HTTP, verifica en BD y limpia. Verificado end-to-end en produccion. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
108 lines
4.8 KiB
JavaScript
108 lines
4.8 KiB
JavaScript
// Smoke test de los EPs del bot de WhatsApp contra un entorno real.
|
|
// Crea un lead de PRUEBA, ejerce los 4 EPs por HTTP, verifica las escrituras en la BD y borra
|
|
// el lead (el cascade limpia las filas hijas). No toca leads reales.
|
|
//
|
|
// Uso (PowerShell):
|
|
// $env:DATABASE_URL="postgres://..."; $env:BASE_URL="https://reformix.dv3.com.es"; $env:FUNNEL_API_KEY="..."
|
|
// node mvp/b2c/api-docs/smoke-bot-eps.mjs
|
|
//
|
|
// DATABASE_URL solo hace falta para crear/verificar/limpiar el lead de prueba; el bot real
|
|
// únicamente necesita BASE_URL + FUNNEL_API_KEY para usar los EPs.
|
|
|
|
import postgres from 'postgres';
|
|
|
|
const { DATABASE_URL, BASE_URL, FUNNEL_API_KEY } = process.env;
|
|
if (!DATABASE_URL || !BASE_URL || !FUNNEL_API_KEY) {
|
|
console.error('Faltan env: DATABASE_URL, BASE_URL y FUNNEL_API_KEY son obligatorias.');
|
|
process.exit(1);
|
|
}
|
|
|
|
const sql = postgres(DATABASE_URL, { prepare: false });
|
|
|
|
async function api(path, body) {
|
|
const r = await fetch(`${BASE_URL}${path}`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${FUNNEL_API_KEY}` },
|
|
body: JSON.stringify(body),
|
|
});
|
|
const json = await r.json().catch(() => null);
|
|
return { status: r.status, json };
|
|
}
|
|
|
|
let leadId;
|
|
let ok = true;
|
|
const check = (label, cond, extra) => {
|
|
ok = ok && cond;
|
|
console.log(`${cond ? 'OK ' : 'FAIL'} ${label}${extra ? ` ${extra}` : ''}`);
|
|
};
|
|
|
|
try {
|
|
const [tenant] = await sql`select id, nombre from tenants limit 1`;
|
|
if (!tenant) throw new Error('No hay tenants en la BD.');
|
|
console.log(`tenant: ${tenant.nombre} (${tenant.id})`);
|
|
|
|
const inserted = await sql`
|
|
insert into leads (tenant_id, nombre, telefono, email, tipo_reforma, consent_privacidad, consent_contratacion)
|
|
values (${tenant.id}, 'TEST EP Bot (smoke)', '+34600000000', 'smoke-bot-ep@example.com', 'cocina', true, true)
|
|
returning id`;
|
|
leadId = inserted[0].id;
|
|
console.log(`lead de prueba creado: ${leadId}\n`);
|
|
|
|
const r1 = await api(`/api/leads/${leadId}/conversacion`, {
|
|
rol: 'user', mensaje: 'Quiero reformar la cocina', estadoWa: 'leido', botStep: 'espacio',
|
|
});
|
|
check('POST conversacion → 200', r1.status === 200, JSON.stringify(r1.json));
|
|
|
|
const r2 = await api(`/api/leads/${leadId}/perfil`, {
|
|
tipoReforma: 'cocina', m2Suelo: 12.5, calidadGlobal: 'premium', urgencia: 'alta', viable: true, botStep: 'tamano',
|
|
});
|
|
check('POST perfil → 200', r2.status === 200, JSON.stringify(r2.json));
|
|
|
|
const r3a = await api(`/api/leads/${leadId}/calificacion`, { score: 60, nivel: 'C', criterios: { fase: 'inicial' } });
|
|
check('POST calificacion #1 (insert) → 200', r3a.status === 200, JSON.stringify(r3a.json));
|
|
const r3b = await api(`/api/leads/${leadId}/calificacion`, { score: 82, nivel: 'A', notasAgente: 'Lead caliente' });
|
|
check('POST calificacion #2 (upsert) → 200', r3b.status === 200, JSON.stringify(r3b.json));
|
|
|
|
const r4 = await api(`/api/leads/${leadId}/intento`, {
|
|
canal: 'whatsapp', numeroIntento: 1, resultado: 'exitoso', completado: true, metadata: { origen: 'smoke' },
|
|
});
|
|
check('POST intento → 200', r4.status === 200, JSON.stringify(r4.json));
|
|
|
|
console.log('\n— verificación en BD —');
|
|
const conv = await sql`select rol, mensaje from conversacion_whatsapp where lead_id=${leadId}`;
|
|
check('conversacion_whatsapp: 1 turno', conv.length === 1, JSON.stringify(conv));
|
|
|
|
const [lead] = await sql`
|
|
select tipo_reforma, m2_suelo, calidad_global, urgencia, viable, bot_step, estado_wa from leads where id=${leadId}`;
|
|
check(
|
|
'leads(perfil): cocina/12.5/premium/alta/viable + bot_step=tamano + estado_wa=leido',
|
|
lead.tipo_reforma === 'cocina' && Number(lead.m2_suelo) === 12.5 && lead.calidad_global === 'premium' &&
|
|
lead.urgencia === 'alta' && lead.viable === true && lead.bot_step === 'tamano' && lead.estado_wa === 'leido',
|
|
JSON.stringify(lead),
|
|
);
|
|
|
|
const calif = await sql`select score, nivel, notas_agente from lead_calificacion where lead_id=${leadId}`;
|
|
check(
|
|
'lead_calificacion: 1 fila tras upsert, score=82 nivel=A notas conservadas',
|
|
calif.length === 1 && calif[0].score === 82 && calif[0].nivel === 'A' && calif[0].notas_agente === 'Lead caliente',
|
|
JSON.stringify(calif),
|
|
);
|
|
|
|
const intentos = await sql`select canal, resultado, completado, numero_intento from intentos_contacto where lead_id=${leadId}`;
|
|
check(
|
|
'intentos_contacto: 1 intento whatsapp exitoso completado',
|
|
intentos.length === 1 && intentos[0].canal === 'whatsapp' && intentos[0].resultado === 'exitoso' &&
|
|
intentos[0].completado === true && intentos[0].numero_intento === 1,
|
|
JSON.stringify(intentos),
|
|
);
|
|
} finally {
|
|
if (leadId) {
|
|
await sql`delete from leads where id=${leadId}`;
|
|
console.log(`\nlead de prueba borrado: ${leadId}`);
|
|
}
|
|
await sql.end();
|
|
}
|
|
|
|
console.log(`\n${ok ? 'TODO OK ✅' : 'HAY FALLOS ❌'}`);
|
|
process.exit(ok ? 0 : 1);
|