From 9ea2d0bbe307631e0a6d0d779ebad5ae8599a010 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 26 Jun 2026 03:51:48 +0000 Subject: [PATCH 1/3] T5: add E2E smoke test and CI workflow - Add tests/e2e/smoke-test.sh: checks gateway health, verifies 401 on unauthenticated /api/customers, and 200 on customer-service /healthz - Add src/docker-compose.e2e.yml: minimal compose with postgres, customer-service, and api-gateway (avoids Notification.API build bug) - Add .github/workflows/e2e.yml: runs smoke tests on push/PR to main and devin/** branches --- .github/workflows/e2e.yml | 35 +++++++++++++++++++++++++++++ src/docker-compose.e2e.yml | 35 +++++++++++++++++++++++++++++ tests/e2e/smoke-test.sh | 46 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 .github/workflows/e2e.yml create mode 100644 src/docker-compose.e2e.yml create mode 100755 tests/e2e/smoke-test.sh diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 0000000..ccbbcc8 --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,35 @@ +name: E2E Smoke Tests + +on: + push: + branches: [main, 'devin/**'] + pull_request: + branches: [main, 'devin/**'] + +jobs: + e2e-smoke: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build and start services + working-directory: src + run: | + docker compose -f docker-compose.e2e.yml up -d --build + echo "Waiting for services to start..." + sleep 30 + + - name: Run smoke tests + run: | + chmod +x tests/e2e/smoke-test.sh + ./tests/e2e/smoke-test.sh + + - name: Show logs on failure + if: failure() + working-directory: src + run: docker compose -f docker-compose.e2e.yml logs + + - name: Cleanup + if: always() + working-directory: src + run: docker compose -f docker-compose.e2e.yml down -v diff --git a/src/docker-compose.e2e.yml b/src/docker-compose.e2e.yml new file mode 100644 index 0000000..a685a89 --- /dev/null +++ b/src/docker-compose.e2e.yml @@ -0,0 +1,35 @@ +services: + api-gateway: + build: + context: . + dockerfile: ApiGateway/Dockerfile + ports: + - "5000:5000" + depends_on: + - customer-service + environment: + - ASPNETCORE_ENVIRONMENT=Development + + customer-service: + build: + context: . + dockerfile: Services/Customer/Customer.API/Dockerfile + ports: + - "5002:5002" + depends_on: + - postgres + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ConnectionStrings__DefaultConnection=Host=postgres;Database=customerdb;Username=postgres;Password=postgres + + postgres: + image: postgres:16-alpine + ports: + - "5432:5432" + environment: + POSTGRES_PASSWORD: postgres + volumes: + - postgres_data:/var/lib/postgresql/data + +volumes: + postgres_data: diff --git a/tests/e2e/smoke-test.sh b/tests/e2e/smoke-test.sh new file mode 100755 index 0000000..d2ec5d1 --- /dev/null +++ b/tests/e2e/smoke-test.sh @@ -0,0 +1,46 @@ +#!/bin/bash +set -euo pipefail + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:5000}" +MAX_RETRIES=30 +RETRY_INTERVAL=5 + +echo "Waiting for gateway to be healthy..." +for i in $(seq 1 $MAX_RETRIES); do + if curl -sf "$GATEWAY_URL/healthz" > /dev/null 2>&1; then + echo "Gateway is healthy!" + break + fi + if [ "$i" -eq "$MAX_RETRIES" ]; then + echo "Gateway failed to become healthy after $((MAX_RETRIES * RETRY_INTERVAL))s" + exit 1 + fi + echo "Attempt $i/$MAX_RETRIES - waiting ${RETRY_INTERVAL}s..." + sleep $RETRY_INTERVAL +done + +echo "" +echo "=== Smoke Test: GET /api/customers (unauthenticated - expect 401) ===" +HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$GATEWAY_URL/api/customers") +echo "Response code: $HTTP_CODE" +if [ "$HTTP_CODE" = "401" ]; then + echo "PASS: Unauthenticated request correctly returns 401" +else + echo "FAIL: Expected 401, got $HTTP_CODE" + exit 1 +fi + +echo "" +echo "=== Smoke Test: GET /healthz on customer-service (via direct port) ===" +CUSTOMER_URL="${CUSTOMER_URL:-http://localhost:5002}" +HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$CUSTOMER_URL/healthz") +echo "Response code: $HTTP_CODE" +if [ "$HTTP_CODE" = "200" ]; then + echo "PASS: Customer service health check returns 200" +else + echo "FAIL: Expected 200, got $HTTP_CODE" + exit 1 +fi + +echo "" +echo "All smoke tests passed!" From 57fe3b08f49fec64a01f9613c9fd6c64fd1f7caa Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 26 Jun 2026 03:55:53 +0000 Subject: [PATCH 2/3] fix: set POSTGRES_DB in E2E compose for future CRUD tests --- src/docker-compose.e2e.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/docker-compose.e2e.yml b/src/docker-compose.e2e.yml index a685a89..5f2fb83 100644 --- a/src/docker-compose.e2e.yml +++ b/src/docker-compose.e2e.yml @@ -27,6 +27,7 @@ services: ports: - "5432:5432" environment: + POSTGRES_DB: customerdb POSTGRES_PASSWORD: postgres volumes: - postgres_data:/var/lib/postgresql/data From cb243fb1fb29684cd3491f0d045a62edc24ccc2a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 26 Jun 2026 04:00:28 +0000 Subject: [PATCH 3/3] fix: wait for customer-service readiness before proxy test Reorder smoke tests: poll customer-service /healthz before testing the gateway proxy route, preventing 502 flakiness when customer-service starts slower than the gateway. --- tests/e2e/smoke-test.sh | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/tests/e2e/smoke-test.sh b/tests/e2e/smoke-test.sh index d2ec5d1..749f203 100755 --- a/tests/e2e/smoke-test.sh +++ b/tests/e2e/smoke-test.sh @@ -2,6 +2,7 @@ set -euo pipefail GATEWAY_URL="${GATEWAY_URL:-http://localhost:5000}" +CUSTOMER_URL="${CUSTOMER_URL:-http://localhost:5002}" MAX_RETRIES=30 RETRY_INTERVAL=5 @@ -20,19 +21,22 @@ for i in $(seq 1 $MAX_RETRIES); do done echo "" -echo "=== Smoke Test: GET /api/customers (unauthenticated - expect 401) ===" -HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$GATEWAY_URL/api/customers") -echo "Response code: $HTTP_CODE" -if [ "$HTTP_CODE" = "401" ]; then - echo "PASS: Unauthenticated request correctly returns 401" -else - echo "FAIL: Expected 401, got $HTTP_CODE" - exit 1 -fi +echo "Waiting for customer-service to be healthy..." +for i in $(seq 1 $MAX_RETRIES); do + if curl -sf "$CUSTOMER_URL/healthz" > /dev/null 2>&1; then + echo "Customer service is healthy!" + break + fi + if [ "$i" -eq "$MAX_RETRIES" ]; then + echo "Customer service failed to become healthy after $((MAX_RETRIES * RETRY_INTERVAL))s" + exit 1 + fi + echo "Attempt $i/$MAX_RETRIES - waiting ${RETRY_INTERVAL}s..." + sleep $RETRY_INTERVAL +done echo "" echo "=== Smoke Test: GET /healthz on customer-service (via direct port) ===" -CUSTOMER_URL="${CUSTOMER_URL:-http://localhost:5002}" HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$CUSTOMER_URL/healthz") echo "Response code: $HTTP_CODE" if [ "$HTTP_CODE" = "200" ]; then @@ -42,5 +46,16 @@ else exit 1 fi +echo "" +echo "=== Smoke Test: GET /api/customers (unauthenticated - expect 401) ===" +HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$GATEWAY_URL/api/customers") +echo "Response code: $HTTP_CODE" +if [ "$HTTP_CODE" = "401" ]; then + echo "PASS: Unauthenticated request correctly returns 401" +else + echo "FAIL: Expected 401, got $HTTP_CODE" + exit 1 +fi + echo "" echo "All smoke tests passed!"