import test from 'node:test'; import assert from 'node:assert/strict'; import { managementFocus, naturalBusinessName, renderCampaignMessage, variantForLead } from '../src/outreach/campaign-messages.mjs'; const refusal = 'Se nao fizer sentido para sua empresa, e so me avisar que nao volto a entrar em contato.'; const approvedCases = [ { segment: 'restaurantes', template: `Ola, tudo bem? Sou Gustavo, representante comercial do Grupo GTI. Hoje voces controlam {foco_gestao} em um unico sistema? ${refusal}`, expected: `Ola, tudo bem? Sou Gustavo, representante comercial do Grupo GTI. Hoje voces controlam vendas, estoque, comandas e emissão fiscal em um unico sistema? ${refusal}` }, { segment: 'restaurantes', template: `Ola! Aqui e Gustavo, do Grupo GTI. Trabalhamos com gestao para empresas de Santo Antonio de Jesus e regiao. Como voces organizam hoje {foco_gestao}? ${refusal}`, expected: `Ola! Aqui e Gustavo, do Grupo GTI. Trabalhamos com gestao para empresas de Santo Antonio de Jesus e regiao. Como voces organizam hoje vendas, estoque, comandas e emissão fiscal? ${refusal}` }, { segment: 'restaurantes', template: `Ola, tudo bem? Sou Gustavo, do Grupo GTI. Ha mais de 7 anos atendemos empresas da regiao e ja temos mais de 100 clientes. Voces utilizam algum sistema para {foco_gestao}? ${refusal}`, expected: `Ola, tudo bem? Sou Gustavo, do Grupo GTI. Ha mais de 7 anos atendemos empresas da regiao e ja temos mais de 100 clientes. Voces utilizam algum sistema para vendas, estoque, comandas e emissão fiscal? ${refusal}` }, { segment: 'pizzarias', template: `Ola, tudo bem? Sou Gustavo, representante comercial do Grupo GTI. Queria entender como voces recebem os pedidos de delivery hoje: possuem um canal proprio ou dependem de aplicativos que cobram comissao? ${refusal}`, expected: `Ola, tudo bem? Sou Gustavo, representante comercial do Grupo GTI. Queria entender como voces recebem os pedidos de delivery hoje: possuem um canal proprio ou dependem de aplicativos que cobram comissao? ${refusal}` }, { segment: 'pizzarias', template: `Ola! Aqui e Gustavo, do Grupo GTI. Voces ja possuem um link proprio para receber pedidos, sem pagar comissao por venda a aplicativos? ${refusal}`, expected: `Ola! Aqui e Gustavo, do Grupo GTI. Voces ja possuem um link proprio para receber pedidos, sem pagar comissao por venda a aplicativos? ${refusal}` }, { segment: 'pizzarias', template: `Ola, tudo bem? Sou Gustavo, representante comercial do Grupo GTI. Trabalho com uma solucao de pedidos online por canal proprio e queria primeiro entender como funciona o delivery de voces hoje. ${refusal}`, expected: `Ola, tudo bem? Sou Gustavo, representante comercial do Grupo GTI. Trabalho com uma solucao de pedidos online por canal proprio e queria primeiro entender como funciona o delivery de voces hoje. ${refusal}` } ]; test('nome natural aceita marcas curtas com pelo menos duas letras', () => { assert.equal(naturalBusinessName({ empresa: 'OX' }), 'OX'); assert.equal(naturalBusinessName({ empresa: 'BR2' }), 'BR2'); assert.equal(naturalBusinessName({ empresa: 'Pizzaria Central' }), 'Pizzaria Central'); }); test('nome natural rejeita identificadores, nomes legais, controles, placeholders e ruido generico', () => { const rejected = [ '', '7', '7 Mares', '61.590.668 A ARAUJO', '12.345.678/0001-90', 'A ARAUJO LTDA', 'Loja Sol MEI', 'Loja Sol ME', 'Loja Sol EPP', 'Loja Sol EIRELI', 'Loja Sol S/A', 'Loja\nSol', 'Loja\u0000Sol', 'Loja\u200bSol', 'Loja {nome}', 'Loja {{nome}}', 'Empresa', 'Nome Fantasia', 'Nao informado', 'Comercio', 'Pizzaria Central com um nome comercial excessivamente longo para uma saudacao natural e segura' ]; for (const empresa of rejected) { assert.equal(naturalBusinessName({ empresa, legal_name: 'A ARAUJO LTDA' }), '', empresa); } }); test('foco de gestao segue os quatro grupos aprovados', () => { assert.equal(managementFocus('restaurantes'), 'vendas, estoque, comandas e emissão fiscal'); assert.equal(managementFocus('mercados_de_bairro'), 'vendas, estoque e emissão fiscal'); assert.equal(managementFocus('oficinas_mecanicas'), 'ordens de serviço, estoque e financeiro'); assert.equal(managementFocus('lojas_de_roupa'), 'vendas, estoque, financeiro e notas fiscais'); }); test('variante permanece estavel e considera apenas variantes ativas', () => { const variants = [{ code: 'A', ativo: 1 }, { code: 'B', ativo: 1 }, { code: 'C', ativo: 1 }]; assert.equal(variantForLead(variants, 4).code, 'B'); assert.equal(variantForLead(variants, 4).code, 'B'); assert.equal(variantForLead([{ code: 'A', ativo: 0 }, ...variants.slice(1)], 4).code, 'B'); assert.throws(() => variantForLead([], 4), /variante ativa/i); }); test('variante usa a ordem ativa mesmo com ids nao contiguos', () => { const variants = [ { id: 8, code: 'A', ativo: 1 }, { id: 37, code: 'B', ativo: 1 }, { id: 104, code: 'C', ativo: 1 } ]; assert.equal(variantForLead(variants, 4), variants[1]); }); test('variante rejeita array nao vazio quando todas estao inativas', () => { assert.throws(() => variantForLead([ { id: 8, code: 'A', ativo: 0 }, { id: 37, code: 'B', ativo: false } ], 4), /variante ativa/i); }); test('renderizacao preserva os seis textos aprovados fora das substituicoes permitidas', () => { for (const { segment, template, expected } of approvedCases) { assert.equal(renderCampaignMessage(template, { empresa: '61.590.668 A ARAUJO', legal_name: 'A ARAUJO LTDA', segmento: segment }), expected); } }); test('renderizacao personaliza somente a saudacao inicial com nome natural', () => { const template = approvedCases[0].template; const rendered = renderCampaignMessage(template, { empresa: 'Pizzaria Central', segmento: 'restaurantes' }); assert.equal(rendered, `Olá, pessoal da Pizzaria Central, tudo bem? Sou Gustavo, representante comercial do Grupo GTI. Hoje voces controlam vendas, estoque, comandas e emissão fiscal em um unico sistema? ${refusal}`); }); test('validacao aceita texto portugues legitimo sem remover diacriticos', () => { const template = `Olá, tudo bem? Sou Gustavo, do Grupo GTI. Gestão ágil para comércio. Se não fizer sentido para sua empresa, é só me avisar que não volto a entrar em contato.`; assert.equal(renderCampaignMessage(template, { empresa: '', segmento: 'lojas_de_roupa' }), template); const punctuation = `Olá, tudo bem? Sou Gustavo, do Grupo GTI. Gestão simples (vendas/estoque), ágil e segura. Se não fizer sentido para sua empresa, é só me avisar que não volto a entrar em contato.`; assert.equal(renderCampaignMessage(punctuation, { empresa: '' }), punctuation); }); test('validacao rejeita placeholders simples ou duplos que restarem no texto', () => { const safe = `Ola, tudo bem? Sou Gustavo, do Grupo GTI. ${refusal}`; const placeholders = [ '{produto}', '{{produto_2}}', '{ produto }', '{produto-nome}', '{123}', '{{ produto }}', '{{produto-nome}}', '{{123}}' ]; for (const placeholder of placeholders) { const template = safe.replace('Grupo GTI.', `Grupo GTI. ${placeholder}`); assert.throws(() => renderCampaignMessage(template, {}), /mensagem inicial insegura/i, placeholder); } }); test('validacao de placeholders preserva portugues normal sem chaves', () => { const template = `Olá, tudo bem? Sou Gustavo, do Grupo GTI. Produto 123 - nome e gestão simples. Se não fizer sentido para sua empresa, é só me avisar que não volto a entrar em contato.`; assert.equal(renderCampaignMessage(template, {}), template); }); test('validacao rejeita localhost, enderecos IP e caminhos absolutos', () => { const safe = `Ola, tudo bem? Sou Gustavo, do Grupo GTI. ${refusal}`; const addresses = [ 'localhost', 'localhost:3000/agenda', '127.0.0.1', '192.168.0.1:8080/agenda', '[::1]', '[2001:db8::1]:8080/agenda', '2001:db8::1', '/agenda/demonstracao' ]; for (const address of addresses) { const template = safe.replace('Grupo GTI.', `Grupo GTI. ${address}`); assert.throws(() => renderCampaignMessage(template, {}), /mensagem inicial insegura/i, address); } }); test('renderizacao e selecao nao alteram os objetos de entrada', () => { const template = { mensagem: approvedCases[0].template }; const lead = { empresa: 'Pizzaria Central', segmento: 'restaurantes' }; const variants = [{ id: 8, code: 'A', ativo: 0 }, { id: 37, code: 'B', ativo: 1 }]; const before = structuredClone({ template, lead, variants }); renderCampaignMessage(template, lead); variantForLead(variants, 7); assert.deepEqual({ template, lead, variants }, before); }); test('validacao rejeita URL, CNPJ, preco e requisitos obrigatorios ausentes', () => { const safe = `Ola, tudo bem? Sou Gustavo, do Grupo GTI. ${refusal}`; const unsafe = [ safe.replace('Grupo GTI.', 'Grupo GTI. https://gti.example'), safe.replace('Grupo GTI.', 'Grupo GTI. gti.com.br'), safe.replace('Grupo GTI.', 'Grupo GTI. 12.345.678/0001-90'), safe.replace('Grupo GTI.', 'Grupo GTI. 12345678000190'), safe.replace('Grupo GTI.', 'Grupo GTI. R$ 99'), safe.replace('Gustavo', 'Consultor'), safe.replace('Grupo GTI', 'nossa empresa'), safe.replace(` ${refusal}`, '') ]; for (const template of unsafe) { assert.throws(() => renderCampaignMessage(template, { empresa: '' }), /mensagem inicial insegura/i); } });