feat: add admin-web Docker build & push workflow with semantic-release
This commit is contained in:
228
scripts/prepare-release.test.cjs
Normal file
228
scripts/prepare-release.test.cjs
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* 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 <semver>` 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();
|
||||
Reference in New Issue
Block a user