From 064017834b3dafc6b0a83414b7390f5e1a09264b Mon Sep 17 00:00:00 2001 From: Thomas Flament Date: Tue, 30 Jun 2026 15:11:32 +0200 Subject: [PATCH] Add cucumber-level time budget to stop CTST gracefully The GitHub step timeout hard-kills the process, so Cucumber never writes its reports (empty report.xml). Add an in-process deadline instead: the runner script exports a deadline (default 180m) and a marker path; an early Before hook skips any scenario starting past the deadline (so the expensive @Quotas/count-items hooks are short-circuited) and drops the marker. Cucumber then finishes normally, writing report.xml/html/ndjson and running teardown; the script fails the run if the marker is present. The 190m step timeout (ZENKO-5306) remains as a hard backstop. Issue: ZENKO-5309 --- .github/scripts/end2end/run-e2e-ctst.sh | 22 +++++++++++++++++++++- tests/functional/ctst/common/hooks.ts | 18 ++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/.github/scripts/end2end/run-e2e-ctst.sh b/.github/scripts/end2end/run-e2e-ctst.sh index 0744fdd555..55a20fb0ac 100755 --- a/.github/scripts/end2end/run-e2e-ctst.sh +++ b/.github/scripts/end2end/run-e2e-ctst.sh @@ -23,6 +23,17 @@ cd "$FUNCTIONAL_TESTS_DIR" mkdir -p ctst/reports export SDK=true # Cli-testing also has a cli mode, not really used in practice + +# Gracefully stop before the GitHub step timeout hard-kills the process: +# scenarios that start after this deadline are skipped (see the time-budget +# Before hook), so Cucumber finishes normally and writes its reports. The hook +# drops a marker file; we fail the run if it is present. +CTST_MAX_RUNTIME_MIN=${CTST_MAX_RUNTIME_MIN:-180} +export CTST_DEADLINE_EPOCH_MS=$(( $(date +%s%3N) + CTST_MAX_RUNTIME_MIN * 60 * 1000 )) +export CTST_TIMEOUT_MARKER="/tmp/ctst-timeout.marker" +rm -f "$CTST_TIMEOUT_MARKER" + +rc=0 yarn cucumber-js \ --config ctst/cucumber.config.cjs \ --tags "${TAGS}" \ @@ -33,4 +44,13 @@ yarn cucumber-js \ --format pretty \ --format html:ctst/reports/report.html \ --format junit:ctst/reports/report.xml \ - --format message:ctst/reports/report.ndjson + --format message:ctst/reports/report.ndjson || rc=$? + +if [ -f "$CTST_TIMEOUT_MARKER" ]; then + echo "::error::CTST exceeded its ${CTST_MAX_RUNTIME_MIN}-minute time budget; remaining scenarios were skipped." + if [ "$rc" -eq 0 ]; then + rc=1 + fi +fi + +exit "$rc" diff --git a/tests/functional/ctst/common/hooks.ts b/tests/functional/ctst/common/hooks.ts index 2359019b5c..e73776e37c 100644 --- a/tests/functional/ctst/common/hooks.ts +++ b/tests/functional/ctst/common/hooks.ts @@ -1,3 +1,4 @@ +import fs from 'fs'; import { Before, After, @@ -53,6 +54,23 @@ AfterAll(async function () { await stopDLQConsumer(); }); +// Time budget: scenarios that start after the deadline are skipped so Cucumber +// finishes normally (writing its reports) before the GitHub step timeout would +// hard-kill the process. The marker file lets the runner script fail the run. +Before(() => { + const deadline = Number(process.env.CTST_DEADLINE_EPOCH_MS); + if (deadline && Date.now() > deadline) { + const marker = process.env.CTST_TIMEOUT_MARKER; + if (marker) { + try { + fs.writeFileSync(marker, 'timeout'); + } catch { /* best-effort: marker is advisory */ } + } + return 'skipped'; + } + return undefined; +}); + Before(async function (this: Zenko, scenario: ITestCaseHookParameter) { this.resetSaved(); Identity.resetIdentity();