Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/build-load-test-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Build Load Test Image

on:
push:
branches:
- main
- develop
paths:
- "apps/load-tests/**"
- "package.json"
- "yarn.lock"
pull_request:
branches:
- main
- develop
paths:
- "apps/load-tests/**"
- "package.json"
- "yarn.lock"

jobs:
build:
name: Build Docker Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}/load-test
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./apps/load-tests/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Image digest
run: echo "Image built: ${{ steps.meta.outputs.tags }}"
112 changes: 112 additions & 0 deletions .github/workflows/load-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: Load Test

on:
workflow_dispatch:
inputs:
environment:
description: "Target environment"
required: true
type: choice
options:
- dev
- uat
- prod
scenario:
description: "Test scenario"
required: true
type: choice
options:
- connection-storm
- steady-state
connections:
description: "Number of connections"
required: false
type: string
default: "100"
duration:
description: "Test duration in seconds (for steady-state)"
required: false
type: string
default: "60"
rampUp:
description: "Ramp-up time in seconds"
required: false
type: string
default: "10"

jobs:
load-test:
name: Run Load Test
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Validate inputs
run: |
echo "Environment: ${{ github.event.inputs.environment }}"
echo "Scenario: ${{ github.event.inputs.scenario }}"
echo "Connections: ${{ github.event.inputs.connections }}"
echo "Duration: ${{ github.event.inputs.duration }}"
echo "Ramp-up: ${{ github.event.inputs.rampUp }}"

- name: Require manual approval for production
if: github.event.inputs.environment == 'prod'
uses: trstringer/manual-approval@v1
with:
secret: ${{ github.TOKEN }}
approvers: "chakra-guy,adonesky1,jiexi,ffmcgee725,wenfix"
minimum-approvals: 1
issue-title: "Approve production load test"
issue-body: |
Production load test requested:
- Environment: ${{ github.event.inputs.environment }}
- Scenario: ${{ github.event.inputs.scenario }}
- Connections: ${{ github.event.inputs.connections }}
- Duration: ${{ github.event.inputs.duration }}s
- Ramp-up: ${{ github.event.inputs.rampUp }}s

**⚠️ WARNING: This will run load tests against PRODUCTION**

Please review and approve if this is intentional.

- name: Run load test
env:
RELAY_URL_DEV: ${{ secrets.RELAY_URL_DEV }}
RELAY_URL_UAT: ${{ secrets.RELAY_URL_UAT }}
RELAY_URL_PROD: ${{ secrets.RELAY_URL_PROD }}
run: |
cd apps/load-tests
yarn start \
--environment "${{ github.event.inputs.environment }}" \
--scenario "${{ github.event.inputs.scenario }}" \
--connections "${{ github.event.inputs.connections }}" \
--duration "${{ github.event.inputs.duration }}" \
--ramp-up "${{ github.event.inputs.rampUp }}" \
--output "results/load-test-$(date +%Y%m%d-%H%M%S).json"

- name: Upload results
if: always()
uses: actions/upload-artifact@v4
with:
name: load-test-results-${{ github.event.inputs.environment }}-${{ github.run_number }}
path: apps/load-tests/results/*.json
retention-days: 30

- name: Display results summary
if: always()
run: |
echo "## Load Test Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- Environment: ${{ github.event.inputs.environment }}" >> $GITHUB_STEP_SUMMARY
echo "- Scenario: ${{ github.event.inputs.scenario }}" >> $GITHUB_STEP_SUMMARY
echo "- Results uploaded as artifact" >> $GITHUB_STEP_SUMMARY
44 changes: 44 additions & 0 deletions apps/load-tests/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Dependencies
node_modules/
.yarn/

# Build outputs
dist/
*.tsbuildinfo

# Test results
results/
*.json
!package.json
!tsconfig.json

# Git
.git/
.gitignore

# IDE
.vscode/
.idea/
*.swp
*.swo

# Environment files
.env
.env.local
.env.*.local

# Documentation
README.md
*.md

# CI/CD
.github/

# Infrastructure state
.infrastructure/

# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
20 changes: 20 additions & 0 deletions apps/load-tests/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Load Test Runner Configuration
# ================================

# Target relay server URL (required for running tests)
# RELAY_URL=ws://localhost:8000/connection/websocket
# RELAY_URL=wss://mm-sdk-relay.api.cx.metamask.io/connection/websocket

# DigitalOcean Infrastructure Configuration
# ==========================================

# DigitalOcean API token (required for infra commands)
# Get this from: https://cloud.digitalocean.com/account/api/tokens
DIGITALOCEAN_TOKEN=

# SSH key fingerprint registered with DigitalOcean (required for infra commands)
# Find this in: https://cloud.digitalocean.com/account/security
SSH_KEY_FINGERPRINT=

# Path to SSH private key (optional, defaults to ~/.ssh/id_rsa)
# SSH_PRIVATE_KEY_PATH=~/.ssh/id_rsa
11 changes: 11 additions & 0 deletions apps/load-tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Environment files (except example)
.env
.env.local

# Results (except .gitkeep)
results/*
!results/.gitkeep

# Infrastructure state
results/.infra-state.json

Loading
Loading