#!/bin/bash # # Authenticated gRPC Load Test using ghz # Tests API Gateway (port 50051) with JWT authentication # # Prerequisites: # - ghz installed (https://github.com/bojand/ghz) # - API Gateway running on port 50051 # - JWT_SECRET configured in .env set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' echo -e "${BLUE}=======================================================================${NC}" echo -e "${BLUE} FOXHUNT API GATEWAY - AUTHENTICATED gRPC LOAD TEST (ghz)${NC}" echo -e "${BLUE}=======================================================================${NC}" echo "" # Check if ghz is installed if ! command -v ghz &> /dev/null; then echo -e "${RED}❌ ghz is not installed${NC}" echo "" echo "Install with:" echo " Ubuntu/Debian: wget https://github.com/bojand/ghz/releases/download/v0.117.0/ghz-linux-x86_64.tar.gz && tar -xzf ghz-linux-x86_64.tar.gz && sudo mv ghz /usr/local/bin/" echo " MacOS: brew install ghz" echo " Arch: yay -S ghz" echo "" exit 1 fi # Check if jq is installed (for token parsing) if ! command -v jq &> /dev/null; then echo -e "${YELLOW}⚠️ jq not installed (recommended for result parsing)${NC}" echo "Install with: sudo apt-get install jq" fi # Configuration GRPC_HOST="${GRPC_HOST:-localhost:50051}" PROTO_PATH="$PROJECT_ROOT/tli/proto/trading.proto" SERVICE="foxhunt.tli.TradingService" OUTPUT_DIR="${OUTPUT_DIR:-$SCRIPT_DIR/results}" echo "Configuration:" echo " gRPC Host: $GRPC_HOST" echo " Proto: $PROTO_PATH" echo " Service: $SERVICE" echo " Output: $OUTPUT_DIR" echo "" # Create output directory mkdir -p "$OUTPUT_DIR" # Generate JWT token echo -e "${YELLOW}Generating JWT token...${NC}" JWT_TOKEN=$("$PROJECT_ROOT/tests/e2e_helpers/jwt_token_generator_fixed.sh" "load_test_user" "trader") if [ -z "$JWT_TOKEN" ]; then echo -e "${RED}❌ Failed to generate JWT token${NC}" exit 1 fi echo -e "${GREEN}✓ JWT token generated: ${JWT_TOKEN:0:50}...${NC}" echo "" # Test connectivity echo -e "${YELLOW}Testing API Gateway connectivity...${NC}" if ! nc -z localhost 50051 2>/dev/null; then echo -e "${RED}❌ API Gateway not running on port 50051${NC}" echo "Start with: docker-compose up -d api_gateway" exit 1 fi echo -e "${GREEN}✓ API Gateway is accessible${NC}" echo "" # Test 1: Baseline Authenticated Load (1K requests, 100 RPS) echo -e "${BLUE}=======================================================================${NC}" echo -e "${BLUE} TEST 1: BASELINE AUTHENTICATED LOAD (1,000 requests, 100 RPS)${NC}" echo -e "${BLUE}=======================================================================${NC}" OUTPUT_FILE="$OUTPUT_DIR/baseline_authenticated_$(date +%Y%m%d_%H%M%S).json" ghz --proto "$PROTO_PATH" \ --import-paths="$PROJECT_ROOT/tli/proto" \ --call "$SERVICE.SubmitOrder" \ --insecure \ --total 1000 \ --concurrency 10 \ --rps 100 \ --metadata "{\"authorization\":\"Bearer $JWT_TOKEN\"}" \ --data '{ "symbol": "BTC/USD", "side": "ORDER_SIDE_BUY", "order_type": "ORDER_TYPE_LIMIT", "quantity": 1.0, "price": 50000.0, "time_in_force": "GTC", "client_order_id": "auth-test-{{.RequestNumber}}" }' \ --format json \ --output "$OUTPUT_FILE" \ "$GRPC_HOST" || echo -e "${YELLOW}⚠️ Test 1 failed or partially completed${NC}" echo "" echo -e "${GREEN}✓ Test 1 complete. Results: $OUTPUT_FILE${NC}" # Parse results if jq available if command -v jq &> /dev/null && [ -f "$OUTPUT_FILE" ]; then echo "" echo -e "${BLUE}Results Summary:${NC}" echo " Total Requests: $(jq -r '.count' "$OUTPUT_FILE")" echo " Success Rate: $(jq -r '(.statusCodeDistribution["0"] / .count * 100 | floor)' "$OUTPUT_FILE")%" echo " P50 Latency: $(jq -r '.latencies.p50 / 1000000 | floor' "$OUTPUT_FILE")ms" echo " P95 Latency: $(jq -r '.latencies.p95 / 1000000 | floor' "$OUTPUT_FILE")ms" echo " P99 Latency: $(jq -r '.latencies.p99 / 1000000 | floor' "$OUTPUT_FILE")ms" echo " Throughput: $(jq -r '.rps | floor' "$OUTPUT_FILE") req/s" fi echo "" # Test 2: Medium Authenticated Load (5K requests, 500 RPS) echo -e "${BLUE}=======================================================================${NC}" echo -e "${BLUE} TEST 2: MEDIUM AUTHENTICATED LOAD (5,000 requests, 500 RPS)${NC}" echo -e "${BLUE}=======================================================================${NC}" OUTPUT_FILE="$OUTPUT_DIR/medium_authenticated_$(date +%Y%m%d_%H%M%S).json" ghz --proto "$PROTO_PATH" \ --import-paths="$PROJECT_ROOT/tli/proto" \ --call "$SERVICE.SubmitOrder" \ --insecure \ --total 5000 \ --concurrency 50 \ --rps 500 \ --metadata "{\"authorization\":\"Bearer $JWT_TOKEN\"}" \ --data '{ "symbol": "ETH/USD", "side": "ORDER_SIDE_SELL", "order_type": "ORDER_TYPE_LIMIT", "quantity": 10.0, "price": 3000.0, "time_in_force": "GTC", "client_order_id": "auth-test-{{.RequestNumber}}" }' \ --format json \ --output "$OUTPUT_FILE" \ "$GRPC_HOST" || echo -e "${YELLOW}⚠️ Test 2 failed or partially completed${NC}" echo "" echo -e "${GREEN}✓ Test 2 complete. Results: $OUTPUT_FILE${NC}" if command -v jq &> /dev/null && [ -f "$OUTPUT_FILE" ]; then echo "" echo -e "${BLUE}Results Summary:${NC}" echo " Total Requests: $(jq -r '.count' "$OUTPUT_FILE")" echo " Success Rate: $(jq -r '(.statusCodeDistribution["0"] / .count * 100 | floor)' "$OUTPUT_FILE")%" echo " P50 Latency: $(jq -r '.latencies.p50 / 1000000 | floor' "$OUTPUT_FILE")ms" echo " P95 Latency: $(jq -r '.latencies.p95 / 1000000 | floor' "$OUTPUT_FILE")ms" echo " P99 Latency: $(jq -r '.latencies.p99 / 1000000 | floor' "$OUTPUT_FILE")ms" echo " Throughput: $(jq -r '.rps | floor' "$OUTPUT_FILE") req/s" fi echo "" # Test 3: High Authenticated Load (10K requests, 1K RPS) echo -e "${BLUE}=======================================================================${NC}" echo -e "${BLUE} TEST 3: HIGH AUTHENTICATED LOAD (10,000 requests, 1K RPS)${NC}" echo -e "${BLUE}=======================================================================${NC}" OUTPUT_FILE="$OUTPUT_DIR/high_authenticated_$(date +%Y%m%d_%H%M%S).json" ghz --proto "$PROTO_PATH" \ --import-paths="$PROJECT_ROOT/tli/proto" \ --call "$SERVICE.SubmitOrder" \ --insecure \ --total 10000 \ --concurrency 100 \ --rps 1000 \ --metadata "{\"authorization\":\"Bearer $JWT_TOKEN\"}" \ --data '{ "symbol": "SOL/USD", "side": "ORDER_SIDE_BUY", "order_type": "ORDER_TYPE_MARKET", "quantity": 100.0, "time_in_force": "IOC", "client_order_id": "auth-test-{{.RequestNumber}}" }' \ --format json \ --output "$OUTPUT_FILE" \ "$GRPC_HOST" || echo -e "${YELLOW}⚠️ Test 3 failed or partially completed${NC}" echo "" echo -e "${GREEN}✓ Test 3 complete. Results: $OUTPUT_FILE${NC}" if command -v jq &> /dev/null && [ -f "$OUTPUT_FILE" ]; then echo "" echo -e "${BLUE}Results Summary:${NC}" echo " Total Requests: $(jq -r '.count' "$OUTPUT_FILE")" echo " Success Rate: $(jq -r '(.statusCodeDistribution["0"] / .count * 100 | floor)' "$OUTPUT_FILE")%" echo " P50 Latency: $(jq -r '.latencies.p50 / 1000000 | floor' "$OUTPUT_FILE")ms" echo " P95 Latency: $(jq -r '.latencies.p95 / 1000000 | floor' "$OUTPUT_FILE")ms" echo " P99 Latency: $(jq -r '.latencies.p99 / 1000000 | floor' "$OUTPUT_FILE")ms" echo " Throughput: $(jq -r '.rps | floor' "$OUTPUT_FILE") req/s" fi echo "" # Test 4: Sustained Authenticated Load (2 minutes at 500 RPS) echo -e "${BLUE}=======================================================================${NC}" echo -e "${BLUE} TEST 4: SUSTAINED AUTHENTICATED LOAD (2 minutes, 500 RPS)${NC}" echo -e "${BLUE}=======================================================================${NC}" echo "Running sustained load for 2 minutes (60,000 total requests)..." OUTPUT_FILE="$OUTPUT_DIR/sustained_authenticated_$(date +%Y%m%d_%H%M%S).json" ghz --proto "$PROTO_PATH" \ --import-paths="$PROJECT_ROOT/tli/proto" \ --call "$SERVICE.SubmitOrder" \ --insecure \ --duration 120s \ --concurrency 50 \ --rps 500 \ --metadata "{\"authorization\":\"Bearer $JWT_TOKEN\"}" \ --data '{ "symbol": "AVAX/USD", "side": "{{randomString \"ORDER_SIDE_BUY\" \"ORDER_SIDE_SELL\"}}", "order_type": "ORDER_TYPE_LIMIT", "quantity": {{randomInt 1 100}}, "price": {{randomInt 10 100}}, "time_in_force": "GTC", "client_order_id": "sustained-{{.RequestNumber}}" }' \ --format json \ --output "$OUTPUT_FILE" \ "$GRPC_HOST" || echo -e "${YELLOW}⚠️ Test 4 failed or partially completed${NC}" echo "" echo -e "${GREEN}✓ Test 4 complete. Results: $OUTPUT_FILE${NC}" if command -v jq &> /dev/null && [ -f "$OUTPUT_FILE" ]; then echo "" echo -e "${BLUE}Results Summary:${NC}" echo " Total Requests: $(jq -r '.count' "$OUTPUT_FILE")" echo " Success Rate: $(jq -r '(.statusCodeDistribution["0"] / .count * 100 | floor)' "$OUTPUT_FILE")%" echo " P50 Latency: $(jq -r '.latencies.p50 / 1000000 | floor' "$OUTPUT_FILE")ms" echo " P95 Latency: $(jq -r '.latencies.p95 / 1000000 | floor' "$OUTPUT_FILE")ms" echo " P99 Latency: $(jq -r '.latencies.p99 / 1000000 | floor' "$OUTPUT_FILE")ms" echo " Throughput: $(jq -r '.rps | floor' "$OUTPUT_FILE") req/s" fi echo "" echo -e "${BLUE}=======================================================================${NC}" echo -e "${BLUE} AUTHENTICATED LOAD TEST COMPLETE${NC}" echo -e "${BLUE}=======================================================================${NC}" echo "" echo "All results saved to: $OUTPUT_DIR" echo "" echo "Next steps:" echo " 1. Analyze detailed JSON results in $OUTPUT_DIR" echo " 2. Check Prometheus metrics: http://localhost:9091/metrics" echo " 3. Check Grafana dashboards: http://localhost:3000" echo ""