Skip to content

Commit 478c222

Browse files
authored
ci: adds reusable setup polytest and mock server actions, minor scripts and docs refinements (#18)
* chore: cd for image publishing * chore: test (#1) * fix: handles binary msgpack responses correctly Ensures correct handling of binary msgpack responses by using `arrayBuffer()` instead of `text()` to prevent UTF-8 corruption. Also adds logging for content type and binary status, and adjusts the response preview logic to only apply to non msgpack data. * chore: cd for image publishing * feat: add shared setup-polytest gh action * chore: Update test_configs/kmd_client.jsonc * chore: update .github/workflows/publish-docker.yml * refactor: adds run-mock-server GitHub Action Introduces a composite GitHub Action to simplify starting mock servers for testing Algorand API clients (algod, indexer, kmd). This action handles installing Bun, mock-server dependencies, starting the server in the background, waiting for the server to be healthy, and exporting environment variables for use in subsequent steps. It uses the mock-server located in the `resources/mock-server/` directory of this repo and makes it available as a reusable action. * chore: tweak health check * docs: tweak docs * chore: copilot comments
1 parent 5fab6e9 commit 478c222

File tree

8 files changed

+496
-29
lines changed

8 files changed

+496
-29
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Run Mock Server Action
2+
3+
A GitHub Actions composite action to start mock servers for testing Algorand API clients (algod, indexer, kmd).
4+
5+
## Inputs
6+
7+
| Input | Required | Default | Description |
8+
| -------- | -------- | ------- | ----------------------------------------------------- |
9+
| `client` | Yes | - | The client type to mock: `algod`, `indexer`, or `kmd` |
10+
| `port` | No | * | Port to run the server on |
11+
12+
\* Default ports: `algod`: 8000, `kmd`: 8001, `indexer`: 8002
13+
14+
## Outputs
15+
16+
Environment variables exported for use in subsequent steps:
17+
18+
| Client | Environment Variable | Default Value |
19+
| --------- | -------------------- | ----------------------- |
20+
| `algod` | `MOCK_ALGOD_URL` | `http://localhost:8000` |
21+
| `kmd` | `MOCK_KMD_URL` | `http://localhost:8001` |
22+
| `indexer` | `MOCK_INDEXER_URL` | `http://localhost:8002` |
23+
24+
## Usage
25+
26+
```yaml
27+
- name: Start algod mock server
28+
uses: algorandfoundation/algokit-polytest/.github/actions/run-mock-server@main
29+
with:
30+
client: algod
31+
32+
- name: Start kmd mock server
33+
uses: algorandfoundation/algokit-polytest/.github/actions/run-mock-server@main
34+
with:
35+
client: kmd
36+
37+
- name: Start indexer mock server
38+
uses: algorandfoundation/algokit-polytest/.github/actions/run-mock-server@main
39+
with:
40+
client: indexer
41+
42+
- name: Run tests
43+
run: npm test # Uses $MOCK_ALGOD_URL, $MOCK_KMD_URL, $MOCK_INDEXER_URL
44+
```
45+
46+
## Troubleshooting
47+
48+
- **Server logs**: `/tmp/mock-server-{client}.log`
49+
- **Custom port**: Add `port: 9000` to the `with` block
50+
- **Health check timeout**: Action waits up to 30 seconds for server readiness
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
name: "Run Mock Server"
2+
description: "Start a mock server for testing Algorand API clients (algod, indexer, kmd)"
3+
4+
inputs:
5+
client:
6+
description: "The client type to mock (algod, indexer, kmd)"
7+
required: true
8+
port:
9+
description: "Port to run the server on (defaults: algod=8000, indexer=8002, kmd=8001)"
10+
required: false
11+
default: ""
12+
13+
runs:
14+
using: "composite"
15+
steps:
16+
- name: Determine port
17+
id: port
18+
shell: bash
19+
run: |
20+
set -euo pipefail
21+
22+
CLIENT="${{ inputs.client }}"
23+
PORT="${{ inputs.port }}"
24+
25+
# Validate client type
26+
if [[ ! "$CLIENT" =~ ^(algod|indexer|kmd)$ ]]; then
27+
echo "::error::Invalid client type '$CLIENT'. Must be one of: algod, indexer, kmd"
28+
exit 1
29+
fi
30+
31+
# Set default port based on client type if not provided
32+
if [[ -z "$PORT" ]]; then
33+
case "$CLIENT" in
34+
algod) PORT=8000 ;;
35+
indexer) PORT=8002 ;;
36+
kmd) PORT=8001 ;;
37+
esac
38+
fi
39+
40+
# Validate port is a number
41+
if [[ ! "$PORT" =~ ^[0-9]+$ ]]; then
42+
echo "::error::Invalid port '$PORT'. Must be a number"
43+
exit 1
44+
fi
45+
46+
echo "port=$PORT" >> "$GITHUB_OUTPUT"
47+
echo "Using port $PORT for $CLIENT mock server"
48+
49+
- name: Setup Bun
50+
uses: oven-sh/setup-bun@v2
51+
52+
- name: Install mock-server dependencies
53+
shell: bash
54+
working-directory: ${{ github.action_path }}/../../../resources/mock-server
55+
run: |
56+
set -euo pipefail
57+
bun install --frozen-lockfile
58+
59+
- name: Start mock server
60+
shell: bash
61+
working-directory: ${{ github.action_path }}/../../../resources/mock-server
62+
run: |
63+
set -euo pipefail
64+
65+
CLIENT="${{ inputs.client }}"
66+
PORT="${{ steps.port.outputs.port }}"
67+
CLIENT_UPPER=$(echo "$CLIENT" | tr '[:lower:]' '[:upper:]')
68+
69+
echo "Starting $CLIENT mock server on port $PORT..."
70+
71+
# Export port for the mock server to read
72+
export "${CLIENT_UPPER}_PORT=$PORT"
73+
74+
# Start server in background with nohup
75+
nohup bun bin/server.ts "$CLIENT" > /tmp/mock-server-${CLIENT}.log 2>&1 &
76+
SERVER_PID=$!
77+
78+
echo "Mock server started with PID $SERVER_PID"
79+
echo "MOCK_SERVER_PID=$SERVER_PID" >> "$GITHUB_ENV"
80+
81+
- name: Wait for health check
82+
shell: bash
83+
run: |
84+
set -euo pipefail
85+
86+
CLIENT="${{ inputs.client }}"
87+
PORT="${{ steps.port.outputs.port }}"
88+
HEALTH_URL="http://localhost:${PORT}/health"
89+
MAX_RETRIES=30
90+
RETRY_INTERVAL=1
91+
92+
echo "Waiting for $CLIENT mock server health check at $HEALTH_URL..."
93+
94+
for ((i=1; i<=MAX_RETRIES; i++)); do
95+
# Use -s (silent) and check HTTP status code
96+
# Accept ANY HTTP response (even 4xx/5xx) as server being ready
97+
# The mock server returns 500 for unrecorded endpoints like /health
98+
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 2 "$HEALTH_URL" 2>/dev/null || echo "000")
99+
100+
if [[ "$HTTP_CODE" != "000" ]]; then
101+
echo "Mock server is responding (HTTP $HTTP_CODE) after $i seconds"
102+
break
103+
fi
104+
105+
if [[ $i -eq $MAX_RETRIES ]]; then
106+
echo "::error::Health check failed after ${MAX_RETRIES} seconds"
107+
echo "Server logs:"
108+
cat /tmp/mock-server-${CLIENT}.log 2>/dev/null || echo "No logs available"
109+
exit 1
110+
fi
111+
112+
echo "Attempt $i/$MAX_RETRIES: Server not ready, retrying in ${RETRY_INTERVAL}s..."
113+
sleep "$RETRY_INTERVAL"
114+
done
115+
116+
- name: Export environment variables
117+
shell: bash
118+
run: |
119+
set -euo pipefail
120+
121+
CLIENT="${{ inputs.client }}"
122+
PORT="${{ steps.port.outputs.port }}"
123+
URL="http://localhost:${PORT}"
124+
125+
# Convert client to uppercase for env var name
126+
CLIENT_UPPER=$(echo "$CLIENT" | tr '[:lower:]' '[:upper:]')
127+
ENV_VAR_NAME="MOCK_${CLIENT_UPPER}_URL"
128+
129+
echo "${ENV_VAR_NAME}=${URL}" >> "$GITHUB_ENV"
130+
echo "Exported $ENV_VAR_NAME=$URL"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Setup Polytest
2+
description: Install Rust and polytest CLI
3+
4+
inputs:
5+
version:
6+
description: Polytest version (empty for latest)
7+
required: false
8+
default: ""
9+
10+
runs:
11+
using: composite
12+
steps:
13+
- uses: dtolnay/rust-toolchain@stable
14+
15+
- name: Install polytest
16+
shell: bash
17+
run: cargo install polytest --locked ${{ inputs.version && format('--version {0}', inputs.version) || '' }}

README.md

Lines changed: 94 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,102 @@
11
# AlgoKit Polytest
22

3-
This repos contains the polytest version control and configuration files for testing AlgoKit libraries.
3+
Test configuration files and mock server infrastructure for cross-language testing of AlgoKit libraries.
44

5-
## Usage
5+
## Overview
66

7-
Documentation on the recommended workflow can be found in the polytest repo [here](https://github.com/joe-p/polytest/blob/main/docs/multi_repo.md)
7+
This repository provides:
88

9-
An example of integration with algokit-core can be seen in this draft PR: https://github.com/algorandfoundation/algokit-core/pull/285
9+
- **Test Configurations** (`test_configs/`) - Shared test plans for polytest CLI, ensuring consistent test coverage across Python, TypeScript, and other language implementations
10+
- **Mock Server** (`resources/mock-server/`) - Bun-based HTTP server that replays pre-recorded HAR files for deterministic API testing
11+
- **GitHub Actions** (`.github/actions/`) - Reusable composite actions for CI integration
1012

11-
In summary, the implementation repos must use `polytest` with the `--git` flag:
13+
## Directory Structure
1214

13-
```toml
14-
[tool.poe.tasks]
15-
polytest = "polytest --config test_configs/transact.json --git https://github.com/joe-p/algokit-polytest#main run -t pytest"
1615
```
16+
├── .github/
17+
│ └── actions/
18+
│ ├── setup-polytest/ # Install polytest CLI
19+
│ └── run-mock-server/ # Start mock server for testing
20+
├── resources/
21+
│ └── mock-server/ # Bun/Fastify mock server
22+
│ ├── recordings/ # HAR files for algod, indexer, kmd
23+
│ └── src/ # Server implementation
24+
├── test_configs/ # Polytest configuration files
25+
│ ├── algod_client.jsonc
26+
│ ├── indexer_client.jsonc
27+
│ ├── kmd_client.jsonc
28+
│ └── transact.jsonc
29+
└── docs/ # Generated test plan documentation
30+
```
31+
32+
## GitHub Actions
33+
34+
### `setup-polytest`
35+
36+
Installs Rust toolchain and the polytest CLI.
37+
38+
```yaml
39+
- uses: algorandfoundation/algokit-polytest/.github/actions/setup-polytest@main
40+
with:
41+
version: "0.6.0" # Optional: pin to specific version
42+
```
43+
44+
### `run-mock-server`
45+
46+
Starts a mock server for testing Algorand API clients.
47+
48+
```yaml
49+
- uses: algorandfoundation/algokit-polytest/.github/actions/run-mock-server@main
50+
with:
51+
client: algod # Required: algod, indexer, or kmd
52+
53+
# After this step, MOCK_ALGOD_URL, MOCK_INDEXER_URL, MOCK_KMD_URL is available (e.g., http://localhost:8000)
54+
```
55+
56+
See [run-mock-server README](.github/actions/run-mock-server/README.md) for full documentation.
57+
58+
## Local Development
59+
60+
### Prerequisites
61+
62+
- [Bun](https://bun.sh/) runtime: `curl -fsSL https://bun.sh/install | bash`
63+
64+
### Running the Mock Server
65+
66+
**Start all servers (recommended):**
67+
68+
```bash
69+
cd resources/mock-server
70+
./scripts/start_all_servers.sh
71+
```
72+
73+
This starts algod (port 8000), kmd (port 8001), and indexer (port 8002) in the background and outputs the environment variables to set.
74+
75+
**Start a single server:**
76+
77+
```bash
78+
cd resources/mock-server
79+
./scripts/start_server.sh algod # Port 8000
80+
./scripts/start_server.sh kmd # Port 8001
81+
./scripts/start_server.sh indexer # Port 8002
82+
```
83+
84+
**Stop all servers:**
85+
86+
```bash
87+
cd resources/mock-server
88+
./scripts/stop_all_servers.sh
89+
```
90+
91+
See [mock-server README](resources/mock-server/README.md) for more details.
92+
93+
### Recording New HAR Files
94+
95+
Edit `resources/mock-server/src/record.ts` to add new requests, then restart the server. It will record any missing requests to the HAR files.
96+
97+
## Integration with Implementation Repos
98+
99+
Implementation repositories (e.g., `algokit-utils-py`, `algokit-utils-ts`) use this repo via:
100+
101+
1. **Test Generation**: polytest CLI with `--git` flag pulls configs from this repo
102+
2. **Mock Server**: GitHub Action starts the mock server in CI

0 commit comments

Comments
 (0)