185 lines
6.4 KiB
Bash
Executable file
185 lines
6.4 KiB
Bash
Executable file
#!/bin/bash
|
|
# test-playground-migrations.sh — Playground v3 dataVersion v1->v2 idempotency
|
|
#
|
|
# Verifiserer:
|
|
# 1. fixture (playground/test-fixtures/state-v1-snapshot.json) eksisterer + er v1
|
|
# 2. V2_FOUNDATION-blokken kan ekstraheres fra HTML-fila
|
|
# 3. migrateDataVersion(fixture) gir resultat A
|
|
# 4. migrateDataVersion(A) gir resultat B
|
|
# 5. JSON.stringify(A) === JSON.stringify(B) -> idempotency
|
|
# 6. A.dataVersion === 2 -> migrasjonen ble utført
|
|
#
|
|
# Bash 3.2-kompatibel. Bruker node til JS-eval; ingen npm-deps.
|
|
# Designet for å integreres i tests/run-e2e.sh --playground.
|
|
|
|
set -euo pipefail
|
|
|
|
PLUGIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
HTML_FILE="$PLUGIN_ROOT/playground/ms-ai-architect-playground.html"
|
|
FIXTURE_FILE="$PLUGIN_ROOT/playground/test-fixtures/state-v1-snapshot.json"
|
|
|
|
# shellcheck disable=SC1091
|
|
source "$PLUGIN_ROOT/tests/lib/e2e-helpers.sh"
|
|
|
|
init_suite "Playground v3 — dataVersion v1->v2 migration idempotency"
|
|
|
|
# ---- 1. Filer eksisterer ----
|
|
if [ ! -f "$HTML_FILE" ]; then
|
|
fail "HTML-fila finnes ikke: $HTML_FILE"
|
|
print_summary; exit 1
|
|
fi
|
|
pass "HTML-fil finnes: $(basename "$HTML_FILE")"
|
|
|
|
if [ ! -f "$FIXTURE_FILE" ]; then
|
|
fail "Fixture mangler: $FIXTURE_FILE"
|
|
print_summary; exit 1
|
|
fi
|
|
pass "Fixture finnes: $(basename "$FIXTURE_FILE")"
|
|
|
|
# ---- 2. Fixture er v1 (mangler dataVersion eller dataVersion < 2) ----
|
|
if node -e "
|
|
const f = JSON.parse(require('fs').readFileSync(process.argv[1], 'utf8'));
|
|
if (f.dataVersion === 2) { console.error('fixture er allerede v2'); process.exit(1); }
|
|
if (typeof f.schemaVersion !== 'number') { console.error('fixture mangler schemaVersion'); process.exit(1); }
|
|
if (!Array.isArray(f.projects) || f.projects.length === 0) { console.error('fixture har ingen projects'); process.exit(1); }
|
|
" "$FIXTURE_FILE" 2>/dev/null; then
|
|
pass "Fixture er v1-state (dataVersion ikke satt)"
|
|
else
|
|
fail "Fixture er ikke gyldig v1-state"
|
|
print_summary; exit 1
|
|
fi
|
|
|
|
# ---- 3. V2_FOUNDATION-markører eksisterer i HTML ----
|
|
if grep -q "V2_FOUNDATION_BEGIN" "$HTML_FILE" && grep -q "V2_FOUNDATION_END" "$HTML_FILE"; then
|
|
pass "V2_FOUNDATION_BEGIN/END markører finnes"
|
|
else
|
|
fail "Mangler V2_FOUNDATION-markører i HTML"
|
|
print_summary; exit 1
|
|
fi
|
|
|
|
# ---- 4. migrateDataVersion-funksjonen er definert ----
|
|
if grep -q "function migrateDataVersion" "$HTML_FILE"; then
|
|
pass "migrateDataVersion-funksjonen finnes"
|
|
else
|
|
fail "migrateDataVersion-funksjonen mangler"
|
|
print_summary; exit 1
|
|
fi
|
|
|
|
# ---- 5. Eval idempotency-test i node ----
|
|
# Strategi: ekstraher V2_FOUNDATION-blokken via sed, wrap med stubs (window,
|
|
# CATALOG-mock som returnerer archetype basert på fixture command-IDs), eval,
|
|
# kjør migrasjon to ganger på fixture-deep-copy, sammenlign JSON.stringify.
|
|
|
|
IDEMPOTENCY_RESULT=$(node -e '
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const htmlPath = process.argv[1];
|
|
const fixturePath = process.argv[2];
|
|
|
|
const html = fs.readFileSync(htmlPath, "utf8");
|
|
const beginMarker = "// === V2_FOUNDATION_BEGIN ===";
|
|
const endMarker = "// === V2_FOUNDATION_END ===";
|
|
const beginIdx = html.indexOf(beginMarker);
|
|
const endIdx = html.indexOf(endMarker);
|
|
if (beginIdx < 0 || endIdx < 0) {
|
|
console.error("MARKER_MISSING");
|
|
process.exit(2);
|
|
}
|
|
const block = html.substring(beginIdx, endIdx + endMarker.length);
|
|
|
|
// Stub-ene som blokken trenger
|
|
const stubs = `
|
|
const window = {};
|
|
function escapeHtml(s) { return String(s == null ? "" : s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """); }
|
|
function escapeAttr(s) { return escapeHtml(s); }
|
|
// Mock CATALOG: kun de command-id-ene fixture-en bruker.
|
|
const CATALOG = { commands: [
|
|
{ id: "classify", report_archetype: "aiact" },
|
|
{ id: "ros", report_archetype: "matrix-risk" },
|
|
{ id: "cost", report_archetype: "cost-distribution" },
|
|
{ id: "summary", report_archetype: "verdict" }
|
|
]};
|
|
`;
|
|
|
|
const wrapped = stubs + block + "\nreturn { migrateDataVersion, defaultArchetypeFor };";
|
|
let api;
|
|
try {
|
|
api = (new Function(wrapped))();
|
|
} catch (e) {
|
|
console.error("EVAL_FAILED:", e.message);
|
|
process.exit(3);
|
|
}
|
|
|
|
const fixture = JSON.parse(fs.readFileSync(fixturePath, "utf8"));
|
|
|
|
// Deep clones så vi sammenligner uavhengige objekter
|
|
const stateA = JSON.parse(JSON.stringify(fixture));
|
|
api.migrateDataVersion(stateA, api.defaultArchetypeFor);
|
|
|
|
const stateB = JSON.parse(JSON.stringify(stateA));
|
|
api.migrateDataVersion(stateB, api.defaultArchetypeFor);
|
|
|
|
const a = JSON.stringify(stateA);
|
|
const b = JSON.stringify(stateB);
|
|
|
|
if (stateA.dataVersion !== 2) {
|
|
console.error("DATA_VERSION_NOT_BUMPED");
|
|
process.exit(4);
|
|
}
|
|
|
|
// Sjekk at minst én rapport fikk verdict + keyStats utledet
|
|
let verdictsAdded = 0, statsAdded = 0;
|
|
for (const p of (stateA.projects || [])) {
|
|
for (const id of Object.keys(p.reports || {})) {
|
|
const r = p.reports[id];
|
|
if (r && r.parsed) {
|
|
if (r.parsed.verdict != null) verdictsAdded++;
|
|
if (Array.isArray(r.parsed.keyStats)) statsAdded++;
|
|
}
|
|
}
|
|
}
|
|
if (verdictsAdded === 0) { console.error("NO_VERDICTS_ADDED"); process.exit(5); }
|
|
if (statsAdded === 0) { console.error("NO_KEYSTATS_ADDED"); process.exit(6); }
|
|
|
|
if (a === b) {
|
|
console.log("IDEMPOTENT verdicts=" + verdictsAdded + " stats=" + statsAdded);
|
|
} else {
|
|
console.error("NOT_IDEMPOTENT");
|
|
process.exit(7);
|
|
}
|
|
' "$HTML_FILE" "$FIXTURE_FILE" 2>&1) || RC=$?
|
|
|
|
if [ "${RC:-0}" -eq 0 ] && echo "$IDEMPOTENCY_RESULT" | grep -q "^IDEMPOTENT"; then
|
|
pass "migrateDataVersion er idempotent ($IDEMPOTENCY_RESULT)"
|
|
else
|
|
fail "Idempotency-test feilet: $IDEMPOTENCY_RESULT"
|
|
fi
|
|
|
|
# ---- 6. dataVersion bumpes til 2 ved første kjøring ----
|
|
DV_RESULT=$(node -e '
|
|
const fs = require("fs");
|
|
const html = fs.readFileSync(process.argv[1], "utf8");
|
|
const begin = html.indexOf("// === V2_FOUNDATION_BEGIN ===");
|
|
const end = html.indexOf("// === V2_FOUNDATION_END ===");
|
|
const block = html.substring(begin, end + 32);
|
|
const stubs = `
|
|
const window = {};
|
|
function escapeHtml(s) { return String(s == null ? "" : s); }
|
|
function escapeAttr(s) { return escapeHtml(s); }
|
|
const CATALOG = { commands: [
|
|
{ id: "classify", report_archetype: "aiact" }
|
|
]};
|
|
`;
|
|
const api = (new Function(stubs + block + "\nreturn { migrateDataVersion, defaultArchetypeFor };"))();
|
|
const state = { schemaVersion: 1, projects: [] };
|
|
api.migrateDataVersion(state, api.defaultArchetypeFor);
|
|
console.log(state.dataVersion);
|
|
' "$HTML_FILE" 2>&1) || true
|
|
|
|
if [ "$DV_RESULT" = "2" ]; then
|
|
pass "dataVersion bumpes til 2"
|
|
else
|
|
fail "dataVersion ble ikke bumpet til 2 (got '$DV_RESULT')"
|
|
fi
|
|
|
|
print_summary
|