diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 19ebfde2..4fad504a 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -23,5 +23,12 @@ jobs: - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y bats jq + - name: Install gomplate + run: | + sudo curl -fsSL -o /usr/local/bin/gomplate \ + https://github.com/hairyhenderson/gomplate/releases/download/v4.3.3/gomplate_linux-amd64 + sudo chmod +x /usr/local/bin/gomplate + gomplate --version + - name: Run unit tests run: make test-unit diff --git a/k8s/deployment/tests/build_deployment.bats b/k8s/deployment/tests/build_deployment.bats index 41adfe2a..be88f49d 100644 --- a/k8s/deployment/tests/build_deployment.bats +++ b/k8s/deployment/tests/build_deployment.bats @@ -213,6 +213,8 @@ _render_context() { "capabilities": { "cpu_millicores": 100, "ram_memory": 128, + "cpu_millicores_limit": 200, + "ram_memory_limit": 256, "additional_ports": [], "scaling_type": "fixed", "autoscaling": { diff --git a/k8s/diagnose/tests/scope/health_probe_endpoints.bats b/k8s/diagnose/tests/scope/health_probe_endpoints.bats index 3621480d..92471cb4 100644 --- a/k8s/diagnose/tests/scope/health_probe_endpoints.bats +++ b/k8s/diagnose/tests/scope/health_probe_endpoints.bats @@ -180,7 +180,10 @@ EOF stripped=$(strip_ansi "$output") assert_contains "$stripped" "Readiness Probe on HTTP://8080/health:" assert_contains "$stripped" "HTTP 404 - Health check endpoint not found" - assert_contains "$stripped" "Update probe path or implement the endpoint in application" + # Remediation guidance lives in the structured evidence (suggested_actions), + # not in stdout - the check publishes it for the AI summarizer, not operators. + actions=$(jq -r '.evidence.suggested_actions[]' "$SCRIPT_OUTPUT_FILE") + assert_contains "$actions" "Update probe path or implement health endpoint in application" } @test "scope/health_probe_endpoints: updates status to failed on 404" { @@ -261,7 +264,10 @@ EOF stripped=$(strip_ansi "$output") assert_contains "$stripped" "Readiness Probe on HTTP://8080/health:" assert_contains "$stripped" "HTTP 500 - Application error" - assert_contains "$stripped" "Check application logs and fix internal errors or dependencies" + # Remediation guidance lives in the structured evidence (suggested_actions), + # not in stdout - the check publishes it for the AI summarizer, not operators. + actions=$(jq -r '.evidence.suggested_actions[]' "$SCRIPT_OUTPUT_FILE") + assert_contains "$actions" "Check application logs for internal errors" } @test "scope/health_probe_endpoints: updates status to warning on 500" { diff --git a/k8s/scope/tests/build_context.bats b/k8s/scope/tests/build_context.bats index bd86e56b..61b8563c 100644 --- a/k8s/scope/tests/build_context.bats +++ b/k8s/scope/tests/build_context.bats @@ -154,6 +154,7 @@ teardown() { "gateway_name": "co-gateway-public", "alb_name": "co-balancer-public", "component": "test-namespace-test-app", + "base_domain": "cloud-domain.io", "k8s_modifiers": {} }' @@ -213,6 +214,7 @@ teardown() { "gateway_name": "co-gateway-private", "alb_name": "co-balancer-private", "component": "test-namespace-test-app", + "base_domain": "cloud-domain.io", "k8s_modifiers": {} }' diff --git a/k8s/scope/tests/wait_on_balancer.bats b/k8s/scope/tests/wait_on_balancer.bats index 83d384d4..96f4406c 100644 --- a/k8s/scope/tests/wait_on_balancer.bats +++ b/k8s/scope/tests/wait_on_balancer.bats @@ -72,9 +72,15 @@ teardown() { # external_dns: Success after retries # ============================================================================= @test "wait_on_balancer: external_dns success after retries" { - local call_count=0 + # The script reads kubectl output via command substitution ($(kubectl ...)), + # which runs the mock in a subshell — an in-memory counter would reset every + # call. Persist the attempt count in a file so it survives across subshells. + export CALL_COUNT_FILE="$BATS_TEST_TMPDIR/dnsendpoint_calls" + echo 0 > "$CALL_COUNT_FILE" kubectl() { - call_count=$((call_count + 1)) + local call_count + call_count=$(($(cat "$CALL_COUNT_FILE") + 1)) + echo "$call_count" > "$CALL_COUNT_FILE" case "$*" in "get dnsendpoint k8s-my-app-my-scope-scope-123-dns -n default-namespace -o jsonpath={.status.observedGeneration}") if [ "$call_count" -ge 2 ]; then diff --git a/k8s/utils/assume_role_lib b/k8s/utils/assume_role_lib index 58c08352..ca008e13 100644 --- a/k8s/utils/assume_role_lib +++ b/k8s/utils/assume_role_lib @@ -17,11 +17,14 @@ arn_for_selector() { local json="$1" selector="$2" [ -n "$json" ] || return 0 [ -n "$selector" ] || return 0 + # jq exits non-zero (status 5) on malformed JSON; swallow it so the helper + # honors its "never crashes, returns empty" contract instead of propagating + # jq's failure as the function's return code. printf '%s' "$json" | jq -r --arg sel "$selector" ' [ .iam_role_arns.arns[]? | select(.selector == $sel) | .arn ] - | first // ""' 2>/dev/null + | first // ""' 2>/dev/null || true } # resolve_assume_role_arn diff --git a/testing/run_bats_tests.sh b/testing/run_bats_tests.sh index d17384e6..91c7edc7 100755 --- a/testing/run_bats_tests.sh +++ b/testing/run_bats_tests.sh @@ -82,11 +82,24 @@ run_tests_in_dir() { local exit_code=0 ( cd "$test_dir" - # Use script to force TTY for colored output - # Exclude integration directory - those tests are run by run_integration_tests.sh - # --print-output-on-failure: only show test output when a test fails - script -q /dev/null bats --formatter pretty --print-output-on-failure $(find . -name "*.bats" -not -path "*/integration/*" | sort) - ) 2>&1 | tee "$temp_output" || exit_code=$? + # Force a TTY so the bats "pretty" formatter renders; --print-output-on-failure + # only dumps test output when a test fails. Exclude integration tests (run + # separately). `script`'s command syntax differs by platform: BSD/macOS takes a + # positional command (script ), while util-linux (Linux CI) needs + # -c "" . Using the wrong one silently runs zero tests. + local test_files + test_files=$(find . -name "*.bats" -not -path "*/integration/*" | sort | tr '\n' ' ') + local bats_cmd="bats --formatter pretty --print-output-on-failure $test_files" + # The pretty formatter shells out to `tput`, which needs $TERM. CI runners and + # containers often leave it unset - default it so tput can find a terminfo entry. + export TERM="${TERM:-xterm}" + if [ "$(uname)" = "Darwin" ]; then + script -q /dev/null $bats_cmd + else + script -qec "$bats_cmd" /dev/null + fi + ) 2>&1 | tee "$temp_output" + exit_code=${PIPESTATUS[0]} # Extract failed tests from output # Strip all ANSI escape codes (colors, cursor movements, etc.) @@ -191,4 +204,12 @@ if [ ${#FAILED_TESTS[@]} -gt 0 ]; then exit 1 fi +# A non-zero runner exit with no parsed "✗" lines means bats itself failed to run +# (e.g. a malformed .bats file or a broken `script` invocation) - never report success. +if [ "$HAS_FAILURES" -ne 0 ]; then + echo -e "${RED}BATS runner exited with errors but no individual test failures were parsed.${NC}" + echo -e "${RED}This usually means the test harness failed to execute - check the output above.${NC}" + exit 1 +fi + echo -e "${GREEN}All BATS tests passed!${NC}"