Files
SLACK_COORD_RELATORIO/test/workflowStructure.test.js
T
Automacao GLPIandClaude Sonnet 5 64ddfb9b7b V2: guarda de envio real (SEND_ENABLED/FORCE_SEND), idempotencia por send_key e Slack nativo
- src/v2/sendGuard.js (novo): canSend() reduz SHADOW_MODE/DEV_MODE/
  SEND_ENABLED/FORCE_SEND a uma formula unica testada exaustivamente
  (secao 15 do ciclo final: matriz completa de guarda de seguranca).
  computeMessageHash/buildSendKey/isDuplicate implementam a chave
  logica de idempotencia da secao 13 (report_type+periodo+destino+hash).
- src/v2/config.js: SEND_ENABLED, FORCE_SEND, SLACK_DESTINATION.
- src/v2/pipeline.js: buildReport agora calcula envio.pode_enviar/
  motivo_guarda/duplicata_bloqueada/send_key a partir de triggerType
  (.mode) e sendLogRows (Data Table de log de envios).
- tools/build_v2_workflow*.js: novo Data Table 'Buscar Log de Envios',
  troca do HTTP Slack com webhook placeholder por node Slack nativo
  (credencial real lNqwBBrtLfkRqw2X, reaproveitada do projeto irmao
  Coordenador GLPI), branches de sucesso/falha gravando o log de envio
  so apos confirmacao real do Slack.
- test/sendGuard.test.js, test/workflowStructure.test.js (novos):
  cobrem a matriz de guarda de seguranca e travam regressao do bug de
  timeout (executeOnce=true nos 8 nodes de leitura em lote) e do bug
  de sendGuard.js nao concatenado no Code node gerado.
- Corrigido bug real encontrado nesta sessao: sendGuard.js nao estava
  na lista de modulos concatenados pelo gerador, deixando canSend/
  computeMessageHash/buildSendKey undefined em runtime.
- Deployado e validado ao vivo no DEV (workflow 3x5kb58TeBCvirm6):
  n8n_validate_workflow -> valid:true, 0 erros, executeOnce preservado
  nos 9 nodes de leitura em lote.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 09:22:42 -03:00

99 lines
4.3 KiB
JavaScript

'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`);
}
});