R3: suite de tests golden — 16 scénarios, bloquante avant push
- tests/golden/run.mjs : chaîne complète des 5 nodes (code réel) en sandbox vm, horloge simulée par scénario, snapshot HA réel comme fixture. - 16 scénarios attendus figés après revue : nuit HC (P7), solaire, abondance P6, provocation P3, gardes SOC, force_grid/force_charge, stop, overload. - n8n-push.mjs exécute la suite avant tout PUT du workflow EVCS : golden rouge = push refusé (--skip-golden en échappatoire consciente). - Validé par mutation : P7 cassé volontairement -> 2 échecs détectés + push bloqué. - tools/fetch-snapshot-fixture.mjs : recapture d'un snapshot production.
This commit is contained in:
50
tools/fetch-snapshot-fixture.mjs
Normal file
50
tools/fetch-snapshot-fixture.mjs
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env node
|
||||
// Capture un snapshot HA RÉEL depuis une exécution n8n récente du workflow EVCS
|
||||
// → tests/golden/fixture-snapshot.json (base des scénarios golden).
|
||||
// Usage: node tools/fetch-snapshot-fixture.mjs [executionId]
|
||||
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
const WORKFLOW_ID = 'ATswvGfG4KJUls0K';
|
||||
const SNAPSHOT_NODE = 'HA Snapshot EVCS Surplus Controller V2.2';
|
||||
const OUT = 'tests/golden/fixture-snapshot.json';
|
||||
|
||||
function loadApiConfig() {
|
||||
const cfgPath = path.join(os.homedir(), '.config/opencode/opencode.json');
|
||||
const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
|
||||
const env = cfg?.mcp?.['n8n-mcp']?.environment || {};
|
||||
return {
|
||||
url: (env.N8N_API_URL || '').replace(/\/api\/v1\/?$/, ''),
|
||||
key: env.N8N_API_KEY
|
||||
};
|
||||
}
|
||||
const { url, key } = loadApiConfig();
|
||||
const headers = { 'X-N8N-API-KEY': key };
|
||||
|
||||
let execId = process.argv[2];
|
||||
if (!execId) {
|
||||
const res = await fetch(`${url}/api/v1/executions?workflowId=${WORKFLOW_ID}&status=success&limit=1`, { headers });
|
||||
const list = await res.json();
|
||||
execId = list.data?.[0]?.id;
|
||||
if (!execId) { console.error('Aucune exécution trouvée'); process.exit(1); }
|
||||
}
|
||||
|
||||
const res = await fetch(`${url}/api/v1/executions/${execId}?includeData=true`, { headers });
|
||||
if (!res.ok) { console.error(`GET exécution ${execId}: ${res.status}`); process.exit(1); }
|
||||
const exec = await res.json();
|
||||
|
||||
const runData = exec.data?.resultData?.runData;
|
||||
if (!runData?.[SNAPSHOT_NODE]) {
|
||||
console.error(`Node "${SNAPSHOT_NODE}" absent des données. Nodes dispo: ${Object.keys(runData || {}).join(', ')}`);
|
||||
process.exit(1);
|
||||
}
|
||||
const items = runData[SNAPSHOT_NODE][0]?.data?.main?.[0];
|
||||
if (!items?.length) { console.error('Pas de données de sortie sur le node snapshot'); process.exit(1); }
|
||||
|
||||
const snapshot = items[0].json;
|
||||
fs.mkdirSync(path.dirname(OUT), { recursive: true });
|
||||
fs.writeFileSync(OUT, JSON.stringify(snapshot, null, 2) + '\n');
|
||||
console.log(`Fixture écrite: ${OUT} (exécution ${execId}, ${Object.keys(snapshot).length} champs)`);
|
||||
console.log(`Champs: ${Object.keys(snapshot).sort().join(', ')}`);
|
||||
@@ -86,6 +86,25 @@ if (syntaxErrors) {
|
||||
}
|
||||
console.log('Syntaxe OK sur tous les nodes jsCode.');
|
||||
|
||||
// 3bis. Suite golden (R3) : obligatoire pour le workflow EVCS, sauf --skip-golden
|
||||
const isEvcsWorkflow = path.basename(INPUT) === 'evcs-surplus-controller-v2.6.json';
|
||||
const skipGolden = process.argv.includes('--skip-golden');
|
||||
if (isEvcsWorkflow && fs.existsSync('tests/golden/run.mjs')) {
|
||||
if (skipGolden) {
|
||||
console.log('⚠️ Suite golden SAUTÉE (--skip-golden) — à n\'utiliser qu\'en connaissance de cause.');
|
||||
} else {
|
||||
console.log('Exécution de la suite golden (tests/golden/run.mjs)...');
|
||||
const { spawnSync } = await import('child_process');
|
||||
const g = spawnSync('node', ['tests/golden/run.mjs'], { stdio: 'inherit' });
|
||||
if (g.status !== 0) {
|
||||
console.error('ABANDON: la suite golden échoue. Rien n\'a été poussé.');
|
||||
console.error('(Évolution volontaire du comportement ? Mettre à jour les attendus');
|
||||
console.error(' dans tests/golden/scenarios.mjs dans le même commit, puis re-pousser.)');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. PUT
|
||||
const putRes = await fetch(`${url}/api/v1/workflows/${WORKFLOW_ID}`, {
|
||||
method: 'PUT',
|
||||
|
||||
Reference in New Issue
Block a user