Skip to content

Commit 4716987

Browse files
feat: Adding Local node using Anvil (#594)
* chore: Add simple storage contract * chore: Create client for integration tests * chore: Add registry parsing * chore: Add registry * chore: Add mod file * chore: Add integration mod * feat: Add network tags and readiness validation * chore: Apply fmt * feat: Add network selection utils for integration tests * chore: Use openzeppelin_relayer package * feat: Create confirmation receipt helper * feat: Implement basic transfer * feat: Implement common api tests * fix: Resolve relayer test race conditions with cleanup and serialization * refactor: Clean up integration tests and extract EVM helpers * feat: Add integration testing Docker setup and reorganize contracts * chore: clean up broadcast logs and vendor dependencies in test fixtures * chore: remove git submodules for vendor dependencies * chore: Add integration test key * fix: Update .env.integration.example with defaults * chore: Moving TESTING.md file to README * fix: Ignoring signer files * fix: CodeRabbit suggestion * fix: CodeRabbit suggestion * fix: Improving logs for test cases * fix: Docker fixes * chore: Adding base sepolia * fix: Fixing CI * fix: Fixing test case * fix: Splitting tests in different jobs * fix: Fixing tests * fix: Fixing CI report * fix: Applying review suggestions * chore: Adding feature flags for running tests * chore: Adding job for integration tests * chore: Adding aws kms key logic * fix: Removing unnecessary test files * fix: Improvements * chore: Config files and readme improvements * chore: Fix integration test workflow * fix: Removing unused code * fix: Settings for connecting to aws from actions * fix: Fixing account id * fix: Fixing actions * fix: Fixing GH actions * fix: Fixing actions * fix: Fixing secrets * fix: Fixing GH secrets * fix: Fixing actions * fix: Fixing error * fix: Fixing docker compose * fix: API_KEY improvements * chore: Codecov fix * chore: Fix integratio test yaml file * fix: Improving registry logic to be branch agnostic * fix: Creating configs files * fix: Fixing workflow * fix: Fixing config example * fix: Re-organizing folder structure * fix: Fixing config files * fix: Removing tags and fixing workflow * feat: Adding basic tests for apis * fix: Trigger tests manually * feat: Adding local node with Anvil * fix: Improvements * chore: Renaming file * fix: Fixing integration test workflow * fix: Fix CI workflow * fix: Coderabbit suggestions * fix: Improvements * fix: Fix workflow * fix: Fix codecov * chore: Improving anvil local script * chore: README improvements * chore: README fix * chore: README fix * chore: Fix gitignore file * refactor: Reading active relayers from API * fix: Config files * fix: Docker optimisation * chore: Removing contract no longer needed * fix: Applying suggestions * fix: Fix workflow * fix: Fix readme about anvil pk * fix: Fix readme * fix: Improvements --------- Co-authored-by: Luis Urrutia <luis@urrutia.me>
1 parent 2915e69 commit 4716987

24 files changed

+948
-331
lines changed

.env.integration.example

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,38 @@
11
# Integration Test Environment Variables
22
# Copy this file to .env.integration and fill in your values
33

4+
# ==============================================================================
5+
# Test Mode Configuration
6+
# ==============================================================================
7+
8+
# MODE=local # Use Anvil (default, no testnet funds needed)
9+
# MODE=testnet # Use live testnets
10+
411
# ==============================================================================
512
# Required Variables
613
# ==============================================================================
714

815
# API key for the relayer service
16+
# Local mode: any value works (e.g., "local-test-key")
17+
# Testnet mode: valid API key required
918
API_KEY=your-api-key-here
1019

1120
# Keystore passphrase for local signer
21+
# Local mode: empty (Anvil account has no passphrase)
22+
# Testnet mode: your keystore passphrase (not needed if using KMS)
1223
KEYSTORE_PASSPHRASE=your-keystore-passphrase-here
1324

25+
# Use KMS signers instead of local keystore files
26+
# Set to "true" to skip keystore file checks in testnet mode
27+
# USE_KMS=true
28+
1429
# Webhook signing key (can be any UUID)
1530
WEBHOOK_SIGNING_KEY=your-webhook-signing-key-here
1631

1732
# ==============================================================================
1833
# Network Selection
1934
# ==============================================================================
20-
# Network selection is controlled via tests/integration/registry.json
35+
# Network selection is controlled via tests/integration/config/registry.json
2136
# Enable/disable networks by setting "enabled": true/false in registry.json
2237

2338
# ==============================================================================

.github/workflows/integration-tests.yaml

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,22 @@ jobs:
2828
- name: Create integration test config
2929
run: |
3030
cat > .env.integration << EOF
31+
MODE=testnet
3132
API_KEY=${{ secrets.INTEGRATION_TESTS_API_KEY }}
3233
LOG_LEVEL=info
34+
USE_KMS=true
3335
AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
3436
AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
3537
AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN}
3638
AWS_REGION=${{ vars.AWS_REGION_KMS_KEY }}
3739
EOF
38-
- name: Create config files from examples
40+
- name: Inject config files
41+
env:
42+
AWS_KMS_REGION: ${{ vars.AWS_REGION_KMS_KEY }}
43+
AWS_KMS_KEY_ID: ${{ secrets.TESTNET_AWS_KMS_KEY_ID }}
3944
run: |
40-
cp tests/integration/config/config.example.json tests/integration/config/config.json
41-
cp tests/integration/config/registry.example.json tests/integration/config/registry.json
42-
- name: Inject AWS KMS signer config
43-
run: |
44-
jq --arg region "${{ vars.AWS_REGION_KMS_KEY }}" \
45-
--arg key_id "${{ secrets.TESTNET_AWS_KMS_KEY_ID }}" \
46-
'.signers = [{"id": "local-signer", "type": "aws_kms", "config": {"region": $region, "key_id": $key_id}}]' \
47-
tests/integration/config/config.json > tests/integration/config/config.tmp.json && \
48-
mv tests/integration/config/config.tmp.json tests/integration/config/config.json
45+
mkdir -p tests/integration/config/testnet
46+
echo '${{ vars.INTEGRATION_CONFIG_JSON }}' | envsubst > tests/integration/config/testnet/config.json
47+
echo '${{ vars.INTEGRATION_REGISTRY_JSON }}' > tests/integration/config/testnet/registry.json
4948
- name: Run Integration Tests
5049
run: ./scripts/run-integration-docker.sh

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ tests/integration/config/keys/*.json
3030
tests/integration/config/config.json
3131
tests/integration/config/registry.json
3232

33+
# Local Anvil keystore
34+
tests/integration/config/local/keys/*.json
35+
!tests/integration/config/local/keys/.gitkeep
36+
37+
# Standalone config (auto-generated)
38+
tests/integration/config/local-standalone/
39+
40+
# Testnet configs
41+
tests/integration/config/testnet/
42+
3343
!examples/**/*.json
3444

3545

Dockerfile.integration

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,10 @@ WORKDIR /app
1919
# Copy manifests
2020
COPY Cargo.toml Cargo.lock ./
2121

22-
# Create dummy source to cache dependencies
23-
RUN mkdir -p src/bin tests/integration && \
24-
echo "fn main() {}" > src/bin/dummy.rs && \
25-
echo "#[test] fn dummy() {}" > tests/integration.rs
26-
27-
# Build dependencies (this layer will be cached)
22+
# Fetch dependencies (this layer will be cached)
2823
RUN --mount=type=cache,target=/usr/local/cargo/registry \
2924
--mount=type=cache,target=/usr/local/cargo/git \
30-
--mount=type=cache,target=/app/target \
31-
cargo build --features integration-tests --test integration --release 2>/dev/null || true && \
32-
rm -rf src tests
25+
cargo fetch --locked
3326

3427
# Copy actual source
3528
COPY src ./src

Makefile.toml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,46 @@ script = [
1616
"chmod +x ./scripts/docker_compose.sh",
1717
"./scripts/docker_compose.sh down"
1818
]
19+
20+
[tasks.integration-test-local]
21+
description = "Run integration tests against local standalone Anvil"
22+
env = { "TEST_REGISTRY_PATH" = "tests/integration/config/local-standalone/registry.json" }
23+
command = "cargo"
24+
args = ["test", "--features", "integration-tests", "--test", "integration"]
25+
26+
[tasks.integration-test-standalone]
27+
description = "Start Anvil if needed, check relayer, then run integration tests"
28+
script = '''
29+
#!/bin/bash
30+
set -e
31+
32+
# Check if Anvil is running, start if not
33+
if ! docker ps --format '{{.Names}}' | grep -q "^standalone-anvil$"; then
34+
echo "🔧 Anvil not running, starting it..."
35+
./scripts/anvil-local.sh start
36+
echo ""
37+
fi
38+
39+
# Load API_KEY from .env if it exists
40+
if [ -f .env ]; then
41+
export $(grep -v '^#' .env | grep API_KEY | xargs)
42+
fi
43+
44+
# Check if relayer is running (health endpoint)
45+
if ! curl -s -f -H "Authorization: Bearer ${API_KEY}" http://localhost:8080/api/v1/health > /dev/null 2>&1; then
46+
echo "❌ Relayer not running!"
47+
echo ""
48+
echo "Please start the relayer in another terminal:"
49+
echo " cargo run"
50+
echo ""
51+
echo "Then re-run: cargo make integration-test-standalone"
52+
exit 1
53+
fi
54+
55+
echo "✅ Anvil running, relayer healthy. Running tests..."
56+
echo ""
57+
58+
# Run tests
59+
TEST_REGISTRY_PATH=tests/integration/config/local-standalone/registry.json \
60+
cargo test --features integration-tests --test integration
61+
'''
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"networks": [
3+
{
4+
"type": "evm",
5+
"network": "localhost-anvil-docker",
6+
"chain_id": 31337,
7+
"required_confirmations": 1,
8+
"symbol": "ETH",
9+
"rpc_urls": ["http://anvil:8545"],
10+
"explorer_urls": [],
11+
"average_blocktime_ms": 1000,
12+
"is_testnet": true,
13+
"features": ["eip1559"]
14+
}
15+
]
16+
}

config/networks/local-anvil.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"networks": [
3+
{
4+
"type": "evm",
5+
"network": "localhost-anvil",
6+
"chain_id": 31337,
7+
"required_confirmations": 1,
8+
"symbol": "ETH",
9+
"rpc_urls": ["http://localhost:8545"],
10+
"explorer_urls": [],
11+
"average_blocktime_ms": 1000,
12+
"is_testnet": true,
13+
"features": ["eip1559"]
14+
}
15+
]
16+
}

docker-compose.integration.yml

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,33 @@ services:
1616
timeout: 2s
1717
retries: 5
1818
start_period: 5s
19+
anvil:
20+
profiles:
21+
- local
22+
image: ghcr.io/foundry-rs/foundry:stable
23+
container_name: integration-anvil
24+
entrypoint: ''
25+
command:
26+
- sh
27+
- -c
28+
- anvil --host 0.0.0.0 --chain-id 31337 --block-time 1
29+
ports:
30+
- 0.0.0.0:8545:8545
31+
networks:
32+
- integration
33+
volumes:
34+
- ./tests/integration/contracts:/contracts:ro
35+
healthcheck:
36+
test:
37+
- CMD
38+
- cast
39+
- client
40+
- --rpc-url
41+
- http://localhost:8545
42+
interval: 2s
43+
timeout: 2s
44+
retries: 10
45+
start_period: 3s
1946
relayer:
2047
build:
2148
context: .
@@ -24,6 +51,9 @@ services:
2451
depends_on:
2552
redis:
2653
condition: service_healthy
54+
anvil:
55+
condition: service_healthy
56+
required: false # Only when local profile is active, but we handle startup order in script
2757
ports:
2858
- 8080:8080
2959
networks:
@@ -37,7 +67,7 @@ services:
3767
# This filters out verbose OpenTelemetry traces
3868
- RUST_LOG=warn,openzeppelin_relayer=warn,actix_web=warn,tracing_actix_web=error
3969
volumes:
40-
- ./tests/integration/config:/app/config:ro
70+
- ${CONFIG_SOURCE:-./tests/integration/config/local}:/app/config:ro
4171
- ./config/networks:/app/networks:ro
4272
healthcheck:
4373
test:
@@ -66,6 +96,8 @@ services:
6696
# Test logging configuration
6797
# Default to INFO level for clean output, can be overridden with RUST_LOG
6898
- RUST_LOG=${RUST_LOG:-info}
99+
# Test registry path (tests discover relayers via API)
100+
- TEST_REGISTRY_PATH=${TEST_REGISTRY_PATH:-tests/integration/config/registry.json}
69101
volumes:
70102
- ./tests/integration/config:/app/config:ro
71103
- ./config/networks:/app/networks:ro

0 commit comments

Comments
 (0)