diff --git a/tests/e2e/mock/lib/common.sh b/tests/e2e/mock/lib/common.sh index 63ec993..499a76b 100644 --- a/tests/e2e/mock/lib/common.sh +++ b/tests/e2e/mock/lib/common.sh @@ -90,6 +90,25 @@ wait_for_status() { return 1 } +# Poll a URL until its body contains a substring, or fail. Used when the status +# code alone can't tell the states apart (e.g. captcha page vs backend, both 200). +# Usage: wait_for_body_contains URL NEEDLE [TIMEOUT_SECONDS] [curl args...] +wait_for_body_contains() { + local url="$1" needle="$2" timeout="${3:-30}" + shift 3 || true + local elapsed=0 body="" + while (( elapsed < timeout )); do + body=$(curl -s "$@" "$url" || true) + if grep -q "$needle" <<<"$body"; then + return 0 + fi + sleep 1 + elapsed=$((elapsed + 1)) + done + echo "wait_for_body_contains: $url did not contain \"$needle\" within ${timeout}s" >&2 + return 1 +} + # Assert a single curl returns the expected status code. # Usage: assert_status URL CODE [curl args...] assert_status() { diff --git a/tests/e2e/mock/scenarios/captcha/run.sh b/tests/e2e/mock/scenarios/captcha/run.sh index 4f6c39d..86ed301 100755 --- a/tests/e2e/mock/scenarios/captcha/run.sh +++ b/tests/e2e/mock/scenarios/captcha/run.sh @@ -11,15 +11,14 @@ body() { echo "[$SCENARIO] adding captcha decision for 1.2.3.4" lapi_add_decision 1.2.3.4 captcha 5m - echo "[$SCENARIO] waiting one stream tick + buffer..." - sleep 4 + # Status stays 200 before/after (captcha page vs backend), so gate on the body + # marker appearing once the captcha decision has been polled. + echo "[$SCENARIO] captcha page must be served once the decision is polled (200 + marker)" + wait_for_body_contains "http://127.0.0.1:${WEB_PORT}/foo" "E2E_CAPTCHA_PAGE_MARKER" 15 -H "X-Forwarded-For: 1.2.3.4" - echo "[$SCENARIO] captcha response must be HTTP 200 (the captcha page itself, not a 403)" + echo "[$SCENARIO] captcha response is HTTP 200 (the captcha page itself, not a 403)" assert_status "http://127.0.0.1:${WEB_PORT}/foo" 200 -H "X-Forwarded-For: 1.2.3.4" - echo "[$SCENARIO] captcha response body must contain the captcha template marker" - assert_body_contains "http://127.0.0.1:${WEB_PORT}/foo" "E2E_CAPTCHA_PAGE_MARKER" -H "X-Forwarded-For: 1.2.3.4" - echo "[$SCENARIO] non-flagged IP must still pass through to the backend" assert_status "http://127.0.0.1:${WEB_PORT}/foo" 200 -H "X-Forwarded-For: 5.6.7.8" } diff --git a/tests/e2e/mock/scenarios/custom-ban-page/run.sh b/tests/e2e/mock/scenarios/custom-ban-page/run.sh index 91f17ea..7ef4fbf 100755 --- a/tests/e2e/mock/scenarios/custom-ban-page/run.sh +++ b/tests/e2e/mock/scenarios/custom-ban-page/run.sh @@ -11,11 +11,8 @@ body() { echo "[$SCENARIO] adding ban decision" lapi_add_decision 1.2.3.4 ban 5m - echo "[$SCENARIO] waiting one stream tick + buffer..." - sleep 4 - - echo "[$SCENARIO] banned response status is 403" - assert_status "http://127.0.0.1:${WEB_PORT}/foo" 403 -H "X-Forwarded-For: 1.2.3.4" + echo "[$SCENARIO] banned response becomes 403 once the next stream poll lands" + wait_for_status "http://127.0.0.1:${WEB_PORT}/foo" 403 15 -H "X-Forwarded-For: 1.2.3.4" echo "[$SCENARIO] banned response Content-Type is HTML" assert_header "http://127.0.0.1:${WEB_PORT}/foo" Content-Type "text/html; charset=utf-8" -H "X-Forwarded-For: 1.2.3.4" diff --git a/tests/e2e/mock/scenarios/live-mode/run.sh b/tests/e2e/mock/scenarios/live-mode/run.sh index 7f73a02..2f90dd4 100755 --- a/tests/e2e/mock/scenarios/live-mode/run.sh +++ b/tests/e2e/mock/scenarios/live-mode/run.sh @@ -14,11 +14,10 @@ body() { echo "[$SCENARIO] adding ban decision for 1.2.3.4" lapi_add_decision 1.2.3.4 ban 5m - echo "[$SCENARIO] waiting for defaultDecisionSeconds cache to expire..." - sleep 3 - - echo "[$SCENARIO] next hit must re-query LAPI and now see the ban" - assert_status "http://127.0.0.1:${WEB_PORT}/foo" 403 -H "X-Forwarded-For: 1.2.3.4" + # Stays 200 until the cached 'allowed' (defaultDecisionSeconds) expires, then + # the re-query sees the ban — poll instead of guessing the cache TTL. + echo "[$SCENARIO] hit must turn 403 once the cached 'allowed' expires and LAPI is re-queried" + wait_for_status "http://127.0.0.1:${WEB_PORT}/foo" 403 15 -H "X-Forwarded-For: 1.2.3.4" echo "[$SCENARIO] another non-banned IP must still pass" assert_status "http://127.0.0.1:${WEB_PORT}/foo" 200 -H "X-Forwarded-For: 5.6.7.8" diff --git a/tests/e2e/mock/scenarios/stream-mode/run.sh b/tests/e2e/mock/scenarios/stream-mode/run.sh index 9c9cb50..c584502 100755 --- a/tests/e2e/mock/scenarios/stream-mode/run.sh +++ b/tests/e2e/mock/scenarios/stream-mode/run.sh @@ -14,11 +14,8 @@ body() { echo "[$SCENARIO] adding ban decision for 1.2.3.4" lapi_add_decision 1.2.3.4 ban 5m - echo "[$SCENARIO] waiting one stream tick + buffer..." - sleep 4 - - echo "[$SCENARIO] banned IP must be blocked (HTTP 403)" - assert_status "http://127.0.0.1:${WEB_PORT}/foo" 403 -H "X-Forwarded-For: 1.2.3.4" + echo "[$SCENARIO] banned IP must be blocked once the next stream poll lands (HTTP 403)" + wait_for_status "http://127.0.0.1:${WEB_PORT}/foo" 403 15 -H "X-Forwarded-For: 1.2.3.4" echo "[$SCENARIO] non-banned IP must still pass (HTTP 200)" assert_status "http://127.0.0.1:${WEB_PORT}/foo" 200 -H "X-Forwarded-For: 5.6.7.8" @@ -26,11 +23,8 @@ body() { echo "[$SCENARIO] deleting ban decision" lapi_delete_decision 1.2.3.4 - echo "[$SCENARIO] waiting one stream tick + buffer..." - sleep 4 - - echo "[$SCENARIO] previously banned IP must pass again" - assert_status "http://127.0.0.1:${WEB_PORT}/foo" 200 -H "X-Forwarded-For: 1.2.3.4" + echo "[$SCENARIO] previously banned IP must pass again once the deletion is polled" + wait_for_status "http://127.0.0.1:${WEB_PORT}/foo" 200 15 -H "X-Forwarded-For: 1.2.3.4" } run_scenario "$SCENARIO" "$HERE" body diff --git a/tests/e2e/mock/scenarios/trusted-ips/run.sh b/tests/e2e/mock/scenarios/trusted-ips/run.sh index 714cc24..4031c42 100755 --- a/tests/e2e/mock/scenarios/trusted-ips/run.sh +++ b/tests/e2e/mock/scenarios/trusted-ips/run.sh @@ -12,14 +12,13 @@ body() { lapi_add_decision 1.2.3.4 ban 5m lapi_add_decision 5.6.7.8 ban 5m - echo "[$SCENARIO] waiting one stream tick + buffer..." - sleep 4 + # The untrusted IP turning 403 is our signal that the bans have been polled; + # it also doubles as the control proving the bouncer is active. + echo "[$SCENARIO] untrusted banned IP must be blocked once the bans are polled (HTTP 403)" + wait_for_status "http://127.0.0.1:${WEB_PORT}/foo" 403 15 -H "X-Forwarded-For: 5.6.7.8" echo "[$SCENARIO] trusted IP must bypass the bouncer even though it is banned" assert_status "http://127.0.0.1:${WEB_PORT}/foo" 200 -H "X-Forwarded-For: 1.2.3.4" - - echo "[$SCENARIO] untrusted banned IP must be blocked (control: proves the bouncer is active)" - assert_status "http://127.0.0.1:${WEB_PORT}/foo" 403 -H "X-Forwarded-For: 5.6.7.8" } run_scenario "$SCENARIO" "$HERE" body