diff --git a/src/Services/Product/tests/docker-compose.test.yml b/src/Services/Product/tests/docker-compose.test.yml new file mode 100644 index 0000000..a18604c --- /dev/null +++ b/src/Services/Product/tests/docker-compose.test.yml @@ -0,0 +1,46 @@ +version: '3.8' + +services: + productdb: + image: postgres:16-alpine + environment: + POSTGRES_PASSWORD: postgres + POSTGRES_DB: productdb + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 2s + timeout: 5s + retries: 10 + + product-service: + build: + context: ../../../ + dockerfile: Services/Product/Product.API/Dockerfile + ports: + - "5004:5004" + depends_on: + productdb: + condition: service_healthy + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ConnectionStrings__DefaultConnection=Host=productdb;Database=productdb;Username=postgres;Password=postgres + healthcheck: + test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/localhost/5004' || exit 1"] + interval: 2s + timeout: 5s + retries: 15 + + api-gateway: + build: + context: ../../../ + dockerfile: ApiGateway/Dockerfile + ports: + - "5000:5000" + depends_on: + product-service: + condition: service_healthy + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ReverseProxy__Clusters__product-cluster__Destinations__destination1__Address=http://product-service:5004/ diff --git a/src/Services/Product/tests/smoke-test.sh b/src/Services/Product/tests/smoke-test.sh new file mode 100755 index 0000000..c20c538 --- /dev/null +++ b/src/Services/Product/tests/smoke-test.sh @@ -0,0 +1,140 @@ +#!/bin/bash +set -e + +GATEWAY_URL="${GATEWAY_URL:-http://localhost:5000}" +SERVICE_URL="${SERVICE_URL:-http://localhost:5004}" +JWT_SECRET="QuickApp_Microservices_SuperSecret_Key_For_Dev_Only_Min_32_Chars!" +PASS=0 +FAIL=0 + +# Generate a JWT token +generate_jwt() { + local header='{"alg":"HS256","typ":"JWT"}' + local payload="{\"sub\":\"test-user\",\"iss\":\"quickapp-identity\",\"aud\":\"quickapp-api\",\"exp\":$(($(date +%s) + 3600))}" + + local header_b64=$(echo -n "$header" | base64 -w0 | tr '+/' '-_' | tr -d '=') + local payload_b64=$(echo -n "$payload" | base64 -w0 | tr '+/' '-_' | tr -d '=') + local signature=$(echo -n "${header_b64}.${payload_b64}" | openssl dgst -sha256 -hmac "$JWT_SECRET" -binary | base64 -w0 | tr '+/' '-_' | tr -d '=') + + echo "${header_b64}.${payload_b64}.${signature}" +} + +assert_status() { + local description="$1" + local expected="$2" + local actual="$3" + + if [ "$actual" = "$expected" ]; then + echo "PASS: $description (HTTP $actual)" + PASS=$((PASS + 1)) + else + echo "FAIL: $description — expected $expected, got $actual" + FAIL=$((FAIL + 1)) + fi +} + +# Wait for services to be ready +wait_for_service() { + local url="$1" + local name="$2" + local retries=30 + local i=0 + echo "Waiting for $name at $url ..." + while [ $i -lt $retries ]; do + if curl -sf -o /dev/null "$url"; then + echo "$name is ready" + return 0 + fi + i=$((i + 1)) + sleep 1 + done + echo "ERROR: $name not ready after ${retries}s" + return 1 +} + +wait_for_service "$SERVICE_URL/healthz" "product-service" +wait_for_service "$GATEWAY_URL/healthz" "api-gateway" + +TOKEN=$(generate_jwt) +echo "Generated JWT token" +echo "Testing against gateway: $GATEWAY_URL" +echo "Testing against service: $SERVICE_URL" +echo "---" + +# Test 1: Health check (direct to service — public) +STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$SERVICE_URL/healthz") +assert_status "GET /healthz (direct) → 200" "200" "$STATUS" + +# Test 2: GET /api/product without token → 401 (direct) +STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$SERVICE_URL/api/product") +assert_status "GET /api/product without token (direct) → 401" "401" "$STATUS" + +# Test 3: GET /api/products without token → 401 (through gateway) +STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$GATEWAY_URL/api/products") +assert_status "GET /api/products without token (gateway) → 401" "401" "$STATUS" + +# Test 4: Create a category with valid JWT → 201 +RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "$GATEWAY_URL/api/products/categories" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name":"Electronics","description":"Electronic devices","icon":"electronics-icon"}') +STATUS=$(echo "$RESPONSE" | tail -1) +BODY=$(echo "$RESPONSE" | sed '$d') +assert_status "POST /api/products/categories with JWT → 201" "201" "$STATUS" + +# Extract category ID +CATEGORY_ID=$(echo "$BODY" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2) +echo "Created category ID: $CATEGORY_ID" + +# Test 5: Create a product with valid JWT → 201 +if [ -z "$CATEGORY_ID" ]; then + echo "FAIL: POST /api/products — skipped, no category ID from previous step" + FAIL=$((FAIL + 1)) + PRODUCT_ID="" +else + RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "$GATEWAY_URL/api/products" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"name\":\"Test Product\",\"description\":\"A test product\",\"icon\":\"test-icon\",\"buyingPrice\":10.50,\"sellingPrice\":15.99,\"unitsInStock\":100,\"isActive\":true,\"isDiscontinued\":false,\"parentId\":null,\"productCategoryId\":$CATEGORY_ID}") + STATUS=$(echo "$RESPONSE" | tail -1) + BODY=$(echo "$RESPONSE" | sed '$d') + assert_status "POST /api/products with JWT → 201" "201" "$STATUS" + + # Extract product ID + PRODUCT_ID=$(echo "$BODY" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2) + echo "Created product ID: $PRODUCT_ID" +fi + +# Test 6: GET /api/products with valid JWT → 200 (list includes product) +RESPONSE=$(curl -s -w "\n%{http_code}" "$GATEWAY_URL/api/products" \ + -H "Authorization: Bearer $TOKEN") +STATUS=$(echo "$RESPONSE" | tail -1) +BODY=$(echo "$RESPONSE" | sed '$d') +assert_status "GET /api/products with JWT → 200" "200" "$STATUS" + +# Verify the product is in the list +if echo "$BODY" | grep -q "Test Product"; then + echo "PASS: Response contains 'Test Product'" + PASS=$((PASS + 1)) +else + echo "FAIL: Response does not contain 'Test Product'" + FAIL=$((FAIL + 1)) +fi + +# Test 7: GET /api/products/{id} with valid JWT → 200 +if [ -z "$PRODUCT_ID" ]; then + echo "FAIL: GET /api/products/{id} — skipped, no product ID from previous step" + FAIL=$((FAIL + 1)) +else + STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$GATEWAY_URL/api/products/$PRODUCT_ID" \ + -H "Authorization: Bearer $TOKEN") + assert_status "GET /api/products/$PRODUCT_ID with JWT → 200" "200" "$STATUS" +fi + +echo "---" +echo "Results: $PASS passed, $FAIL failed" + +if [ $FAIL -gt 0 ]; then + exit 1 +fi +echo "All smoke tests passed!"