'use strict'; /** * Teste de regressao estrutural (secao 16 do ciclo final). * * Contexto: a causa raiz do timeout que travou "MySQL - Baseline Diario * Cliente" foi a ausencia de `executeOnce: true` nesse (e em outros) node * MySQL/Data Table — sem ele, o n8n reexecuta o node UMA VEZ POR ITEM de * entrada (76.440 execucoes observadas), em vez de uma unica vez. Este * teste garante que essa regressao especifica nao pode voltar a acontecer * silenciosamente: ele le o workflow gerado (o mesmo arquivo que e * reimportado no n8n) e falha se qualquer um dos nodes criticos perder a * flag. * * Roda contra o ARQUIVO GERADO (nao contra tools/build_v2_workflow_assemble.js * diretamente) porque e o arquivo gerado que de fato e importado no n8n — * e o que importa e o resultado final, nao a implementacao do gerador. * Rode `node tools/build_v2_workflow.js` antes de `npm test` sempre que * alterar o assemble script (o proprio README/uso do script ja pede isso). */ const { test } = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const WORKFLOW_PATH = path.join(__dirname, '..', 'Resumo Operacional GLPI v2 - IA.n8n.json'); // Os nodes que buscam dados em lote (uma query cobrindo todos os itens) e // que, se rodarem "por item" em vez de uma unica vez, causam o mesmo tipo // de explosao combinatoria que gerou o timeout original. const NODES_QUE_DEVEM_EXECUTAR_UMA_VEZ = [ 'MySQL - Indicadores do Periodo', 'MySQL - Ranking Categorias', 'MySQL - Ranking Clientes', 'MySQL - Contexto de Chamados', 'MySQL - Baseline Diario Categoria', 'MySQL - Baseline Diario Cliente', 'Data Table - Buscar Memoria de Insights', 'Data Table - Buscar Historico Anterior', ]; function loadWorkflow() { if (!fs.existsSync(WORKFLOW_PATH)) { throw new Error( `Workflow gerado nao encontrado em ${WORKFLOW_PATH}. Rode "node tools/build_v2_workflow.js" antes de rodar os testes.` ); } return JSON.parse(fs.readFileSync(WORKFLOW_PATH, 'utf8')); } test('workflow gerado existe e tem a lista de nodes esperada', () => { const wf = loadWorkflow(); assert.ok(Array.isArray(wf.nodes)); assert.ok(wf.nodes.length > 0); }); for (const nodeName of NODES_QUE_DEVEM_EXECUTAR_UMA_VEZ) { test(`node "${nodeName}" mantem executeOnce=true (regressao do timeout de 76.440 execucoes)`, () => { const wf = loadWorkflow(); const node = wf.nodes.find((n) => n.name === nodeName); assert.ok(node, `node "${nodeName}" nao existe mais no workflow gerado — renomeado? atualize este teste.`); assert.equal( node.executeOnce, true, `node "${nodeName}" perdeu executeOnce=true — isso reintroduz o bug do timeout (76.440 execucoes).` ); }); } test('Code - Motor V2 inclui as funcoes de sendGuard.js (regressao: modulo requerido mas nao concatenado)', () => { // Bug real encontrado e corrigido em 2026-08-25: pipeline.js passou a // `require('./sendGuard')`, mas tools/build_v2_workflow.js remove TODA // linha de require ao montar o Code node (para o n8n nao tentar dar // require de um arquivo que nao existe no ambiente remoto) — sem incluir // sendGuard.js na lista de modulos concatenados, canSend/computeMessageHash/ // buildSendKey ficariam undefined em runtime, quebrando o node inteiro. const wf = loadWorkflow(); const node = wf.nodes.find((n) => n.name === 'Code - Motor V2: Relatorio Completo'); assert.ok(node, 'node "Code - Motor V2: Relatorio Completo" nao encontrado'); const codigo = node.parameters && node.parameters.jsCode; assert.ok(codigo, 'node sem parameters.jsCode'); for (const nomeFuncao of ['function canSend', 'function computeMessageHash', 'function buildSendKey']) { assert.ok( codigo.includes(nomeFuncao), `Code - Motor V2 nao contem "${nomeFuncao}" — sendGuard.js nao esta sendo concatenado (ver MOD.sendGuard em tools/build_v2_workflow.js)` ); } }); test('nenhum node MySQL do workflow roda sem executeOnce=true (varredura ampla, protege contra novo node MySQL futuro)', () => { const wf = loadWorkflow(); const mysqlNodes = wf.nodes.filter((n) => n.type === 'n8n-nodes-base.mySql'); assert.ok(mysqlNodes.length > 0, 'esperava pelo menos um node MySQL no workflow'); for (const node of mysqlNodes) { assert.equal(node.executeOnce, true, `node MySQL "${node.name}" sem executeOnce=true`); } });