diff --git a/tests/features/step_definitions/commit-status.steps.js b/tests/features/step_definitions/commit-status.steps.js index 3cbe24a..4df2e3a 100644 --- a/tests/features/step_definitions/commit-status.steps.js +++ b/tests/features/step_definitions/commit-status.steps.js @@ -27,7 +27,8 @@ function bashQuiet(cmd) { } function runReportStatus(args) { - return bash(`export GITEA_API_URL="http://localhost:18080" GITEA_TOKEN="test-token-abc123" GIT_PAGES_URL="https://reports.example.com" GITHUB_REPOSITORY="test-owner/test-repo" GITHUB_SHA="abc123def456789012345678901234567890abcd" GITHUB_RUN_ID="42"; bash "${REPORT_SCRIPT}" ${args}`); + const apiUrl = process.env.GITEA_API_URL || 'http://localhost:18080'; + return bash(`export GITEA_API_URL="${apiUrl}" GITEA_TOKEN="test-token-abc123" GIT_PAGES_URL="https://reports.example.com" GITHUB_REPOSITORY="test-owner/test-repo" GITHUB_SHA="abc123def456789012345678901234567890abcd" GITHUB_RUN_ID="42"; bash "${REPORT_SCRIPT}" ${args}`); } function getMockBody() { @@ -110,7 +111,12 @@ Then('the source commit shows the deployment status alongside the build status', When('a build step tries to report status but the build system is unavailable', function () { bashQuiet(`source "${MOCK_SCRIPT}" && mock_stop`); execSync('sleep 0.3', { stdio: 'ignore' }); - bashQuiet(`source "${MOCK_SCRIPT}" && mock_set_response 500 && mock_start`); + const out = execSync(`bash -o pipefail -c 'source "${MOCK_SCRIPT}" && mock_set_response 500 && mock_start >&2 && echo "$GITEA_API_URL"'`, { + cwd: PROJECT_ROOT, + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }); + process.env.GITEA_API_URL = out.trim(); const r = runReportStatus('success "Should fail"'); this.reportStatusFailed = (r.status !== 0); }); diff --git a/tests/features/step_definitions/common.steps.js b/tests/features/step_definitions/common.steps.js index 6c665a8..6460bd7 100644 --- a/tests/features/step_definitions/common.steps.js +++ b/tests/features/step_definitions/common.steps.js @@ -6,15 +6,16 @@ const PROJECT_ROOT = path.resolve(__dirname, '..', '..', '..'); const MOCK_SCRIPT = path.join(PROJECT_ROOT, 'tests', 'helpers', 'mock-api.sh'); Before({ tags: '@mock' }, function () { - const out = execSync(`bash -c 'source "${MOCK_SCRIPT}" && mock_start && sleep 1 && curl -s -o /dev/null -w "%{http_code}" --max-time 3 http://localhost:18080/api/v1/repos/health/check'`, { + const out = execSync(`bash -o pipefail -c 'source "${MOCK_SCRIPT}" && mock_start >&2 && echo "$GITEA_API_URL"'`, { cwd: PROJECT_ROOT, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], }); - const trimmed = out.trim(); - if (!trimmed.startsWith('2') && !trimmed.startsWith('4')) { - throw new Error(`Mock server failed to start (HTTP ${trimmed})`); + const apiUrl = out.trim(); + if (!apiUrl.startsWith('http')) { + throw new Error(`Mock server failed to start (no API URL: ${apiUrl})`); } + process.env.GITEA_API_URL = apiUrl; }); After({ tags: '@mock' }, function () { diff --git a/tests/features/step_definitions/test-execution.steps.js b/tests/features/step_definitions/test-execution.steps.js index 8657eb7..100af60 100644 --- a/tests/features/step_definitions/test-execution.steps.js +++ b/tests/features/step_definitions/test-execution.steps.js @@ -21,9 +21,15 @@ function bash(cmd) { } } +function getFreePort() { + const out = execSync(`python3 -c "import socket; s=socket.socket(); s.bind(('',0)); print(s.getsockname()[1]); s.close()"`, { encoding: 'utf-8' }); + return parseInt(out.trim(), 10); +} + function setupMock(seqJson) { - execSync('lsof -ti :18080 2>/dev/null | xargs -r kill -9 2>/dev/null || true', { stdio: 'ignore' }); - execSync('sleep 0.4', { stdio: 'ignore' }); + const port = getFreePort(); + process.env.MOCK_PORT = String(port); + process.env.GITEA_API_URL = `http://localhost:${port}`; const seqFile = path.join(os.tmpdir(), `cucumber_seq_${Date.now()}.json`); fs.writeFileSync(seqFile, seqJson); @@ -34,17 +40,16 @@ function setupMock(seqJson) { const configFile = path.join(os.tmpdir(), `cucumber_cfg_${Date.now()}.txt`); fs.writeFileSync(configFile, `SEQUENCE\n${seqJson}\n${idxFile}`); - const proc = spawn('python3', [MOCK_SERVER, '18080', configFile, reqFile], { + const proc = spawn('python3', [MOCK_SERVER, String(port), configFile, reqFile], { cwd: PROJECT_ROOT, detached: true, stdio: 'ignore', }); proc.unref(); - this._mockProc = proc; for (let i = 0; i < 20; i++) { try { - execSync('curl -s --max-time 1 http://localhost:18080/', { stdio: 'ignore' }); + execSync(`nc -z localhost ${port}`, { stdio: 'ignore' }); fs.writeFileSync(idxFile, '0'); return; } catch (_) {} @@ -69,7 +74,8 @@ When('a test workflow is dispatched to a test project', function () { { code: 200, body: { workflow_runs: [{ id: 1, status: 'running' }] } }, { code: 200, body: { id: 1, status: 'completed', conclusion: 'success' } }, ])); - const r = runDispatch('"test-owner/test-repo" "test.yml" "main" \'{"version":"1.2.3"}\' "http://localhost:18080" "test-token-abc123"'); + const url = process.env.GITEA_API_URL; + const r = runDispatch(`"test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "${url}" "test-token-abc123"`); this.dispatchResult = r.status; }); @@ -87,7 +93,8 @@ When('a test workflow is dispatched and the tests fail', function () { { code: 200, body: { workflow_runs: [{ id: 1, status: 'running' }] } }, { code: 200, body: { id: 1, status: 'completed', conclusion: 'failure' } }, ])); - const r = runDispatch('"test-owner/test-repo" "test.yml" "main" \'{"version":"1.2.3"}\' "http://localhost:18080" "test-token-abc123"'); + const url = process.env.GITEA_API_URL; + const r = runDispatch(`"test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "${url}" "test-token-abc123"`); this.dispatchResult = r.status; }); @@ -103,7 +110,8 @@ When('a test workflow is dispatched but does not finish within the allowed time' { code: 200, body: { id: 1, status: 'running' } }, { code: 200, body: { id: 1, status: 'running' } }, ])); - const r = runDispatch('"test-owner/test-repo" "test.yml" "main" \'{"version":"1.2.3"}\' "http://localhost:18080" "test-token-abc123" "0.001"'); + const url = process.env.GITEA_API_URL; + const r = runDispatch(`"test-owner/test-repo" "test.yml" "main" '{"version":"1.2.3"}' "${url}" "test-token-abc123" "0.001"`); this.dispatchResult = r.status; }); diff --git a/tests/helpers/mock-api.sh b/tests/helpers/mock-api.sh index b5420fb..583b1c9 100644 --- a/tests/helpers/mock-api.sh +++ b/tests/helpers/mock-api.sh @@ -13,30 +13,13 @@ _free_port() { python3 -c "import socket; s=socket.socket(); s.bind(('',0)); print(s.getsockname()[1]); s.close()" } -_kill_port() { - local pids - pids=$(lsof -ti ":$MOCK_PORT" 2>/dev/null) || true - [ -n "$pids" ] && kill $pids 2>/dev/null || true - sleep 0.5 - pids=$(lsof -ti ":$MOCK_PORT" 2>/dev/null) || true - [ -n "$pids" ] && kill -9 $pids 2>/dev/null || true -} - -_wait_port_free() { - local i=0 - while lsof -ti ":$MOCK_PORT" >/dev/null 2>&1 && [ $i -lt 50 ]; do - sleep 0.1 - i=$((i + 1)) - done -} - _wait_port_ready() { local i=0 - while [ $i -lt 50 ]; do + while [ $i -lt 10 ]; do if nc -z localhost "$MOCK_PORT" 2>/dev/null; then return 0 fi - sleep 0.2 + sleep 0.1 i=$((i + 1)) done return 1 @@ -73,9 +56,6 @@ mock_start() { echo "$MOCK_RESPONSE_CODE" >> "$MOCK_CONFIG_FILE" fi - _kill_port - _wait_port_free - nohup python3 "$(dirname "${BASH_SOURCE[0]}")/mock-server.py" "$MOCK_PORT" "$MOCK_CONFIG_FILE" "$MOCK_REQUEST_FILE" \ /dev/null 2>&1 & MOCK_PID=$! @@ -84,8 +64,6 @@ mock_start() { mock_stop() { [ -n "${MOCK_PID:-}" ] && kill -9 "$MOCK_PID" 2>/dev/null || true - _kill_port - _wait_port_free [ -n "${MOCK_REQUEST_FILE:-}" ] && rm -f "${MOCK_REQUEST_FILE}" 2>/dev/null || true [ -n "${MOCK_SEQUENCE_FILE:-}" ] && rm -f "${MOCK_SEQUENCE_FILE}" 2>/dev/null || true [ -n "${MOCK_SEQUENCE_FILE:-}" ] && rm -f "${MOCK_SEQUENCE_FILE}.idx" 2>/dev/null || true