Files
COMERCIAL-GTI/maquina-comercial-pessoal/tests/browser/server.mjs
T
GustavoandClaude Sonnet 5 417da56bd6 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
2026-09-28 15:50:17 -03:00

169 lines
6.3 KiB
JavaScript

import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import { fileURLToPath } from 'node:url';
import { DatabaseSync } from 'node:sqlite';
import { openDb, setSetting } from '../../src/db.mjs';
import { hashPassword } from '../../src/auth.mjs';
import { createUser } from '../../src/db.mjs';
import { createAppServer } from '../../src/server/index.mjs';
import { seedCampaigns } from '../../src/outreach/campaign-store.mjs';
const DEBUG = process.env.DEBUG_SERVER === '1';
const log = DEBUG ? (m) => process.stderr.write(`[test-server] ${m}\n`) : () => {};
function randomHex(length) {
const bytes = new Uint8Array(Math.ceil(length / 2));
crypto.getRandomValues(bytes);
return Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('').slice(0, length);
}
export async function startTestServer() {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mcp-test-'));
const dbPath = path.join(tmpDir, 'test.db');
process.env.APP_DB_PATH = dbPath;
process.env.APP_WHATSAPP_AUTH_DIR = path.join(tmpDir, 'whatsapp-auth');
const db = openDb(dbPath);
const adminPw = 'admin-senha-teste';
createUser(db, { username: 'admin', passwordHash: hashPassword(adminPw), role: 'admin' });
seedCampaigns(db);
setSetting(db, 'outreach.mensagensPorDia', '10');
const campaignIds = db.prepare('SELECT id, code FROM outreach_campaigns ORDER BY id').all();
const leadIds = [];
const empresas = {
'1': ['Hamburgueria Bella', 'Pizzaria Famosa', 'Acaiteria Tropical', 'Marmitaria Sabor', 'Lanchonete do Ze'],
'2': ['Restaurante Chefe', 'Mercado Central', 'Mercearia Sao Jose', 'Minimercado Bom', 'Padaria Pao Quente'],
'3': ['Loja de Moda', 'Material Construcao', 'Autopecas Veloz', 'Oficina Mecanica']
};
let leadIdCounter = 1;
for (const c of campaignIds) {
const names = empresas[c.code] || ['Empresa Generica'];
for (const nome of names) {
const whatsapp = `5511999${String(leadIdCounter).padStart(6, '0')}`;
const r = db.prepare(
`INSERT INTO leads (dedup_key, empresa, whatsapp, segmento, prioridade, score, classificacao, telefone_valido)
VALUES (?, ?, ?, ?, ?, ?, 'bom', 1)`
).run(
`browser-test-lead-${leadIdCounter}`,
nome,
whatsapp,
c.code === 'gdelivery-food' ? 'hamburguerias' : c.code === 'gti-food' ? 'restaurantes' : 'lojas_de_roupa',
leadIdCounter <= 5 ? 1 : 2,
100 - leadIdCounter
);
const lid = Number(r.lastInsertRowid);
leadIds.push({ id: lid, empresa: nome, whatsapp, campaignId: c.id });
db.prepare(
`INSERT INTO whatsapp_number_verifications (numero, status, numero_resolvido, checked_at)
VALUES (?, 'confirmado', ?, datetime('now'))`
).run(whatsapp, whatsapp);
leadIdCounter += 1;
}
}
const firstLead = leadIds[0];
setSetting(db, 'outreach.ativo', 'true');
const firstCampaignId = campaignIds[0].id;
const variantIds = db.prepare(
'SELECT id FROM outreach_campaign_variants WHERE campaign_id = ? ORDER BY code LIMIT 1'
).all(firstCampaignId);
if (variantIds.length > 0 && firstLead) {
const variantId = variantIds[0].id;
const produto = db.prepare('SELECT produto FROM outreach_campaigns WHERE id = ?').get(firstCampaignId).produto;
db.prepare(
`INSERT INTO outreach_campaign_queue
(campaign_id, lead_id, variant_id, produto, numero, segmento, rendered_message, selection_reason, status)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'preview')`
).run(firstCampaignId, firstLead.id, variantId, produto, firstLead.whatsapp, 'hamburguerias',
'Ola! Teste de mensagem renderizada para campanha.', 'browser-test');
}
const stageLeadId = leadIds.length > 2 ? leadIds[2].id : (firstLead ? firstLead.id : null);
if (stageLeadId) {
db.prepare(
`INSERT INTO outreach_lead_states (lead_id, stage, first_reply_at)
VALUES (?, 'respondeu', datetime('now'))`
).run(stageLeadId);
db.prepare(
`INSERT INTO outreach_stage_events (lead_id, campaign_id, stage, note)
VALUES (?, ?, 'respondeu', 'cliente quer saber mais sobre G-Delivery')`
).run(stageLeadId, firstCampaignId);
}
const replyLeadId = leadIds.length > 1 ? leadIds[1].id : (firstLead ? firstLead.id : null);
if (replyLeadId) {
db.prepare(
`INSERT OR IGNORE INTO outreach_inbound_replies (provider_message_id, lead_id, numero, received_at)
VALUES (?, ?, ?, datetime('now'))`
).run(`test-inbound-${Date.now()}`, replyLeadId, leadIds[1]?.whatsapp || '5511999990002');
}
let fakeSends = [];
const whatsapp = {
getStatus: () => ({ state: 'READY', connected: true }),
getQr: () => null,
connect: async () => {},
disconnect: async () => {},
checkNumber: async (number) => ({
registered: true,
numero: number,
numeroResolvido: number,
status: 'confirmado'
}),
sendText: async () => {
fakeSends.push({ at: new Date().toISOString() });
throw new Error('fake manager nao envia mensagens reais');
}
};
const jobs = { subscribe: () => null, activeJobs: () => [] };
const server = createAppServer({ db, jobs, whatsapp, log });
return new Promise((resolve, reject) => {
server.listen(3911, '127.0.0.1', () => {
log(`server listening on 3911`);
const apiRequest = async (method, path, options = {}) => {
const url = `http://127.0.0.1:3911${path}`;
const headers = { ...options.headers };
if (options.body && !headers['content-type']) {
headers['content-type'] = 'application/json';
}
const response = await fetch(url, {
method,
headers,
body: options.body ? JSON.stringify(options.body) : undefined
});
let data = null;
try {
data = await response.json();
} catch {
data = null;
}
return { status: response.status, headers: response.headers, data, raw: response };
};
const teardown = async () => {
await new Promise((r) => server.close(r));
db.close();
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
log('test server teardown complete');
};
resolve({ server, db, tmpDir, whatsapp, fakeSends, apiRequest, teardown, adminPw });
});
server.on('error', reject);
});
}