diff --git a/tests/features/step_definitions/gitops-update.steps.js b/tests/features/step_definitions/gitops-update.steps.js index f466833..e690433 100644 --- a/tests/features/step_definitions/gitops-update.steps.js +++ b/tests/features/step_definitions/gitops-update.steps.js @@ -1,11 +1,12 @@ -const { execSync } = require('child_process'); -const { Before, When, Then } = require('@cucumber/cucumber'); +const { spawnSync, execSync } = require('child_process'); +const { Before, After, Given, When, Then } = require('@cucumber/cucumber'); const path = require('path'); const PROJECT_ROOT = path.resolve(__dirname, '..', '..', '..'); const MOCK_SCRIPT = path.join(PROJECT_ROOT, 'tests', 'helpers', 'mock-api.sh'); const GITOPS_SCRIPT = path.join(PROJECT_ROOT, 'scripts', 'gitops-update.sh'); const MOCK_HELPERS = path.join(PROJECT_ROOT, 'tests', 'helpers'); +const REQ_FILE = '/tmp/gitops-mock-requests.log'; const BASE_ENV = { INPUT_FILE: 'dev/Chart.yaml', @@ -20,58 +21,75 @@ const BASE_ENV = { Before({ tags: '@mock' }, function () { process.env.PATH = `${MOCK_HELPERS}:${process.env.PATH}`; + try { execSync('rm -f /tmp/gitops-mock-requests.log', { stdio: 'ignore' }); } catch (_) {} + // Restart mock with known request file path + const result = spawnSync('bash', ['-c', ` + source "${MOCK_SCRIPT}" + mock_stop 2>/dev/null + MOCK_REQUEST_FILE="${REQ_FILE}" + mock_start + sleep 0.5 + curl -s -o /dev/null -w "%{http_code}" --max-time 3 http://localhost:18080/api/v1/repos/health + `], { + cwd: PROJECT_ROOT, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] + }); + const code = result.stdout.trim(); + if (!code.startsWith('2') && !code.startsWith('4')) { + throw new Error(`GitOps mock restart failed: ${result.stderr.substring(0,200)}`); + } +}); + +After({ tags: '@mock' }, function () { + spawnSync('bash', ['-c', `source "${MOCK_SCRIPT}" && mock_stop 2>/dev/null`], { stdio: 'ignore' }); + try { execSync('rm -f /tmp/gitops-mock-requests.log', { stdio: 'ignore' }); } catch (_) {} }); function bash(cmd) { - try { - const out = execSync(`bash -c '${cmd}'`, { - cwd: PROJECT_ROOT, - encoding: 'utf-8', - stdio: ['pipe', 'pipe', 'pipe'], - }); - return { status: 0, stdout: out, stderr: '' }; - } catch (e) { - return { status: e.status, stdout: e.stdout || '', stderr: e.stderr || '' }; - } + const result = spawnSync('bash', ['-c', cmd], { + cwd: PROJECT_ROOT, + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }); + return { status: result.status, stdout: result.stdout || '', stderr: result.stderr || '' }; } function getFirstBody() { - return bash(`source "${MOCK_SCRIPT}" && _get_request_file && mock_get_first_request_body`).stdout.trim(); + return bash(`grep -A1 '^POST ' "${REQ_FILE}" 2>/dev/null | head -2 | tail -1 || echo ""`).stdout.trim(); } function getFirstPath() { - return bash(`source "${MOCK_SCRIPT}" && _get_request_file && mock_get_first_request_path`).stdout.trim(); + return bash(`grep '^POST ' "${REQ_FILE}" 2>/dev/null | head -1 | awk '{print $2}' || echo ""`).stdout.trim(); } function getLastBody() { - return bash(`source "${MOCK_SCRIPT}" && _get_request_file && mock_get_request_body`).stdout.trim(); + return bash(`grep -A1 '^POST ' "${REQ_FILE}" 2>/dev/null | grep -v '^POST ' | tail -1 || echo ""`).stdout.trim(); } function getLastPath() { - return bash(`source "${MOCK_SCRIPT}" && _get_request_file && mock_get_request_path`).stdout.trim(); + return bash(`grep '^POST ' "${REQ_FILE}" 2>/dev/null | tail -1 | awk '{print $2}' || echo ""`).stdout.trim(); } function requestCount() { - const rf = bash(`source "${MOCK_SCRIPT}" && _get_request_file`).stdout.trim(); - const count = bash(`grep -c '^POST ' "${rf}" 2>/dev/null || echo 0`).stdout.trim(); - return parseInt(count, 10) || 0; + return parseInt(bash(`grep -c '^POST ' "${REQ_FILE}" 2>/dev/null || echo 0`).stdout.trim(), 10) || 0; } function runScript(envOverrides) { const env = { ...BASE_ENV, ...envOverrides }; - const envStr = Object.entries(env) - .map(([k, v]) => { - const escaped = v.replace(/'/g, "'\\''"); - return `${k}='${escaped}'`; - }) - .join(' '); - return bash(`${envStr} bash "${GITOPS_SCRIPT}"`); + const scriptPath = `/tmp/gitops-run-${Date.now()}.sh`; + const exports = Object.entries(env) + .map(([k, v]) => `export ${k}="${v.replace(/"/g, '\\"')}"`) + .join('\n'); + require('fs').writeFileSync(scriptPath, `${exports}\nexport PATH="${MOCK_HELPERS}:$PATH"\nset -euo pipefail\nbash "${GITOPS_SCRIPT}"\nsync\n`, 'utf8'); + try { + return bash(`bash "${scriptPath}"`); + } finally { + require('fs').unlinkSync(scriptPath); + } } Given('insufficient environment variables are provided for the GitOps update', function () { this.missingVar = 'INPUT_FILE'; - this.envOverrides = {}; - this.envOverrides.INPUT_FILE = ''; + this.envOverrides = { INPUT_FILE: '' }; }); Given('the GitOps repository clone will fail', function () { @@ -110,11 +128,11 @@ Then('the caller commit shows a failure status', function () { }); Then('the script exits with error', function () { - if (this.result.status === 0) throw new Error(`Expected non-zero exit, got 0. stderr: ${this.result.stderr}`); + if (this.result.status === 0) throw new Error(`Expected non-zero exit, got 0. stderr: ${this.result.stderr.substring(0,200)}`); }); Then('the script exits successfully', function () { - if (this.result.status !== 0) throw new Error(`Expected exit 0, got ${this.result.status}: ${this.result.stderr}`); + if (this.result.status !== 0) throw new Error(`Expected exit 0, got ${this.result.status}: ${this.result.stderr.substring(0,200)}`); }); Then('the caller commit shows a success status with a link to the GitOps commit', function () { diff --git a/tests/helpers/git b/tests/helpers/git index 3afe3ed..207c5a2 100755 --- a/tests/helpers/git +++ b/tests/helpers/git @@ -11,15 +11,8 @@ fi case "$1" in clone) TARGET_DIR="${@: -1}" - mkdir -p "$TARGET_DIR" - cd "$TARGET_DIR" - git init --initial-branch=main 2>/dev/null - git config user.email "mock@test.com" - git config user.name "Mock Test" - mkdir -p "$(dirname "$INPUT_FILE")" - echo 'version: 0.1.0' > "$INPUT_FILE" - git add -A 2>/dev/null - git commit -m "initial" 2>/dev/null + mkdir -p "${TARGET_DIR}/$(dirname "$INPUT_FILE")" + echo 'version: 0.1.0' > "${TARGET_DIR}/${INPUT_FILE}" echo "Cloning into '$TARGET_DIR'..." ;; add|commit|push|config|init) diff --git a/tests/helpers/mock-api.sh b/tests/helpers/mock-api.sh index e6ad4b5..826ccb1 100644 --- a/tests/helpers/mock-api.sh +++ b/tests/helpers/mock-api.sh @@ -46,7 +46,7 @@ mock_clear_sequence() { mock_start() { MOCK_RESPONSE_CODE="${MOCK_RESPONSE_CODE:-201}" - MOCK_REQUEST_FILE=$(mktemp) + MOCK_REQUEST_FILE="${MOCK_REQUEST_FILE:-$(mktemp)}" echo "$MOCK_REQUEST_FILE" > "$MOCK_STATE_FILE" MOCK_CONFIG_FILE=$(mktemp)