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
2 changes: 1 addition & 1 deletion .config/nextest.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[profile.ci-workflow]
default-filter = 'not package(contract-history) and not test(=test_upload_quote_for_collateral_with_phala_endpoint)'
default-filter = 'not package(contract-history) and not package(e2e-tests) and not test(=test_upload_quote_for_collateral_with_phala_endpoint)'
52 changes: 52 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,58 @@ jobs:
- name: Run cargo-nextest
run: cargo nextest run --cargo-profile=test-release --all-features --locked --profile=ci-workflow

mpc-e2e-tests:
name: "Rust E2E tests"
runs-on: warp-ubuntu-2404-x64-16x
timeout-minutes: 60
permissions:
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false

- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y liblzma-dev

- name: Cache Rust dependencies
uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
cache-provider: "warpbuild"

- name: Install cargo-nextest
uses: taiki-e/install-action@d4422f254e595ee762a758628fe4f16ce050fa2e # v2.67.28
with:
tool: nextest@0.9.126

- name: Install cargo-binstall
uses: taiki-e/install-action@d4422f254e595ee762a758628fe4f16ce050fa2e # v2.67.28
with:
tool: cargo-binstall

- name: Install wasm-opt
run: |
cargo binstall --force --no-confirm --locked wasm-opt@0.116.1
echo "${HOME}/.cargo/bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Build mpc-node binary
run: cargo build -p mpc-node --release --locked --features test-utils

- name: Build contract WASM
run: |
cargo build -p mpc-contract --target=wasm32-unknown-unknown --profile=release-contract --locked
wasm-opt -Oz --enable-bulk-memory target/wasm32-unknown-unknown/release-contract/mpc_contract.wasm -o target/wasm32-unknown-unknown/release-contract/mpc_contract.wasm

- name: Run E2E tests
run: cargo nextest run -p e2e-tests --cargo-profile=test-release --locked

mpc-pytest-build:
name: "MPC pytests: build"
runs-on: warp-ubuntu-2404-x64-16x
Expand Down
22 changes: 22 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ members = [
"crates/contract",
"crates/contract-history",
"crates/devnet",
"crates/e2e-tests",
"crates/foreign-chain-inspector",
"crates/foreign-chain-rpc-interfaces",
"crates/include-measurements",
Expand Down Expand Up @@ -146,6 +147,7 @@ near-account-id = "2.5.0"
near-jsonrpc-client = "0.20.0"
near-jsonrpc-primitives = "0.34.6"
near-primitives = "0.34.6"
near-sandbox = "0.3.5"
near-sdk = { version = "5.24.1", features = [
"legacy",
"unit-testing",
Expand Down
25 changes: 25 additions & 0 deletions crates/e2e-tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "e2e-tests"
edition.workspace = true
version.workspace = true
license.workspace = true

[dependencies]
anyhow.workspace = true
bs58.workspace = true
ed25519-dalek.workspace = true
hex.workspace = true
near-mpc-contract-interface = { workspace = true }
near-sandbox = { workspace = true }
near-workspaces = { workspace = true, features = ["unstable"] }
rand.workspace = true
serde = { workspace = true }
serde_json.workspace = true
tempfile.workspace = true
tokio.workspace = true
toml.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true

[lints]
workspace = true
80 changes: 80 additions & 0 deletions crates/e2e-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# E2E Test Infrastructure

Rust E2E test framework replacing the Python pytest system tests. Spawns real
`mpc-node` OS processes against a local NEAR sandbox, exercising the full
binary including config parsing, P2P networking, built-in NEAR indexer, and
Prometheus metrics.

## Architecture

```
MpcCluster
|-- SandboxNode near-sandbox process with controlled ports
|-- NearBlockchain RPC client (near-workspaces) for contract interaction
|-- Vec<MpcNode> N mpc-node OS processes, each with its own neard indexer
```

- **SandboxNode** starts a `near-sandbox` neard validator with deterministic ports.
- **NearBlockchain** is a pure RPC client wrapping `near-workspaces`. It deploys
the MPC contract, creates accounts, submits transactions, and queries state.
Environment-agnostic -- can target sandbox or testnet.
- **MpcNode** manages a single `mpc-node` binary. Generates a `start_config.toml`
pointing the node's built-in NEAR indexer at the sandbox validator via
`boot_nodes`. Each node runs its own neard process internally, peering with the
sandbox validator over P2P.
- **MpcCluster** orchestrates everything: starts sandbox, deploys contract, creates
accounts, starts N nodes, initializes the contract, adds signature domains, and
waits for the Running state.

## Port Allocation

Each test gets a unique `test_id`. All ports are computed deterministically:

```
BASE_PORT (20000) + test_id * 82 + offset
```

Per test: 2 cluster-level ports (sandbox RPC, sandbox network) + 8 ports per
node (P2P, web UI, migration UI, pprof, near RPC, near network, 2 reserved)
times up to 10 nodes = 82 ports total.

This allows `cargo nextest` to run tests in parallel without port collisions.

## Running Tests

```bash
# Prerequisites: build mpc-node binary and contract WASM
cargo build -p mpc-node --release --features test-utils
cargo near build non-reproducible-wasm \
--manifest-path crates/contract/Cargo.toml --locked

# Run E2E tests
cargo nextest run -p e2e-tests --cargo-profile=test-release
```

## Adding a New Test

1. Create `tests/my_test.rs`
2. Use a unique `E2ePortAllocator::new(<unique_id>)` to avoid port collisions
3. Build an `MpcCluster` with your desired configuration
4. Use cluster methods to interact with the contract and assert behavior

```rust
#[tokio::test(flavor = "multi_thread")]
async fn test_my_scenario() -> anyhow::Result<()> {
let cluster = MpcCluster::start(ClusterConfig {
num_nodes: 3,
threshold: 2,
port_allocator: E2ePortAllocator::new(42),
..ClusterConfig::default()
}).await?;

// ... test logic ...
Ok(())
}
```

## Design Reference

See `docs/pytest-deprecation.md` (PR #2446) for the full design document
describing the migration from Python to Rust E2E tests.
Loading
Loading