test: harden updater 3.4 parallel workflow regressions

This commit is contained in:
Masterluke77
2026-07-10 14:42:04 +02:00
parent a2af2b3f3e
commit 3818cee11f
+51 -2
View File
@@ -30,18 +30,35 @@ for (const file of [
'docs/PRODUCTION_UPDATER_3_4.md',
]) assert(existsSync(join(root, file)), `Pflichtdatei fehlt: ${file}`);
const lifecycle = read('tools/updater-lifecycle-3.4.mjs');
for (const marker of ['main-and-production-at-target', 'main-at-target-production-behind', 'matching-open-pr', 'matching-pr-already-merged']) {
assert(lifecycle.includes(marker), `Lifecycle-Entscheidung fehlt: ${marker}`);
}
if (existsSync(join(root, 'scripts/updater/Vendoo.Updater.Worker34.ps1'))) {
const worker = read('scripts/updater/Vendoo.Updater.Worker34.ps1');
for (const marker of [
"'3.4.0'", 'production-update-v4', 'payloadHash', 'baseCommit', 'sourceCommit',
'Get-ReleaseObservation', 'Wait-RequiredGitHubChecks', 'headRefOid', 'workflow_runs',
'force-with-lease', 'main-and-production-at-target', 'completedAt', 'finalPrHeadSha',
'force-with-lease', 'completedAt', 'finalPrHeadSha',
'check-release-payload-3.4.mjs', '--match-head-commit',
]) assert(worker.includes(marker), `Worker-3.4-Vertrag fehlt: ${marker}`);
assert(!worker.includes("@('pr','checks'"), 'Updater 3.4 darf gh pr checks --watch nicht mehr verwenden.');
assert(!worker.includes("@('push','--force'"), 'Updater 3.4 darf keinen unbedingten Force-Push verwenden.');
}
if (existsSync(join(root, 'scripts/updater/Vendoo.Updater.UI.ps1'))) {
const ui = read('scripts/updater/Vendoo.Updater.UI.ps1');
assert(ui.includes("$UpdaterVersion = '3.4.0'"), 'Updater-UI meldet nicht Version 3.4.0.');
assert(ui.includes("Vendoo.Updater.Worker34.ps1"), 'Updater-UI startet nicht den Worker 3.4.');
}
if (existsSync(join(root, 'scripts/updater/Vendoo.Updater.Host.ps1'))) {
assert(read('scripts/updater/Vendoo.Updater.Host.ps1').includes("$UpdaterVersion = '3.4.0'"), 'Updater-Host meldet nicht Version 3.4.0.');
}
const manifestSync = read('.github/workflows/sync-release-manifest.yml');
assert(manifestSync.includes("'release/*-production-update-v*'"), 'Manifest-Sync ist nicht auf versionsneutrale Releasebranches generalisiert.');
assert(!manifestSync.includes('release/1.43.0-production-update-v3'), 'Manifest-Sync enthält weiterhin den alten festen 3.3-Branch.');
const packageJson = JSON.parse(read('package.json'));
for (const name of ['verify:one-click-update', 'verify:updater-ui', 'verify:updater-hotfix']) {
assert(packageJson.scripts?.[name] === 'node tools/verify-production-updater-3.4.mjs', `${name} verweist nicht auf Updater 3.4.`);
@@ -70,11 +87,43 @@ for (const status of ['QUEUED', 'PENDING', 'IN_PROGRESS']) {
assert(['pending', 'waiting-missing'].includes(result.state), `${status} wurde nicht als laufend behandelt.`);
}
result = classifyChecks({ checks: [...successChecks, { ...successChecks[0], completedAt: '2026-07-10T11:00:00Z', conclusion: 'CANCELLED' }], workflowRuns: successRuns, elapsedSeconds: 120 });
assert(result.state === 'success', 'Paralleler älterer abgebrochener Lauf darf neueren Erfolg nicht überstimmen.');
assert(result.state === 'success', 'Paralleler älterer abgebrochener Check darf neueren Erfolg nicht überstimmen.');
result = classifyChecks({ checks: successChecks.map((x, i) => i ? x : ({ ...x, conclusion: 'FAILURE' })), workflowRuns: successRuns, elapsedSeconds: 120 });
assert(result.state === 'failed', 'Fehlgeschlagener Pflichtcheck wurde nicht terminal erkannt.');
assert(formatStatusMatrix(result.rows).includes(REQUIRED_CHECKS[0]), 'Timeout-/Fehlermatrix enthält Checknamen nicht.');
const runBase = successRuns.filter(run => run.name !== REQUIRED_WORKFLOWS[0]);
result = classifyChecks({
checks: successChecks,
workflowRuns: [
...runBase,
{ name: REQUIRED_WORKFLOWS[0], status: 'completed', conclusion: 'success', updated_at: '2026-07-10T11:00:00Z' },
{ name: REQUIRED_WORKFLOWS[0], status: 'completed', conclusion: 'failure', updated_at: '2026-07-10T12:00:00Z' },
],
elapsedSeconds: 120,
});
assert(result.state === 'failed', 'Ein älterer erfolgreicher Workflow-Run darf einen neueren Fehler nicht verdecken.');
result = classifyChecks({
checks: successChecks,
workflowRuns: [
...runBase,
{ name: REQUIRED_WORKFLOWS[0], status: 'completed', conclusion: 'failure', updated_at: '2026-07-10T11:00:00Z' },
{ name: REQUIRED_WORKFLOWS[0], status: 'in_progress', conclusion: null, updated_at: '2026-07-10T12:00:00Z' },
],
elapsedSeconds: 120,
});
assert(result.state === 'pending', 'Ein neuer laufender Workflow-Run muss einen älteren Fehler ablösen.');
result = classifyChecks({
checks: successChecks,
workflowRuns: [
...runBase,
{ name: REQUIRED_WORKFLOWS[0], status: 'completed', conclusion: 'cancelled', updated_at: '2026-07-10T11:00:00Z' },
{ name: REQUIRED_WORKFLOWS[0], status: 'completed', conclusion: 'success', updated_at: '2026-07-10T12:00:00Z' },
],
elapsedSeconds: 120,
});
assert(result.state === 'success', 'Ein neuer erfolgreicher Workflow-Run muss einen älteren Abbruch ablösen.');
let decision = decideReleasePath({ targetVersion: '1.43.0', mainVersion: '1.43.0', productionReady: true, productionVersion: '1.43.0' });
assert(decision.action === 'complete', 'Bereits deployte Zielversion muss sofort erfolgreich enden.');
decision = decideReleasePath({ targetVersion: '1.43.0', mainVersion: '1.43.0', productionReady: true, productionVersion: '1.42.0' });