/* * prepare-release.test.cjs — Test standalone (zero deps) pour scripts/prepare-release.cjs * * Contrat vérifié (issu du planner / critères d'acceptation) : * - `node scripts/prepare-release.cjs ` met à jour `version.json` (racine) * ET `admin-web/package.json` (champ `version`) avec la valeur passée. * - Sans argument -> exit code != 0. * - Version non-semver -> exit code != 0. * - semver simple (1.2.3) ou avec prerelease (1.2.3-beta.1) -> succès. * - Les autres clés de `admin-web/package.json` (name, dependencies, scripts...) * sont préservées (pas écrasées). * * Ce test s'exécute AVANT l'implémentation (le script n'existe pas encore). * Dans ce cas, chaque test échoue proprement avec "[FAIL] ... script not implemented yet", * ce qui confirme l'état rouge attendu (TDD spec-first). * * Usage : node scripts/prepare-release.test.cjs * Exit : 0 si tous les tests passent, 1 sinon. */ "use strict"; const assert = require("node:assert"); const fs = require("node:fs"); const path = require("node:path"); const { spawnSync } = require("node:child_process"); const ROOT = path.resolve(__dirname, ".."); const SCRIPT = path.join(ROOT, "scripts", "prepare-release.cjs"); const VERSION_JSON = path.join(ROOT, "version.json"); const ADMIN_PKG = path.join(ROOT, "admin-web", "package.json"); let passed = 0; let failed = 0; function ok(name) { console.log(`[PASS] ${name}`); passed++; } function ko(name, detail) { console.log(`[FAIL] ${name}${detail ? " — " + detail : ""}`); failed++; } /** * Exécute prepare-release.cjs via node et renvoie { status, stdout, stderr }. * Si le script n'existe pas, on simule un échec avec status null. */ function runScript(args) { if (!fs.existsSync(SCRIPT)) { return { status: null, stdout: "", stderr: "scripts/prepare-release.cjs not implemented yet", missing: true, }; } const res = spawnSync(process.execPath, [SCRIPT, ...args], { cwd: ROOT, encoding: "utf8", timeout: 15000, }); return { status: res.status, stdout: res.stdout ?? "", stderr: res.stderr ?? "", missing: false, }; } /* ------------------------------------------------------------------ * * Backup / restore : on ne veut PAS polluer le working tree. * On backup les fichiers AVANT les tests d'écriture et on restore à la fin, * même si une assertion throw. * ------------------------------------------------------------------ */ const backup = {}; function backupFile(file) { if (fs.existsSync(file)) { backup[file] = { existed: true, content: fs.readFileSync(file) }; } else { backup[file] = { existed: false }; } } function restoreFile(file) { const b = backup[file]; if (!b) return; if (b.existed) { fs.writeFileSync(file, b.content); } else if (fs.existsSync(file)) { fs.unlinkSync(file); } } function readJSON(file) { return JSON.parse(fs.readFileSync(file, "utf8")); } /* ------------------------------------------------------------------ * * TEST CASES * ------------------------------------------------------------------ */ // (a) succès semver simple function test_simple_semver_updates_both_files() { const name = "simple semver (9.9.9) updates version.json + admin-web/package.json"; const r = runScript(["9.9.9"]); if (r.missing) return ko(name, "script not implemented yet"); try { assert.strictEqual(r.status, 0, `expected exit 0, got ${r.status} (stderr=${r.stderr.trim()})`); const v = readJSON(VERSION_JSON); assert.strictEqual(v.version, "9.9.9", `version.json.version=${v.version}`); const p = readJSON(ADMIN_PKG); assert.strictEqual(p.version, "9.9.9", `admin-web/package.json.version=${p.version}`); ok(name); } catch (e) { ko(name, e.message); } } // (b) succès prerelease function test_prerelease_semver_accepted() { const name = "prerelease semver (1.2.3-beta.1) accepted"; const r = runScript(["1.2.3-beta.1"]); if (r.missing) return ko(name, "script not implemented yet"); try { assert.strictEqual(r.status, 0, `expected exit 0, got ${r.status} (stderr=${r.stderr.trim()})`); const v = readJSON(VERSION_JSON); assert.strictEqual(v.version, "1.2.3-beta.1", `version.json.version=${v.version}`); const p = readJSON(ADMIN_PKG); assert.strictEqual(p.version, "1.2.3-beta.1", `admin-web/package.json.version=${p.version}`); ok(name); } catch (e) { ko(name, e.message); } } // (c) échec sans arg function test_no_arg_fails() { const name = "no argument -> exit != 0"; const r = runScript([]); if (r.missing) return ko(name, "script not implemented yet"); try { assert.notStrictEqual(r.status, 0, `expected non-zero exit, got ${r.status}`); ok(name); } catch (e) { ko(name, e.message); } } // (d) échec version invalide function test_invalid_version_fails() { const name = "invalid version ('not-a-version') -> exit != 0"; const r = runScript(["not-a-version"]); if (r.missing) return ko(name, "script not implemented yet"); try { assert.notStrictEqual(r.status, 0, `expected non-zero exit, got ${r.status}`); // fichiers ne doivent pas avoir été modifiés avec une version invalide if (fs.existsSync(VERSION_JSON)) { const v = readJSON(VERSION_JSON); assert.notStrictEqual(v.version, "not-a-version", "version.json should not contain invalid version"); } ok(name); } catch (e) { ko(name, e.message); } } // (e) préservation des autres clés du package.json function test_preserves_other_keys() { const name = "other keys of admin-web/package.json preserved (name, scripts, dependencies)"; const r = runScript(["7.7.7"]); if (r.missing) return ko(name, "script not implemented yet"); try { assert.strictEqual(r.status, 0, `expected exit 0, got ${r.status}`); const p = readJSON(ADMIN_PKG); // Le package.json actuel contient name, scripts, dependencies, devDependencies. // On vérifie que ces clés sont toujours là et non vides. assert.ok(typeof p.name === "string" && p.name.length > 0, `name missing/empty: ${p.name}`); assert.ok(p.scripts && typeof p.scripts === "object", "scripts object missing"); assert.ok(Object.keys(p.scripts).length > 0, "scripts object empty"); assert.ok(p.dependencies && typeof p.dependencies === "object", "dependencies object missing"); assert.ok(Object.keys(p.dependencies).length > 0, "dependencies object empty"); // La version, elle, doit avoir été bumpée assert.strictEqual(p.version, "7.7.7", `version should be 7.7.7, got ${p.version}`); ok(name); } catch (e) { ko(name, e.message); } } /* ------------------------------------------------------------------ * * RUN * ------------------------------------------------------------------ */ function main() { console.log("# prepare-release.cjs — test suite (standalone, zero deps)"); console.log(`# script: ${path.relative(ROOT, SCRIPT)}`); console.log(`# exists: ${fs.existsSync(SCRIPT)}`); console.log(""); // Backup AVANT les tests d'écriture backupFile(VERSION_JSON); backupFile(ADMIN_PKG); let crashed = false; try { test_simple_semver_updates_both_files(); test_prerelease_semver_accepted(); test_no_arg_fails(); test_invalid_version_fails(); test_preserves_other_keys(); } catch (e) { crashed = true; console.error("[ERROR] unexpected exception in test runner:", e); } finally { // Restore TOUJOURS restoreFile(VERSION_JSON); restoreFile(ADMIN_PKG); } console.log(""); console.log(`# Summary: ${passed} passed, ${failed} failed${crashed ? " (+runner crash)" : ""}`); process.exit(failed === 0 && !crashed ? 0 : 1); } main();