Skip to content
158 changes: 158 additions & 0 deletions .github/actions/run-mock-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# Run Mock Server Action

A GitHub Actions composite action to start a mock server for testing Algorand API clients (algod, indexer, kmd).

## Description

This action sets up and starts a Bun-based mock server that simulates Algorand node APIs. It handles:

- Installing Bun runtime
- Installing mock-server dependencies
- Starting the server in the background
- Waiting for the server to be healthy
- Exporting environment variables for use in subsequent steps

## Inputs

| Input | Required | Default | Description |
| -------- | -------- | ------- | ------------------------------------------------------------------ |
| `client` | Yes | - | The client type to mock: `algod`, `indexer`, or `kmd` |
| `port` | No | \* | Port to run the server on |
| `ref` | No | `main` | Git ref to use when the action is called from external repos |

\* Default ports by client type:

- `algod`: 8000
- `kmd`: 8001
- `indexer`: 8002

## Outputs

This action exports the following environment variables:

| Client | Environment Variable | Example Value |
| --------- | --------------------- | ----------------------- |
| `algod` | `MOCK_ALGOD_URL` | `http://localhost:8000` |
| `indexer` | `MOCK_INDEXER_URL` | `http://localhost:8002` |
| `kmd` | `MOCK_KMD_URL` | `http://localhost:8001` |

## Usage

### Basic Usage (within the same repo)

```yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Start algod mock server
uses: ./.github/actions/run-mock-server
with:
client: algod

- name: Run tests
run: |
echo "Mock server URL: $MOCK_ALGOD_URL"
# Your test commands here
```

### Multiple Mock Servers

```yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Start algod mock server
uses: ./.github/actions/run-mock-server
with:
client: algod

- name: Start indexer mock server
uses: ./.github/actions/run-mock-server
with:
client: indexer

- name: Start kmd mock server
uses: ./.github/actions/run-mock-server
with:
client: kmd

- name: Run tests
run: |
echo "Algod URL: $MOCK_ALGOD_URL"
echo "Indexer URL: $MOCK_INDEXER_URL"
echo "KMD URL: $MOCK_KMD_URL"
```

### Custom Port

```yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Start algod mock server on custom port
uses: ./.github/actions/run-mock-server
with:
client: algod
port: 9000

- name: Run tests
run: |
# MOCK_ALGOD_URL will be http://localhost:9000
curl $MOCK_ALGOD_URL/health
```

### Usage from External Repository

```yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout algokit-polytest
uses: actions/checkout@v4
with:
repository: algorandfoundation/algokit-polytest
ref: main
path: algokit-polytest

- name: Start mock server
uses: ./algokit-polytest/.github/actions/run-mock-server
with:
client: algod
ref: main
```

## Requirements

- The mock-server must be located at `resources/mock-server/` relative to this action's parent repository
- The runner must support Bun installation (Linux and macOS runners are supported)

## Troubleshooting

### Server fails to start

Check the server logs which are written to `/tmp/mock-server-{client}.log`. The action will automatically display these logs if the health check fails.

### Port conflicts

If the default port is already in use, specify a custom port:

```yaml
- uses: ./.github/actions/run-mock-server
with:
client: algod
port: 9999
```

### Health check timeout

The action waits up to 30 seconds for the server to become healthy. If you need more time, consider checking your mock-server configuration or increasing server resources.
134 changes: 134 additions & 0 deletions .github/actions/run-mock-server/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
name: "Run Mock Server"
description: "Start a mock server for testing Algorand API clients (algod, indexer, kmd)"

inputs:
client:
description: "The client type to mock (algod, indexer, kmd)"
required: true
port:
description: "Port to run the server on (defaults: algod=8000, indexer=8002, kmd=8001)"
required: false
default: ""
ref:
description: "Git ref to use when action is called from external repos"
required: false
default: "main"

runs:
using: "composite"
steps:
- name: Determine port
id: port
shell: bash
run: |
set -Eeuo pipefail

CLIENT="${{ inputs.client }}"
PORT="${{ inputs.port }}"

# Validate client type
if [[ ! "$CLIENT" =~ ^(algod|indexer|kmd)$ ]]; then
echo "::error::Invalid client type '$CLIENT'. Must be one of: algod, indexer, kmd"
exit 1
fi

# Set default port based on client type if not provided
if [[ -z "$PORT" ]]; then
case "$CLIENT" in
algod) PORT=8000 ;;
indexer) PORT=8002 ;;
kmd) PORT=8001 ;;
esac
fi

# Validate port is a number
if [[ ! "$PORT" =~ ^[0-9]+$ ]]; then
echo "::error::Invalid port '$PORT'. Must be a number"
exit 1
fi

echo "port=$PORT" >> "$GITHUB_OUTPUT"
echo "Using port $PORT for $CLIENT mock server"

- name: Setup Bun
uses: oven-sh/setup-bun@v2

- name: Install mock-server dependencies
shell: bash
working-directory: ${{ github.action_path }}/../../../resources/mock-server
run: |
set -Eeuo pipefail
bun install --frozen-lockfile

- name: Start mock server
shell: bash
working-directory: ${{ github.action_path }}/../../../resources/mock-server
run: |
set -Eeuo pipefail

CLIENT="${{ inputs.client }}"
PORT="${{ steps.port.outputs.port }}"
CLIENT_UPPER=$(echo "$CLIENT" | tr '[:lower:]' '[:upper:]')

echo "Starting $CLIENT mock server on port $PORT..."

# Export port for the mock server to read
export "${CLIENT_UPPER}_PORT=$PORT"

# Start server in background with nohup
nohup bun bin/server.ts "$CLIENT" > /tmp/mock-server-${CLIENT}.log 2>&1 &
SERVER_PID=$!

echo "Mock server started with PID $SERVER_PID"
echo "MOCK_SERVER_PID=$SERVER_PID" >> "$GITHUB_ENV"

- name: Wait for health check
shell: bash
run: |
set -Eeuo pipefail

CLIENT="${{ inputs.client }}"
PORT="${{ steps.port.outputs.port }}"
HEALTH_URL="http://localhost:${PORT}/health"
MAX_RETRIES=30
RETRY_INTERVAL=1

echo "Waiting for $CLIENT mock server health check at $HEALTH_URL..."

for ((i=1; i<=MAX_RETRIES; i++)); do
# Use -s (silent) and check HTTP status code
# Accept ANY HTTP response (even 4xx/5xx) as server being ready
# The mock server returns 500 for unrecorded endpoints like /health
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 2 "$HEALTH_URL" 2>/dev/null || echo "000")

if [[ "$HTTP_CODE" != "000" ]]; then
echo "Mock server is responding (HTTP $HTTP_CODE) after $i seconds"
break
fi

if [[ $i -eq $MAX_RETRIES ]]; then
echo "::error::Health check failed after ${MAX_RETRIES} seconds"
echo "Server logs:"
cat /tmp/mock-server-${CLIENT}.log 2>/dev/null || echo "No logs available"
exit 1
fi

echo "Attempt $i/$MAX_RETRIES: Server not ready, retrying in ${RETRY_INTERVAL}s..."
sleep "$RETRY_INTERVAL"
done

- name: Export environment variables
shell: bash
run: |
set -Eeuo pipefail

CLIENT="${{ inputs.client }}"
PORT="${{ steps.port.outputs.port }}"
URL="http://localhost:${PORT}"

# Convert client to uppercase for env var name
CLIENT_UPPER=$(echo "$CLIENT" | tr '[:lower:]' '[:upper:]')
ENV_VAR_NAME="MOCK_${CLIENT_UPPER}_URL"

echo "${ENV_VAR_NAME}=${URL}" >> "$GITHUB_ENV"
echo "Exported $ENV_VAR_NAME=$URL"
17 changes: 17 additions & 0 deletions .github/actions/setup-polytest/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Setup Polytest
description: Install Rust and polytest CLI

inputs:
version:
description: Polytest version (empty for latest)
required: false
default: ""

runs:
using: composite
steps:
- uses: dtolnay/rust-toolchain@stable

- name: Install polytest
shell: bash
run: cargo install polytest --locked ${{ inputs.version && format('--version {0}', inputs.version) || '' }}
Loading