From 86ad4b22da1cf22b26e19352fba3bd318c75207b Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:52:15 -0700 Subject: [PATCH] ci: separate tests that require network and run localnet in ci (#38) --- .github/workflows/ci.yml | 54 ++++ Makefile | 6 +- crates/sui-graphql-client/README.md | 6 +- crates/sui-graphql-client/src/lib.rs | 380 ++++++++++++++------------- 4 files changed, 253 insertions(+), 193 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a8f21bf4..5a4035494 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,3 +62,57 @@ jobs: - name: Run tests in wasm run: make wasm + + + run_tests_with_network: + runs-on: ubuntu-latest + env: + EPOCH_DURATION_MS: 10000 + services: + postgres: # we need this postgres instance for running a local network with indexer and graphql + image: postgres + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgrespw + POSTGRES_DB: sui_indexer_v2 + POSTGRES_HOST_AUTH_METHOD: trust + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: rust version + run: | + rustc --version + cargo --version + + - uses: taiki-e/install-action@cargo-nextest + + - name: Get the Sui testnet binary and start a local network + shell: bash + env: + SUI_BINARY_VERSION: "1.35.1" # used for downloading a specific Sui binary versions that matches the GraphQL schema for local network tests + SUI_NETWORK_RELEASE: "testnet" # which release to use + run: | + ASSET_NAME="sui-$SUI_NETWORK_RELEASE-v$SUI_BINARY_VERSION-ubuntu-x86_64.tgz" + download_url="https://github.com/mystenlabs/sui/releases/download/$SUI_NETWORK_RELEASE-v$SUI_BINARY_VERSION/$ASSET_NAME" + + echo "Downloading testnet binary from $download_url" + wget -q $download_url -O sui.tgz + tar -zxvf sui.tgz ./sui + echo "Starting local network with a faucet, an indexer (port 5432) and GraphQL. Epoch duration is set to $EPOCH_DURATION_MS ms" + ./sui start --force-regenesis --with-faucet --with-indexer --with-graphql --pg-port 5432 --pg-db-name sui_indexer_v2 --epoch-duration-ms $EPOCH_DURATION_MS & + + - name: Run tests that require local network (GraphQL Client and Tx Builder) + env: + NETWORK: "local" # other expected options are mainnet, testnet, or devnet, or an actual URL to a GraphQL server: http://localhost:port + run: | + sleep $((EPOCH_DURATION_MS / 1000)) # wait for the network to get to epoch #2 + make test-with-localnet + diff --git a/Makefile b/Makefile index 9d9ef9b3c..a7dad8cf0 100644 --- a/Makefile +++ b/Makefile @@ -21,9 +21,13 @@ clippy: .PHONY: test test: - cargo nextest run --all-features + cargo nextest run --all-features -p sui-sdk-types -p sui-crypto cargo test --doc +.PHONY: test-with-localnet +test-with-localnet: + cargo nextest run -p sui-graphql-client + .PHONY: wasm wasm: $(MAKE) -C crates/sui-sdk-types wasm diff --git a/crates/sui-graphql-client/README.md b/crates/sui-graphql-client/README.md index a8840d0b7..9901fb414 100644 --- a/crates/sui-graphql-client/README.md +++ b/crates/sui-graphql-client/README.md @@ -20,7 +20,7 @@ executing transactions and more. ## Connecting to a GraphQL server Instantiate a client with [`Client::new(server: &str)`] or use one of the predefined functions for different networks [`Client`]. -```rust +```rust, no_run use sui_graphql_client::Client; use anyhow::Result; @@ -118,7 +118,7 @@ The generated query types are defined below. Note that the `id` variable is opti Note that instead of using `Uint53`, the scalar is mapped to `u64` in the library using `impl_scalar(u64, schema::Uint53)`, thus all references to `Uint53` in the schema are replaced with `u64` in the code below. -```rust,ignore +```rust, ignore #[derive(cynic::QueryVariables, Debug)] pub struct CustomQueryVariables { pub id: Option, @@ -145,7 +145,7 @@ pub struct BigInt(pub String); ``` The complete example is shown below: -```rust +```rust, ignore use anyhow::Result; use cynic::QueryBuilder; diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 8a53c97fe..d11b7ae43 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -929,6 +929,7 @@ impl Client { } } +// This function is used in tests to create a new client instance for the local server. #[cfg(test)] mod tests { use futures::StreamExt; @@ -939,7 +940,17 @@ mod tests { use crate::LOCAL_HOST; use crate::MAINNET_HOST; use crate::TESTNET_HOST; - const NETWORKS: [(&str, &str); 2] = [(MAINNET_HOST, "35834a8a"), (TESTNET_HOST, "4c78adac")]; + + fn test_client() -> Client { + let network = std::env::var("NETWORK").unwrap_or_else(|_| "local".to_string()); + match network.as_str() { + "mainnet" => Client::new_mainnet(), + "testnet" => Client::new_testnet(), + "devnet" => Client::new_devnet(), + "local" => Client::new_localhost(), + _ => Client::new(&network).expect("Invalid network URL: {network}"), + } + } #[test] fn test_rpc_server() { @@ -958,243 +969,235 @@ mod tests { #[tokio::test] async fn test_balance_query() { - for (n, _) in NETWORKS.iter() { - let client = Client::new(n).unwrap(); - let balance = client.balance("0x1".parse().unwrap(), None).await; - assert!(balance.is_ok(), "Balance query failed for network: {n}"); - } + let client = test_client(); + let balance = client.balance("0x1".parse().unwrap(), None).await; + assert!( + balance.is_ok(), + "Balance query failed for {} network", + client.rpc_server() + ); } #[tokio::test] async fn test_chain_id() { - for (n, id) in NETWORKS.iter() { - let client = Client::new(n).unwrap(); - let chain_id = client.chain_id().await; - assert!(chain_id.is_ok()); - assert_eq!(&chain_id.unwrap(), id); - } + let client = test_client(); + let chain_id = client.chain_id().await; + assert!(chain_id.is_ok()); } #[tokio::test] async fn test_reference_gas_price_query() { - for (n, _) in NETWORKS.iter() { - let client = Client::new(n).unwrap(); - let rgp = client.reference_gas_price(None).await; - assert!( - rgp.is_ok(), - "Reference gas price query failed for network: {n}" - ); - } + let client = test_client(); + let rgp = client.reference_gas_price(None).await; + assert!( + rgp.is_ok(), + "Reference gas price query failed for {} network", + client.rpc_server() + ); } #[tokio::test] async fn test_protocol_config_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let pc = client.protocol_config(None).await; - assert!(pc.is_ok()); - - // test specific version - let pc = client.protocol_config(Some(50)).await; - assert!(pc.is_ok()); - let pc = pc.unwrap(); - if let Some(pc) = pc { - assert_eq!( - pc.protocol_version, 50, - "Protocol version query mismatch for network: {n}. Expected: 50, received: {}", - pc.protocol_version - ); - } + let client = test_client(); + let pc = client.protocol_config(None).await; + assert!(pc.is_ok()); + + // test specific version + let pc = client.protocol_config(Some(50)).await; + assert!(pc.is_ok()); + let pc = pc.unwrap(); + if let Some(pc) = pc { + assert_eq!( + pc.protocol_version, + 50, + "Protocol version query mismatch for {} network. Expected: 50, received: {}", + client.rpc_server(), + pc.protocol_version + ); } } #[tokio::test] async fn test_service_config_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let sc = client.service_config().await; - assert!(sc.is_ok(), "Service config query failed for network: {n}"); - } + let client = test_client(); + let sc = client.service_config().await; + assert!( + sc.is_ok(), + "Service config query failed for {} network", + client.rpc_server() + ); } #[tokio::test] async fn test_active_validators() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let av = client - .active_validators(None, PaginationFilter::default()) - .await; - assert!( - av.is_ok(), - "Active validators query failed for network: {n}. Error: {}", - av.unwrap_err() - ); + let client = test_client(); + let av = client + .active_validators(None, PaginationFilter::default()) + .await; + assert!( + av.is_ok(), + "Active validators query failed for {} network. Error: {}", + client.rpc_server(), + av.unwrap_err() + ); - assert!( - !av.unwrap().is_empty(), - "Active validators query returned None for network: {n}" - ); - } + assert!( + !av.unwrap().is_empty(), + "Active validators query returned None for {} network", + client.rpc_server() + ); } #[tokio::test] async fn test_coin_metadata_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let cm = client.coin_metadata("0x2::sui::SUI").await; - assert!(cm.is_ok(), "Coin metadata query failed for network: {n}"); - } + let client = test_client(); + let cm = client.coin_metadata("0x2::sui::SUI").await; + assert!( + cm.is_ok(), + "Coin metadata query failed for {} network", + client.rpc_server() + ); } #[tokio::test] async fn test_checkpoint_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let c = client.checkpoint(None, None).await; - assert!( - c.is_ok(), - "Checkpoint query failed for network: {n}. Error: {}", - c.unwrap_err() - ); - } + let client = test_client(); + let c = client.checkpoint(None, None).await; + assert!( + c.is_ok(), + "Checkpoint query failed for {} network. Error: {}", + client.rpc_server(), + c.unwrap_err() + ); } #[tokio::test] async fn test_checkpoints_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let c = client.checkpoints(PaginationFilter::default()).await; - assert!( - c.is_ok(), - "Checkpoints query failed for network: {n}. Error: {}", - c.unwrap_err() - ); - } + let client = test_client(); + let c = client.checkpoints(PaginationFilter::default()).await; + assert!( + c.is_ok(), + "Checkpoints query failed for {} network. Error: {}", + client.rpc_server(), + c.unwrap_err() + ); } #[tokio::test] async fn test_latest_checkpoint_sequence_number_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let last_checkpoint = client.latest_checkpoint_sequence_number().await; - assert!( - last_checkpoint.is_ok(), - "Latest checkpoint sequence number query failed for network: {n}. Error: {}", - last_checkpoint.unwrap_err() - ); - } + let client = test_client(); + let last_checkpoint = client.latest_checkpoint_sequence_number().await; + assert!( + last_checkpoint.is_ok(), + "Latest checkpoint sequence number query failed for {} network. Error: {}", + client.rpc_server(), + last_checkpoint.unwrap_err() + ); } #[tokio::test] async fn test_epoch_total_checkpoints_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let e = client.epoch_total_checkpoints(None).await; - assert!( - e.is_ok(), - "Epoch total checkpoints query failed for network: {n}. Error: {}", - e.unwrap_err() - ); - } + let client = test_client(); + let e = client.epoch_total_checkpoints(None).await; + assert!( + e.is_ok(), + "Epoch total checkpoints query failed for {} network. Error: {}", + client.rpc_server(), + e.unwrap_err() + ); } #[tokio::test] async fn test_epoch_total_transaction_blocks_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let e = client.epoch_total_transaction_blocks(None).await; - assert!( - e.is_ok(), - "Epoch total transaction blocks query failed for network: {n}. Error: {}", - e.unwrap_err() - ); - } + let client = test_client(); + let e = client.epoch_total_transaction_blocks(None).await; + assert!( + e.is_ok(), + "Epoch total transaction blocks query failed for {} network. Error: {}", + client.rpc_server(), + e.unwrap_err() + ); } #[tokio::test] async fn test_epoch_summary_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let e = client.epoch_summary(None).await; - assert!( - e.is_ok(), - "Epoch summary query failed for network: {n}. Error: {}", - e.unwrap_err() - ); - } + let client = test_client(); + let e = client.epoch_summary(None).await; + assert!( + e.is_ok(), + "Epoch summary query failed for {} network. Error: {}", + client.rpc_server(), + e.unwrap_err() + ); } #[tokio::test] #[ignore] // schema was updated, but the service has not been released with the new schema async fn test_events_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let events = client.events(None, PaginationFilter::default()).await; - assert!( - events.is_ok(), - "Events query failed for network: {n}. Error: {}", - events.unwrap_err() - ); - assert!( - !events.unwrap().is_empty(), - "Events query returned no data for network: {n}" - ); - } + let client = test_client(); + let events = client.events(None, PaginationFilter::default()).await; + assert!( + events.is_ok(), + "Events query failed for {} network. Error: {}", + client.rpc_server(), + events.unwrap_err() + ); + assert!( + !events.unwrap().is_empty(), + "Events query returned no data for {} network", + client.rpc_server() + ); } #[tokio::test] #[ignore] async fn test_objects_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let objects = client.objects(None, PaginationFilter::default()).await; - assert!( - objects.is_ok(), - "Objects query failed for network: {n}. Error: {}", - objects.unwrap_err() - ); - } + let client = test_client(); + let objects = client.objects(None, PaginationFilter::default()).await; + assert!( + objects.is_ok(), + "Objects query failed for {} network. Error: {}", + client.rpc_server(), + objects.unwrap_err() + ); } #[tokio::test] #[ignore] async fn test_object_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let object = client.object("0x5".parse().unwrap(), None).await; - assert!( - object.is_ok(), - "Object query failed for network: {n}. Error: {}", - object.unwrap_err() - ); - } + let client = test_client(); + let object = client.object("0x5".parse().unwrap(), None).await; + assert!( + object.is_ok(), + "Object query failed for {} network. Error: {}", + client.rpc_server(), + object.unwrap_err() + ); } #[tokio::test] async fn test_object_bcs_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let object_bcs = client.object_bcs("0x5".parse().unwrap()).await; - assert!( - object_bcs.is_ok(), - "Object bcs query failed for network: {n}. Error: {}", - object_bcs.unwrap_err() - ); - } + let client = test_client(); + let object_bcs = client.object_bcs("0x5".parse().unwrap()).await; + assert!( + object_bcs.is_ok(), + "Object bcs query failed for {} network. Error: {}", + client.rpc_server(), + object_bcs.unwrap_err() + ); } #[tokio::test] async fn test_coins_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let coins = client - .coins("0x1".parse().unwrap(), None, PaginationFilter::default()) - .await; - assert!( - coins.is_ok(), - "Coins query failed for network: {n}. Error: {}", - coins.unwrap_err() - ); - } + let client = test_client(); + let coins = client + .coins("0x1".parse().unwrap(), None, PaginationFilter::default()) + .await; + assert!( + coins.is_ok(), + "Coins query failed for {} network. Error: {}", + client.rpc_server(), + coins.unwrap_err() + ); } #[tokio::test] @@ -1212,33 +1215,32 @@ mod tests { #[tokio::test] #[ignore] async fn test_transactions_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let transactions = client.transactions(None, PaginationFilter::default()).await; - assert!( - transactions.is_ok(), - "Transactions query failed for network: {n}. Error: {}", - transactions.unwrap_err() - ); - } + let client = test_client(); + let transactions = client.transactions(None, PaginationFilter::default()).await; + assert!( + transactions.is_ok(), + "Transactions query failed for {} network. Error: {}", + client.rpc_server(), + transactions.unwrap_err() + ); } #[tokio::test] async fn test_total_supply() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let ts = client.total_supply("0x2::sui::SUI").await; - assert!( - ts.is_ok(), - "Total supply query failed for network: {n}. Error: {}", - ts.unwrap_err() - ); - assert_eq!( - ts.unwrap().unwrap(), - 10_000_000_000, - "Total supply mismatch for network: {n}" - ); - } + let client = test_client(); + let ts = client.total_supply("0x2::sui::SUI").await; + assert!( + ts.is_ok(), + "Total supply query failed for {} network. Error: {}", + client.rpc_server(), + ts.unwrap_err() + ); + assert_eq!( + ts.unwrap().unwrap(), + 10_000_000_000, + "Total supply mismatch for {} network", + client.rpc_server() + ); } #[tokio::test]