From 6d68b02ed7aa6efc8ed2412d3e62e569a755dc36 Mon Sep 17 00:00:00 2001 From: Juan Olveira Date: Thu, 28 May 2026 15:57:50 +0000 Subject: [PATCH 1/2] cli: migrate accesspass, resource, and leaf verbs to async Flip all six accesspass verbs (set, close, list, get, user-balances, fund), all six resource verbs (allocate, create, deallocate, get, close, verify), and the eight leaf single-file verbs (address, balance, init, migrate, keygen, export, config get, config set) to the RFC-20 async fn execute(self, ctx, client, out) signature. The five small leaf verbs (address, balance, init, migrate, keygen) adopt require! and print_signature where applicable. The larger and more idiosyncratic verbs (config get/set, export, all accesspass and resource verbs) keep their existing bodies for now and only get the signature flip; helper adoption lands opportunistically in follow-ups. accesspass fund's bespoke R: BufRead signature is preserved; the _ctx parameter is inserted after self. config get/set tests gain a per-file block_on shim so the existing sync #[test] bodies can still drive the now-async execute. The doublezero binary, admin, and the serviceability dispatcher all forward ctx and await every accesspass, resource, and leaf arm. All 345 unit tests pass byte-identically. --- CHANGELOG.md | 1 + controlplane/doublezero-admin/src/main.rs | 35 +- smartcontract/cli/src/accesspass/close.rs | 29 +- smartcontract/cli/src/accesspass/fund.rs | 97 +++- smartcontract/cli/src/accesspass/get.rs | 33 +- smartcontract/cli/src/accesspass/list.rs | 171 +++--- smartcontract/cli/src/accesspass/set.rs | 544 ++++++++++-------- .../cli/src/accesspass/user_balances.rs | 8 +- smartcontract/cli/src/address.rs | 13 +- smartcontract/cli/src/balance.rs | 13 +- smartcontract/cli/src/cli/command.rs | 41 +- smartcontract/cli/src/config/get.rs | 24 +- smartcontract/cli/src/config/set.rs | 243 ++++---- smartcontract/cli/src/export.rs | 8 +- smartcontract/cli/src/init.rs | 22 +- smartcontract/cli/src/keygen.rs | 8 +- smartcontract/cli/src/migrate.rs | 11 +- smartcontract/cli/src/resource/allocate.rs | 24 +- smartcontract/cli/src/resource/close.rs | 33 +- smartcontract/cli/src/resource/create.rs | 24 +- smartcontract/cli/src/resource/deallocate.rs | 24 +- smartcontract/cli/src/resource/get.rs | 24 +- smartcontract/cli/src/resource/verify.rs | 30 +- 23 files changed, 932 insertions(+), 528 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6834158cb..74c3ab3102 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ All notable changes to this project will be documented in this file. - Ship shell-completion scripts in the client installer and recommend `bash-completion` so apt/dnf pull it in when available. `build/` is added to `.gitignore`. - Migrate all six `exchange` verbs (`create`, `update`, `list`, `get`, `delete`, `set-device`) to the RFC-20 conforming shape on top of the shared CLI helpers. Every verb is now `pub async fn execute(self, ctx: &CliContext, client: &C, out: &mut W) -> eyre::Result<()>`, consumes the helpers (`require!`, `render_collection`, `render_record`, `print_signature`), and the update/delete/set-device paths route their pubkey-or-code argument through a new `resolve_exchange_pk` helper in `smartcontract/cli/src/helpers.rs`. The pre-existing BGP community range check in `exchange update` is preserved. `exchange set-device` retains its legacy `Option::and_then` semantics for `--device1` / `--device2` (an unknown device silently resolves to `None`, which clears the slot) under an explanatory comment. `controlplane/doublezero-admin`, the unified `doublezero` binary, and the serviceability dispatcher all forward `&ctx` and await every exchange arm. Behavior is byte-identical: table layout, JSON schema, `Signature: ` line, and `--json` / `--json-compact` semantics match pre-refactor output exactly; all 7 exchange unit tests pass without assertion changes. - Migrate all five `contributor` verbs (`create`, `update`, `list`, `get`, `delete`) to the RFC-20 conforming shape on top of the shared CLI helpers. Every verb is now `pub async fn execute(self, ctx: &CliContext, client: &C, out: &mut W) -> eyre::Result<()>`, consumes the helpers (`require!`, `render_collection`, `render_record`, `print_signature`), and `update` / `delete` route their pubkey-or-code argument through a new `resolve_contributor_pk` helper in `smartcontract/cli/src/helpers.rs`. The duplicate-code precondition in `create` and `update` is preserved, as is the `owner = "me"` short-circuit in `create` that resolves to the payer. `update`'s pubkey resolution now goes through the shared helper rather than an in-line `Pubkey::from_str` (the old code-by-pubkey path was a code-or-pubkey path despite the variable name; the resolver accepts both). `controlplane/doublezero-admin`, the unified `doublezero` binary, and the serviceability dispatcher all forward `&ctx` and await every contributor arm. Behavior is byte-identical: table layout, JSON schema, `Signature: ` line, and `--json` / `--json-compact` semantics match pre-refactor output exactly; all 5 contributor unit tests pass without assertion changes. + - Migrate all six `accesspass` verbs (`set`, `close`, `list`, `get`, `user-balances`, `fund`) and all six `resource` verbs (`allocate`, `create`, `deallocate`, `get`, `close`, `verify`), plus the eight leaf single-file verbs (`address`, `balance`, `init`, `migrate`, `keygen`, `export`, `config get`, `config set`), to the RFC-20 `pub async fn execute(self, ctx: &CliContext, client, out) -> eyre::Result<()>` signature. The five small leaf verbs (`address`, `balance`, `init`, `migrate`, `keygen`) also adopt the `require!` macro and (where applicable) the `print_signature` helper since their bodies were one-line readiness checks paired with a single `Signature:` write. The larger and more idiosyncratic verbs (`config get`/`set` which manipulate the persisted YAML, `export` which serializes the whole graph, the accesspass and resource verbs which contain bespoke output and progress-spinner logic) keep their existing bodies for now and only get the signature flip; helper adoption for those lands opportunistically in follow-up PRs. `controlplane/doublezero-admin`, the unified `doublezero` binary, and the serviceability dispatcher all forward `&ctx` and await every accesspass, resource, and leaf-verb arm. `config get`/`set` tests gain a per-file `block_on` shim and a `cli_context_default_for_tests()` import so the existing sync `#[test]` bodies can still drive the now-async `execute`. The bespoke `accesspass fund` signature (`R: BufRead` for stdin) is preserved — only the `_ctx` parameter is inserted after `self`. Behavior is byte-identical: table layouts, JSON schemas, `Signature:` lines, the `fund` interactive flow, and the `config` text output all match the pre-refactor strings exactly; all 345 unit tests pass without assertion changes. - Migrate all eight `tenant` verbs (`create`, `update`, `list`, `get`, `delete`, `administrator add`, `administrator remove`, `update-payment-status`) and all six `permission` verbs (`set`, `suspend`, `resume`, `delete`, `get`, `list`) to the RFC-20 conforming shape on top of the shared CLI helpers. Every verb is now `pub async fn execute(self, ctx: &CliContext, client: &C, out: &mut W) -> eyre::Result<()>`, consumes the helpers (`require!`, `render_collection`, `render_record`, `print_signature`), and tenant verbs that accept a pubkey-or-code identifier (`update`, `delete`, `add-administrator`, `remove-administrator`, `update-payment-status`) route through a new `resolve_tenant_pk` helper in `smartcontract/cli/src/helpers.rs`. The duplicate-code precondition in `tenant create` is preserved, as is the `administrator = "me"` short-circuit. `tenant delete`'s bespoke two-line output ("✓ Tenant 'X' deleted successfully\n Signature: ..."), its cascade-delete progress spinners, and its reference-count polling loop are preserved with manual `writeln!` calls and an explanatory comment. `permission set`'s bespoke two-line aligned output ("Signature: ..." + "Permissions: ...") is preserved the same way. Permission verbs derive the on-chain PDA from `user_payer` rather than going through a pubkey-or-code resolver. `controlplane/doublezero-admin`, the unified `doublezero` binary, and the serviceability dispatcher all forward `&ctx` and await every tenant and permission arm. Behavior is byte-identical: table layout, JSON schema, `Signature:` line shape, and `--json` / `--json-compact` semantics match pre-refactor output exactly; all 18 tenant and 18 permission unit tests pass without assertion changes. ## [v0.24.0](https://github.com/malbeclabs/doublezero/compare/client/v0.23.0...client/v0.24.0) - 2026-05-22 diff --git a/controlplane/doublezero-admin/src/main.rs b/controlplane/doublezero-admin/src/main.rs index 05c6f18270..44426dccd5 100644 --- a/controlplane/doublezero-admin/src/main.rs +++ b/controlplane/doublezero-admin/src/main.rs @@ -82,13 +82,13 @@ async fn main() -> eyre::Result<()> { let mut handle = stdout.lock(); let res = match app.command { - Command::Address(args) => args.execute(&client, &mut handle), - Command::Balance(args) => args.execute(&client, &mut handle), + Command::Address(args) => args.execute(&ctx, &client, &mut handle).await, + Command::Balance(args) => args.execute(&ctx, &client, &mut handle).await, - Command::Init(args) => args.execute(&client, &mut handle), + Command::Init(args) => args.execute(&ctx, &client, &mut handle).await, Command::Config(command) => match command.command { - ConfigCommands::Get(args) => args.execute(&client, &mut handle), - ConfigCommands::Set(args) => args.execute(&client, &mut handle), + ConfigCommands::Get(args) => args.execute(&ctx, &client, &mut handle).await, + ConfigCommands::Set(args) => args.execute(&ctx, &client, &mut handle).await, }, Command::GlobalConfig(command) => match command.command { GlobalConfigCommands::Set(args) => args.execute(&client, &mut handle), @@ -183,15 +183,24 @@ async fn main() -> eyre::Result<()> { LinkCommands::Delete(args) => args.execute(&client, &mut handle), }, Command::AccessPass(command) => match command.command { - cli::accesspass::AccessPassCommands::Set(args) => args.execute(&client, &mut handle), - cli::accesspass::AccessPassCommands::Close(args) => args.execute(&client, &mut handle), - cli::accesspass::AccessPassCommands::List(args) => args.execute(&client, &mut handle), - cli::accesspass::AccessPassCommands::Get(args) => args.execute(&client, &mut handle), + cli::accesspass::AccessPassCommands::Set(args) => { + args.execute(&ctx, &client, &mut handle).await + } + cli::accesspass::AccessPassCommands::Close(args) => { + args.execute(&ctx, &client, &mut handle).await + } + cli::accesspass::AccessPassCommands::List(args) => { + args.execute(&ctx, &client, &mut handle).await + } + cli::accesspass::AccessPassCommands::Get(args) => { + args.execute(&ctx, &client, &mut handle).await + } cli::accesspass::AccessPassCommands::UserBalances(args) => { - args.execute(&client, &mut handle) + args.execute(&ctx, &client, &mut handle).await } cli::accesspass::AccessPassCommands::Fund(args) => { - args.execute(&client, &mut handle, &mut std::io::stdin().lock()) + args.execute(&ctx, &client, &mut handle, &mut std::io::stdin().lock()) + .await } }, Command::Permission(command) => match command.command { @@ -274,8 +283,8 @@ async fn main() -> eyre::Result<()> { Command::Migrate(args) => match args.command { cli::migrate::MigrateCommands::FlexAlgo(cmd) => cmd.execute(&client, &mut handle), }, - Command::Export(args) => args.execute(&client, &mut handle), - Command::Keygen(args) => args.execute(&client, &mut handle), + Command::Export(args) => args.execute(&ctx, &client, &mut handle).await, + Command::Keygen(args) => args.execute(&ctx, &client, &mut handle).await, Command::Log(args) => args.execute(&dzclient, &mut handle), Command::Completion(args) => { let mut cmd = App::command(); diff --git a/smartcontract/cli/src/accesspass/close.rs b/smartcontract/cli/src/accesspass/close.rs index 835b353f0f..96b2944b7a 100644 --- a/smartcontract/cli/src/accesspass/close.rs +++ b/smartcontract/cli/src/accesspass/close.rs @@ -3,6 +3,7 @@ use crate::{ requirements::{CHECK_BALANCE, CHECK_ID_JSON}, }; use clap::Args; +use doublezero_cli_core::CliContext; use doublezero_sdk::commands::accesspass::close::CloseAccessPassCommand; use solana_sdk::pubkey::Pubkey; use std::io::Write; @@ -15,7 +16,12 @@ pub struct CloseAccessPassCliCommand { } impl CloseAccessPassCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { // Check requirements client.check_requirements(CHECK_ID_JSON | CHECK_BALANCE)?; @@ -36,10 +42,20 @@ mod tests { requirements::{CHECK_BALANCE, CHECK_ID_JSON}, tests::utils::create_test_client, }; + use doublezero_cli_core::testing::cli_context_default_for_tests; use doublezero_sdk::commands::accesspass::close::CloseAccessPassCommand; use doublezero_serviceability::pda::get_accesspass_pda; use mockall::predicate; use solana_sdk::{pubkey::Pubkey, signature::Signature}; + use tokio::runtime::Builder; + + fn block_on(f: F) -> F::Output { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(f) + } #[test] fn test_cli_device_create() { @@ -70,11 +86,14 @@ mod tests { })) .returning(move |_| Ok(signature)); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = CloseAccessPassCliCommand { - pubkey: accesspass_pubkey, - } - .execute(&client, &mut output); + let res = block_on( + CloseAccessPassCliCommand { + pubkey: accesspass_pubkey, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let output_str = String::from_utf8(output).unwrap(); assert_eq!( diff --git a/smartcontract/cli/src/accesspass/fund.rs b/smartcontract/cli/src/accesspass/fund.rs index 970aad132b..3397a27029 100644 --- a/smartcontract/cli/src/accesspass/fund.rs +++ b/smartcontract/cli/src/accesspass/fund.rs @@ -1,5 +1,6 @@ use crate::doublezerocommand::CliCommand; use clap::Args; +use doublezero_cli_core::CliContext; use doublezero_sdk::{ commands::{accesspass::list::ListAccessPassCommand, user::list::ListUserCommand}, UserType, @@ -31,8 +32,9 @@ pub struct FundAccessPassCliCommand { } impl FundAccessPassCliCommand { - pub fn execute( + pub async fn execute( self, + _ctx: &CliContext, client: &C, out: &mut W, input: &mut R, @@ -186,11 +188,21 @@ impl FundAccessPassCliCommand { mod tests { use super::*; use crate::tests::utils::create_test_client; + use doublezero_cli_core::testing::cli_context_default_for_tests; use doublezero_sdk::AccountType; use doublezero_serviceability::state::accesspass::{ AccessPass, AccessPassStatus, AccessPassType, }; use solana_sdk::{account::Account, pubkey::Pubkey}; + use tokio::runtime::Builder; + + fn block_on(f: F) -> F::Output { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(f) + } const RENT_PER_USER: u64 = 1_000_000; // needs_rent for 1 remaining slot = 1_000_000 + 250_000 = 1_250_000 @@ -246,9 +258,14 @@ mod tests { // balance > required (wallet_rent_min + needs_rent = 1_000_000 + 1_250_000 = 2_250_000) let client = setup_client_with_balance(payer, 2_500_000); + let ctx = cli_context_default_for_tests(); let mut out = Vec::new(); - let res = - FundAccessPassCliCommand::default().execute(&client, &mut out, &mut "".as_bytes()); + let res = block_on(FundAccessPassCliCommand::default().execute( + &ctx, + &client, + &mut out, + &mut "".as_bytes(), + )); assert!(res.is_ok()); assert_eq!( @@ -263,12 +280,15 @@ mod tests { // balance = 500_000 < required (wallet_rent_min + needs_rent = 1_000_000 + 1_250_000 = 2_250_000), deficit = 1_750_000 let client = setup_client_with_balance(payer, 500_000); + let ctx = cli_context_default_for_tests(); let mut out = Vec::new(); - let res = FundAccessPassCliCommand { - dry_run: true, - ..Default::default() - } - .execute(&client, &mut out, &mut "".as_bytes()); + let res = block_on( + FundAccessPassCliCommand { + dry_run: true, + ..Default::default() + } + .execute(&ctx, &client, &mut out, &mut "".as_bytes()), + ); assert!(res.is_ok()); let output = String::from_utf8(out).unwrap(); @@ -287,9 +307,14 @@ mod tests { .expect_transfer_sol() .returning(|_, _| Ok(solana_sdk::signature::Signature::default())); + let ctx = cli_context_default_for_tests(); let mut out = Vec::new(); - let res = - FundAccessPassCliCommand::default().execute(&client, &mut out, &mut "y\n".as_bytes()); + let res = block_on(FundAccessPassCliCommand::default().execute( + &ctx, + &client, + &mut out, + &mut "y\n".as_bytes(), + )); assert!(res.is_ok()); let output = String::from_utf8(out).unwrap(); @@ -302,9 +327,14 @@ mod tests { let payer = Pubkey::from_str_const("1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB"); let client = setup_client_with_balance(payer, 500_000); + let ctx = cli_context_default_for_tests(); let mut out = Vec::new(); - let res = - FundAccessPassCliCommand::default().execute(&client, &mut out, &mut "n\n".as_bytes()); + let res = block_on(FundAccessPassCliCommand::default().execute( + &ctx, + &client, + &mut out, + &mut "n\n".as_bytes(), + )); assert!(res.is_ok()); let output = String::from_utf8(out).unwrap(); @@ -320,13 +350,16 @@ mod tests { // required = wallet_rent_min + max(needs_rent, min_balance) = 1_000_000 + max(1_250_000, 2_000_000) = 3_000_000, deficit = 1_500_000 let client = setup_client_with_balance(payer, 1_500_000); + let ctx = cli_context_default_for_tests(); let mut out = Vec::new(); - let res = FundAccessPassCliCommand { - min_balance: Some(0.002), // 2_000_000 lamports - dry_run: true, - ..Default::default() - } - .execute(&client, &mut out, &mut "".as_bytes()); + let res = block_on( + FundAccessPassCliCommand { + min_balance: Some(0.002), // 2_000_000 lamports + dry_run: true, + ..Default::default() + } + .execute(&ctx, &client, &mut out, &mut "".as_bytes()), + ); assert!(res.is_ok()); let output = String::from_utf8(out).unwrap(); @@ -341,13 +374,16 @@ mod tests { // required = wallet_rent_min + max(needs_rent, min_balance) = 1_000_000 + max(1_250_000, 1) = 2_250_000, deficit = 1_750_000 let client = setup_client_with_balance(payer, 500_000); + let ctx = cli_context_default_for_tests(); let mut out = Vec::new(); - let res = FundAccessPassCliCommand { - min_balance: Some(0.000000001), - dry_run: true, - ..Default::default() - } - .execute(&client, &mut out, &mut "".as_bytes()); + let res = block_on( + FundAccessPassCliCommand { + min_balance: Some(0.000000001), + dry_run: true, + ..Default::default() + } + .execute(&ctx, &client, &mut out, &mut "".as_bytes()), + ); assert!(res.is_ok()); let output = String::from_utf8(out).unwrap(); @@ -363,12 +399,15 @@ mod tests { .expect_transfer_sol() .returning(|_, _| Ok(solana_sdk::signature::Signature::default())); + let ctx = cli_context_default_for_tests(); let mut out = Vec::new(); - let res = FundAccessPassCliCommand { - force: true, - ..Default::default() - } - .execute(&client, &mut out, &mut "".as_bytes()); + let res = block_on( + FundAccessPassCliCommand { + force: true, + ..Default::default() + } + .execute(&ctx, &client, &mut out, &mut "".as_bytes()), + ); assert!(res.is_ok()); let output = String::from_utf8(out).unwrap(); diff --git a/smartcontract/cli/src/accesspass/get.rs b/smartcontract/cli/src/accesspass/get.rs index 84d9eebfea..8227a2f1fe 100644 --- a/smartcontract/cli/src/accesspass/get.rs +++ b/smartcontract/cli/src/accesspass/get.rs @@ -1,5 +1,6 @@ use crate::doublezerocommand::CliCommand; use clap::Args; +use doublezero_cli_core::CliContext; use doublezero_sdk::commands::{ accesspass::get::GetAccessPassCommand, multicastgroup::list::ListMulticastGroupCommand, tenant::list::ListTenantCommand, @@ -42,7 +43,12 @@ struct AccessPassDisplay { } impl GetAccessPassCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { let epoch = client.get_epoch()?; let (pubkey, accesspass) = client @@ -124,6 +130,7 @@ impl GetAccessPassCliCommand { #[cfg(test)] mod tests { use crate::{accesspass::get::GetAccessPassCliCommand, tests::utils::create_test_client}; + use doublezero_cli_core::testing::cli_context_default_for_tests; use doublezero_sdk::{ commands::{ accesspass::get::GetAccessPassCommand, multicastgroup::list::ListMulticastGroupCommand, @@ -138,6 +145,15 @@ mod tests { use mockall::predicate; use solana_sdk::pubkey::Pubkey; use std::{collections::HashMap, net::Ipv4Addr}; + use tokio::runtime::Builder; + + fn block_on(f: F) -> F::Output { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(f) + } #[test] fn test_cli_accesspass_get() { @@ -221,13 +237,16 @@ mod tests { Ok(map) }); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = GetAccessPassCliCommand { - client_ip, - user_payer, - json: false, - } - .execute(&client, &mut output); + let res = block_on( + GetAccessPassCliCommand { + client_ip, + user_payer, + json: false, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let output_str = String::from_utf8(output).unwrap(); let has_row = |header: &str, value: &str| { diff --git a/smartcontract/cli/src/accesspass/list.rs b/smartcontract/cli/src/accesspass/list.rs index 52b6a7b145..c4f5f520f6 100644 --- a/smartcontract/cli/src/accesspass/list.rs +++ b/smartcontract/cli/src/accesspass/list.rs @@ -1,5 +1,6 @@ use crate::doublezerocommand::CliCommand; use clap::Args; +use doublezero_cli_core::CliContext; use doublezero_program_common::serializer; use doublezero_sdk::commands::{ accesspass::list::ListAccessPassCommand, multicastgroup::list::ListMulticastGroupCommand, @@ -78,7 +79,12 @@ pub struct AccessPassDisplay { } impl ListAccessPassCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { let epoch = client.get_epoch()?; let mgroups = client.list_multicastgroup(ListMulticastGroupCommand {})?; @@ -271,12 +277,22 @@ impl ListAccessPassCliCommand { #[cfg(test)] mod tests { use crate::{accesspass::list::ListAccessPassCliCommand, tests::utils::create_test_client}; + use doublezero_cli_core::testing::cli_context_default_for_tests; use doublezero_sdk::AccountType; use doublezero_serviceability::state::accesspass::{ AccessPass, AccessPassStatus, AccessPassType, IS_DYNAMIC, }; use solana_sdk::pubkey::Pubkey; use std::{collections::HashMap, net::Ipv4Addr}; + use tokio::runtime::Builder; + + fn block_on(f: F) -> F::Output { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(f) + } #[test] fn test_cli_accesspass_list() { @@ -367,70 +383,79 @@ mod tests { Ok(access_passes) }); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = ListAccessPassCliCommand { - prepaid: false, - client_ip: None, - user_payer: None, - tenant: None, - solana_validator: false, - solana_identity: None, - edge_seat: false, - seat_pubkey: None, - multicast_group_publisher: None, - multicast_group_subscriber: None, - not_multicast_group_publisher: None, - not_multicast_group_subscriber: None, - json: false, - json_compact: false, - } - .execute(&client, &mut output); + let res = block_on( + ListAccessPassCliCommand { + prepaid: false, + client_ip: None, + user_payer: None, + tenant: None, + solana_validator: false, + solana_identity: None, + edge_seat: false, + seat_pubkey: None, + multicast_group_publisher: None, + multicast_group_subscriber: None, + not_multicast_group_publisher: None, + not_multicast_group_subscriber: None, + json: false, + json_compact: false, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let output_str = String::from_utf8(output).unwrap(); assert_eq!(output_str, " account | accesspass_type | client_ip | user_payer | tenant | multicast | last_access_epoch | remaining_epoch | flags | connections | status | owner \n 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM | solana_validator: 1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB | 0.0.0.0 | 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM | | S:test | 123 | 113 | dynamic | 0 | connected | 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM \n 1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB | prepaid | 1.2.3.4 | 1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB | | P:test | 123 | 113 | | 0 | connected | 1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB \n 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo9 | prepaid | 2.3.4.5 | 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo9 | | P:test | 123 | 113 | | 0 | connected | 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo9 \n"); let mut output = Vec::new(); - let res = ListAccessPassCliCommand { - prepaid: false, - solana_validator: false, - solana_identity: None, - edge_seat: false, - seat_pubkey: None, - tenant: None, - json: false, - json_compact: true, - client_ip: None, - user_payer: None, - multicast_group_publisher: None, - multicast_group_subscriber: None, - not_multicast_group_publisher: None, - not_multicast_group_subscriber: None, - } - .execute(&client, &mut output); + let res = block_on( + ListAccessPassCliCommand { + prepaid: false, + solana_validator: false, + solana_identity: None, + edge_seat: false, + seat_pubkey: None, + tenant: None, + json: false, + json_compact: true, + client_ip: None, + user_payer: None, + multicast_group_publisher: None, + multicast_group_subscriber: None, + not_multicast_group_publisher: None, + not_multicast_group_subscriber: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let output_str = String::from_utf8(output).unwrap(); assert_eq!(output_str, "[{\"account\":\"1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM\",\"accesspass_type\":\"solana_validator: 1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB\",\"client_ip\":\"0.0.0.0\",\"user_payer\":\"1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM\",\"tenant\":\"\",\"multicast\":\"S:test\",\"last_access_epoch\":\"123\",\"remaining_epoch\":\"113\",\"flags\":\"dynamic\",\"connections\":0,\"status\":\"Connected\",\"owner\":\"1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM\"},{\"account\":\"1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB\",\"accesspass_type\":\"prepaid\",\"client_ip\":\"1.2.3.4\",\"user_payer\":\"1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB\",\"tenant\":\"\",\"multicast\":\"P:test\",\"last_access_epoch\":\"123\",\"remaining_epoch\":\"113\",\"flags\":\"\",\"connections\":0,\"status\":\"Connected\",\"owner\":\"1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB\"},{\"account\":\"11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo9\",\"accesspass_type\":\"prepaid\",\"client_ip\":\"2.3.4.5\",\"user_payer\":\"11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo9\",\"tenant\":\"\",\"multicast\":\"P:test\",\"last_access_epoch\":\"123\",\"remaining_epoch\":\"113\",\"flags\":\"\",\"connections\":0,\"status\":\"Connected\",\"owner\":\"11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo9\"}]\n"); // Test filtering by client IP let mut output = Vec::new(); - let res = ListAccessPassCliCommand { - client_ip: Some(Ipv4Addr::new(1, 2, 3, 4)), - ..Default::default() - } - .execute(&client, &mut output); + let res = block_on( + ListAccessPassCliCommand { + client_ip: Some(Ipv4Addr::new(1, 2, 3, 4)), + ..Default::default() + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let output_str = String::from_utf8(output).unwrap(); assert_eq!(output_str, " account | accesspass_type | client_ip | user_payer | tenant | multicast | last_access_epoch | remaining_epoch | flags | connections | status | owner \n 1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB | prepaid | 1.2.3.4 | 1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB | | P:test | 123 | 113 | | 0 | connected | 1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB \n"); // Test filtering by user payer let mut output = Vec::new(); - let res = ListAccessPassCliCommand { - user_payer: Some(Pubkey::from_str_const( - "1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB", - )), - ..Default::default() - } - .execute(&client, &mut output); + let res = block_on( + ListAccessPassCliCommand { + user_payer: Some(Pubkey::from_str_const( + "1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB", + )), + ..Default::default() + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let output_str = String::from_utf8(output).unwrap(); assert_eq!(output_str, " account | accesspass_type | client_ip | user_payer | tenant | multicast | last_access_epoch | remaining_epoch | flags | connections | status | owner \n 1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB | prepaid | 1.2.3.4 | 1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB | | P:test | 123 | 113 | | 0 | connected | 1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB \n"); @@ -536,12 +561,15 @@ mod tests { fn test_filter_multicast_group_publisher() { let (client, access1_pubkey, access2_pubkey, access3_pubkey) = setup_multicast_client(); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = ListAccessPassCliCommand { - multicast_group_publisher: Some("test".to_string()), - ..Default::default() - } - .execute(&client, &mut output); + let res = block_on( + ListAccessPassCliCommand { + multicast_group_publisher: Some("test".to_string()), + ..Default::default() + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let out = String::from_utf8(output).unwrap(); @@ -556,12 +584,15 @@ mod tests { fn test_filter_multicast_group_subscriber() { let (client, access1_pubkey, access2_pubkey, access3_pubkey) = setup_multicast_client(); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = ListAccessPassCliCommand { - multicast_group_subscriber: Some("test".to_string()), - ..Default::default() - } - .execute(&client, &mut output); + let res = block_on( + ListAccessPassCliCommand { + multicast_group_subscriber: Some("test".to_string()), + ..Default::default() + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let out = String::from_utf8(output).unwrap(); @@ -576,12 +607,15 @@ mod tests { fn test_filter_not_multicast_group_publisher() { let (client, access1_pubkey, access2_pubkey, access3_pubkey) = setup_multicast_client(); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = ListAccessPassCliCommand { - not_multicast_group_publisher: Some("test".to_string()), - ..Default::default() - } - .execute(&client, &mut output); + let res = block_on( + ListAccessPassCliCommand { + not_multicast_group_publisher: Some("test".to_string()), + ..Default::default() + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let out = String::from_utf8(output).unwrap(); @@ -596,12 +630,15 @@ mod tests { fn test_filter_not_multicast_group_subscriber() { let (client, access1_pubkey, access2_pubkey, access3_pubkey) = setup_multicast_client(); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = ListAccessPassCliCommand { - not_multicast_group_subscriber: Some("test".to_string()), - ..Default::default() - } - .execute(&client, &mut output); + let res = block_on( + ListAccessPassCliCommand { + not_multicast_group_subscriber: Some("test".to_string()), + ..Default::default() + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let out = String::from_utf8(output).unwrap(); diff --git a/smartcontract/cli/src/accesspass/set.rs b/smartcontract/cli/src/accesspass/set.rs index a827444700..e6a97f7ef3 100644 --- a/smartcontract/cli/src/accesspass/set.rs +++ b/smartcontract/cli/src/accesspass/set.rs @@ -3,6 +3,7 @@ use crate::{ requirements::{CHECK_BALANCE, CHECK_ID_JSON}, }; use clap::{Args, ValueEnum}; +use doublezero_cli_core::CliContext; use doublezero_sdk::commands::accesspass::set::SetAccessPassCommand; use doublezero_serviceability::{ pda::{get_accesspass_pda, get_tenant_pda}, @@ -55,7 +56,12 @@ pub struct SetAccessPassCliCommand { } impl SetAccessPassCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { // Check requirements client.check_requirements(CHECK_ID_JSON | CHECK_BALANCE)?; @@ -135,6 +141,7 @@ mod tests { requirements::{CHECK_BALANCE, CHECK_ID_JSON}, tests::utils::create_test_client, }; + use doublezero_cli_core::testing::cli_context_default_for_tests; use doublezero_sdk::commands::accesspass::set::SetAccessPassCommand; use doublezero_serviceability::{ pda::{get_accesspass_pda, get_tenant_pda}, @@ -142,9 +149,18 @@ mod tests { }; use mockall::predicate; use solana_sdk::{pubkey::Pubkey, signature::Signature}; + use tokio::runtime::Builder; use super::*; + fn block_on(f: F) -> F::Output { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(f) + } + #[test] fn test_cli_accesspass_set_prepaid() { let mut client = create_test_client(); @@ -178,20 +194,23 @@ mod tests { })) .returning(move |_| Ok(signature)); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::Prepaid, - client_ip: Some(client_ip), - user_payer: payer.to_string(), - epochs: "max".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::Prepaid, + client_ip: Some(client_ip), + user_payer: payer.to_string(), + epochs: "max".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let output_str = String::from_utf8(output).unwrap(); assert_eq!( @@ -213,20 +232,23 @@ mod tests { .with(predicate::eq(CHECK_ID_JSON | CHECK_BALANCE)) .returning(|_| Ok(())); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::SolanaValidator, - client_ip: Some(client_ip), - user_payer: payer.to_string(), - epochs: "1".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::SolanaValidator, + client_ip: Some(client_ip), + user_payer: payer.to_string(), + epochs: "1".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_err()); assert_eq!( res.err().unwrap().to_string(), @@ -269,20 +291,23 @@ mod tests { })) .returning(move |_| Ok(signature)); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::SolanaValidator, - client_ip: Some(client_ip), - user_payer: payer.to_string(), - epochs: "1".into(), - solana_validator: Some(solana_validator), - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::SolanaValidator, + client_ip: Some(client_ip), + user_payer: payer.to_string(), + epochs: "1".into(), + solana_validator: Some(solana_validator), + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let output_str = String::from_utf8(output).unwrap(); assert_eq!( @@ -324,20 +349,23 @@ mod tests { })) .returning(move |_| Ok(signature)); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::SolanaRPC, - client_ip: Some(client_ip), - user_payer: payer.to_string(), - epochs: "1".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::SolanaRPC, + client_ip: Some(client_ip), + user_payer: payer.to_string(), + epochs: "1".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); } @@ -376,20 +404,23 @@ mod tests { })) .returning(move |_| Ok(signature)); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::SolanaRPC, - client_ip: Some(client_ip), - user_payer: payer.to_string(), - epochs: "1".into(), - solana_validator: Some(solana_validator), - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::SolanaRPC, + client_ip: Some(client_ip), + user_payer: payer.to_string(), + epochs: "1".into(), + solana_validator: Some(solana_validator), + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let output_str = String::from_utf8(output).unwrap(); assert_eq!( @@ -411,20 +442,23 @@ mod tests { .with(predicate::eq(CHECK_ID_JSON | CHECK_BALANCE)) .returning(|_| Ok(())); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::Others, - client_ip: Some(client_ip), - user_payer: payer.to_string(), - epochs: "1".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::Others, + client_ip: Some(client_ip), + user_payer: payer.to_string(), + epochs: "1".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_err()); assert_eq!( res.err().unwrap().to_string(), @@ -445,20 +479,23 @@ mod tests { .with(predicate::eq(CHECK_ID_JSON | CHECK_BALANCE)) .returning(|_| Ok(())); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::Others, - client_ip: Some(client_ip), - user_payer: payer.to_string(), - epochs: "1".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: Some("custom-name".to_string()), - others_key: None, - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::Others, + client_ip: Some(client_ip), + user_payer: payer.to_string(), + epochs: "1".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: Some("custom-name".to_string()), + others_key: None, + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_err()); assert_eq!( res.err().unwrap().to_string(), @@ -502,20 +539,23 @@ mod tests { })) .returning(move |_| Ok(signature)); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::Others, - client_ip: Some(client_ip), - user_payer: payer.to_string(), - epochs: "1".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: Some("custom-name".to_string()), - others_key: Some("custom-key".to_string()), - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::Others, + client_ip: Some(client_ip), + user_payer: payer.to_string(), + epochs: "1".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: Some("custom-name".to_string()), + others_key: Some("custom-key".to_string()), + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let output_str = String::from_utf8(output).unwrap(); assert_eq!( @@ -534,20 +574,23 @@ mod tests { .with(predicate::eq(CHECK_ID_JSON | CHECK_BALANCE)) .returning(|_| Ok(())); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::Prepaid, - client_ip: Some([100, 0, 0, 1].into()), - user_payer: "not-a-valid-pubkey".to_string(), - epochs: "max".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::Prepaid, + client_ip: Some([100, 0, 0, 1].into()), + user_payer: "not-a-valid-pubkey".to_string(), + epochs: "max".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_err()); } @@ -561,21 +604,24 @@ mod tests { .with(predicate::eq(CHECK_ID_JSON | CHECK_BALANCE)) .returning(|_| Ok(())); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::Prepaid, - client_ip: Some([100, 0, 0, 1].into()), - user_payer: Pubkey::from_str_const("1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB") - .to_string(), - epochs: "not-a-number".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::Prepaid, + client_ip: Some([100, 0, 0, 1].into()), + user_payer: Pubkey::from_str_const("1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB") + .to_string(), + epochs: "not-a-number".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_err()); } @@ -590,21 +636,24 @@ mod tests { .returning(|_| Ok(())); let too_long = "a".repeat(33); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::Prepaid, - client_ip: Some([100, 0, 0, 1].into()), - user_payer: Pubkey::from_str_const("1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB") - .to_string(), - epochs: "max".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: None, - tenant: Some(too_long.clone()), - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::Prepaid, + client_ip: Some([100, 0, 0, 1].into()), + user_payer: Pubkey::from_str_const("1111111FVAiSujNZVgYSc27t6zUTWoKfAGxbRzzPB") + .to_string(), + epochs: "max".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: None, + tenant: Some(too_long.clone()), + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_err()); assert_eq!( res.err().unwrap().to_string(), @@ -637,20 +686,23 @@ mod tests { })) .returning(move |_| Ok(signature)); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::Prepaid, - client_ip: Some(client_ip), - user_payer: payer.to_string(), - epochs: "max".into(), - solana_validator: None, - allow_multiple_ip: true, - others_name: None, - others_key: None, - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::Prepaid, + client_ip: Some(client_ip), + user_payer: payer.to_string(), + epochs: "max".into(), + solana_validator: None, + allow_multiple_ip: true, + others_name: None, + others_key: None, + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); } @@ -681,20 +733,23 @@ mod tests { })) .returning(move |_| Ok(signature)); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::Prepaid, - client_ip: Some(client_ip), - user_payer: payer.to_string(), - epochs: "max".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: None, - tenant: Some("acme".to_string()), - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::Prepaid, + client_ip: Some(client_ip), + user_payer: payer.to_string(), + epochs: "max".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: None, + tenant: Some("acme".to_string()), + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); } @@ -722,20 +777,23 @@ mod tests { })) .returning(move |_| Ok(signature)); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::Prepaid, - client_ip: None, - user_payer: payer.to_string(), - epochs: "max".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::Prepaid, + client_ip: None, + user_payer: payer.to_string(), + epochs: "max".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); } @@ -764,20 +822,23 @@ mod tests { })) .returning(move |_| Ok(signature)); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::Prepaid, - client_ip: Some(client_ip), - user_payer: payer.to_string(), - epochs: "0".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::Prepaid, + client_ip: Some(client_ip), + user_payer: payer.to_string(), + epochs: "0".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); } @@ -807,20 +868,23 @@ mod tests { })) .returning(move |_| Ok(signature)); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::Prepaid, - client_ip: Some(client_ip), - user_payer: "me".to_string(), - epochs: "max".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::Prepaid, + client_ip: Some(client_ip), + user_payer: "me".to_string(), + epochs: "max".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); } @@ -837,20 +901,23 @@ mod tests { .with(predicate::eq(CHECK_ID_JSON | CHECK_BALANCE)) .returning(|_| Ok(())); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::EdgeSeat, - client_ip: Some(client_ip), - user_payer: payer.to_string(), - epochs: "1".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: None, - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::EdgeSeat, + client_ip: Some(client_ip), + user_payer: payer.to_string(), + epochs: "1".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: None, + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_err()); assert_eq!( res.err().unwrap().to_string(), @@ -893,20 +960,23 @@ mod tests { })) .returning(move |_| Ok(signature)); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = SetAccessPassCliCommand { - accesspass_type: CliAccessPassType::EdgeSeat, - client_ip: Some(client_ip), - user_payer: payer.to_string(), - epochs: "1".into(), - solana_validator: None, - allow_multiple_ip: false, - others_name: None, - others_key: None, - seat: Some(seat_pk), - tenant: None, - } - .execute(&client, &mut output); + let res = block_on( + SetAccessPassCliCommand { + accesspass_type: CliAccessPassType::EdgeSeat, + client_ip: Some(client_ip), + user_payer: payer.to_string(), + epochs: "1".into(), + solana_validator: None, + allow_multiple_ip: false, + others_name: None, + others_key: None, + seat: Some(seat_pk), + tenant: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let output_str = String::from_utf8(output).unwrap(); assert_eq!( diff --git a/smartcontract/cli/src/accesspass/user_balances.rs b/smartcontract/cli/src/accesspass/user_balances.rs index f65e1214db..07430fd02d 100644 --- a/smartcontract/cli/src/accesspass/user_balances.rs +++ b/smartcontract/cli/src/accesspass/user_balances.rs @@ -1,6 +1,7 @@ use crate::doublezerocommand::CliCommand; use clap::{Args, ValueEnum}; use console::style; +use doublezero_cli_core::CliContext; use doublezero_program_common::serializer; use doublezero_sdk::{ commands::{accesspass::list::ListAccessPassCommand, user::list::ListUserCommand}, @@ -96,7 +97,12 @@ pub struct UserBalanceDisplay { } impl UserBalancesAccessPassCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { let access_passes = client.list_accesspass(ListAccessPassCommand)?; let users = client.list_user(ListUserCommand {})?; let rent_per_ip = client.get_minimum_balance_for_rent_exemption(USER_RENT_BYTES)?; diff --git a/smartcontract/cli/src/address.rs b/smartcontract/cli/src/address.rs index 8de8f730bf..c128aded71 100644 --- a/smartcontract/cli/src/address.rs +++ b/smartcontract/cli/src/address.rs @@ -1,14 +1,19 @@ -use crate::{doublezerocommand::CliCommand, requirements::CHECK_ID_JSON}; +use crate::doublezerocommand::CliCommand; use clap::Args; +use doublezero_cli_core::{require, CliContext, RequirementCheck}; use std::io::Write; #[derive(Args, Debug)] pub struct AddressCliCommand; impl AddressCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { - // Check requirements - client.check_requirements(CHECK_ID_JSON)?; + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { + require!(client, RequirementCheck::KEYPAIR); writeln!(out, "{}", &client.get_payer())?; diff --git a/smartcontract/cli/src/balance.rs b/smartcontract/cli/src/balance.rs index 2707a4169f..2265164471 100644 --- a/smartcontract/cli/src/balance.rs +++ b/smartcontract/cli/src/balance.rs @@ -1,14 +1,19 @@ -use crate::{doublezerocommand::CliCommand, requirements::CHECK_ID_JSON}; +use crate::doublezerocommand::CliCommand; use clap::Args; +use doublezero_cli_core::{require, CliContext, RequirementCheck}; use std::io::Write; #[derive(Args, Debug)] pub struct BalanceCliCommand; impl BalanceCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { - // Check requirements - client.check_requirements(CHECK_ID_JSON)?; + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { + require!(client, RequirementCheck::KEYPAIR); let balance = client.get_balance()?; diff --git a/smartcontract/cli/src/cli/command.rs b/smartcontract/cli/src/cli/command.rs index ef15b780e7..4be857b87a 100644 --- a/smartcontract/cli/src/cli/command.rs +++ b/smartcontract/cli/src/cli/command.rs @@ -94,16 +94,16 @@ impl ServiceabilityCommand { W: Write, { match self { - Self::Init(args) => args.execute(client, out), - Self::Migrate(args) => args.execute(client, out), - Self::Address(args) => args.execute(client, out), - Self::Balance(args) => args.execute(client, out), - Self::Export(args) => args.execute(client, out), - Self::Keygen(args) => args.execute(client, out), + Self::Init(args) => args.execute(ctx, client, out).await, + Self::Migrate(args) => args.execute(ctx, client, out).await, + Self::Address(args) => args.execute(ctx, client, out).await, + Self::Balance(args) => args.execute(ctx, client, out).await, + Self::Export(args) => args.execute(ctx, client, out).await, + Self::Keygen(args) => args.execute(ctx, client, out).await, Self::Config(cmd) => match cmd.command { - ConfigCommands::Get(args) => args.execute(client, out), - ConfigCommands::Set(args) => args.execute(client, out), + ConfigCommands::Get(args) => args.execute(ctx, client, out).await, + ConfigCommands::Set(args) => args.execute(ctx, client, out).await, }, Self::GlobalConfig(cmd) => match cmd.command { GlobalConfigCommands::Set(args) => args.execute(client, out), @@ -210,13 +210,14 @@ impl ServiceabilityCommand { }, }, Self::AccessPass(cmd) => match cmd.command { - AccessPassCommands::Set(args) => args.execute(client, out), - AccessPassCommands::Close(args) => args.execute(client, out), - AccessPassCommands::List(args) => args.execute(client, out), - AccessPassCommands::Get(args) => args.execute(client, out), - AccessPassCommands::UserBalances(args) => args.execute(client, out), + AccessPassCommands::Set(args) => args.execute(ctx, client, out).await, + AccessPassCommands::Close(args) => args.execute(ctx, client, out).await, + AccessPassCommands::List(args) => args.execute(ctx, client, out).await, + AccessPassCommands::Get(args) => args.execute(ctx, client, out).await, + AccessPassCommands::UserBalances(args) => args.execute(ctx, client, out).await, AccessPassCommands::Fund(args) => { - args.execute(client, out, &mut std::io::stdin().lock()) + args.execute(ctx, client, out, &mut std::io::stdin().lock()) + .await } }, Self::User(cmd) => match cmd.command { @@ -230,12 +231,12 @@ impl ServiceabilityCommand { UserCommands::RequestBan(args) => args.execute(client, out), }, Self::Resource(cmd) => match cmd.command { - ResourceCommands::Allocate(args) => args.execute(client, out), - ResourceCommands::Create(args) => args.execute(client, out), - ResourceCommands::Deallocate(args) => args.execute(client, out), - ResourceCommands::Get(args) => args.execute(client, out), - ResourceCommands::Close(args) => args.execute(client, out), - ResourceCommands::Verify(args) => args.execute(client, out), + ResourceCommands::Allocate(args) => args.execute(ctx, client, out).await, + ResourceCommands::Create(args) => args.execute(ctx, client, out).await, + ResourceCommands::Deallocate(args) => args.execute(ctx, client, out).await, + ResourceCommands::Get(args) => args.execute(ctx, client, out).await, + ResourceCommands::Close(args) => args.execute(ctx, client, out).await, + ResourceCommands::Verify(args) => args.execute(ctx, client, out).await, }, } } diff --git a/smartcontract/cli/src/config/get.rs b/smartcontract/cli/src/config/get.rs index 6892914577..b373f142cf 100644 --- a/smartcontract/cli/src/config/get.rs +++ b/smartcontract/cli/src/config/get.rs @@ -1,5 +1,6 @@ use crate::doublezerocommand::CliCommand; use clap::Args; +use doublezero_cli_core::CliContext; use doublezero_sdk::{convert_url_to_ws, read_doublezero_config}; use std::io::Write; @@ -7,7 +8,12 @@ use std::io::Write; pub struct GetConfigCliCommand; impl GetConfigCliCommand { - pub fn execute(self, _client: &dyn CliCommand, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + _client: &dyn CliCommand, + out: &mut W, + ) -> eyre::Result<()> { let (filename, config) = read_doublezero_config()?; writeln!( @@ -39,7 +45,9 @@ impl GetConfigCliCommand { mod tests { use serial_test::serial; use tempfile::TempDir; + use tokio::runtime::Builder; + use doublezero_cli_core::testing::cli_context_default_for_tests; use doublezero_sdk::{create_new_pubkey_user, write_doublezero_config, ClientConfig}; use crate::tests::utils::create_test_client; @@ -49,6 +57,14 @@ mod tests { const CONFIG_ENV_VAR: &str = "DOUBLEZERO_CONFIG_FILE"; + fn block_on(f: F) -> F::Output { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(f) + } + #[test] #[serial] fn test_cli_config_get() { @@ -59,9 +75,10 @@ mod tests { create_new_pubkey_user(false, Some(cfg.keypair_path.clone())).unwrap(); let client = create_test_client(); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - GetConfigCliCommand.execute(&client, &mut output).unwrap(); + block_on(GetConfigCliCommand.execute(&ctx, &client, &mut output)).unwrap(); let output_str = String::from_utf8(output).unwrap(); assert!(output_str.contains("Config File:")); @@ -91,9 +108,10 @@ mod tests { create_new_pubkey_user(false, Some(cfg.keypair_path.clone())).unwrap(); let client = create_test_client(); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - GetConfigCliCommand.execute(&client, &mut output).unwrap(); + block_on(GetConfigCliCommand.execute(&ctx, &client, &mut output)).unwrap(); let output_str = String::from_utf8(output).unwrap(); assert!(output_str.contains("Tenant: my-tenant")); }); diff --git a/smartcontract/cli/src/config/set.rs b/smartcontract/cli/src/config/set.rs index ee30f719f3..462fd3f410 100644 --- a/smartcontract/cli/src/config/set.rs +++ b/smartcontract/cli/src/config/set.rs @@ -1,5 +1,6 @@ use crate::doublezerocommand::CliCommand; use clap::{ArgGroup, Args}; +use doublezero_cli_core::CliContext; use doublezero_config::Environment; use doublezero_sdk::{ convert_geo_program_moniker, convert_program_moniker, convert_url_moniker, convert_url_to_ws, @@ -42,7 +43,12 @@ pub struct SetConfigCliCommand { } impl SetConfigCliCommand { - pub fn execute(self, _client: &dyn CliCommand, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + _client: &dyn CliCommand, + out: &mut W, + ) -> eyre::Result<()> { let (ledger_url, ledger_ws, program_id, geo_program_id) = if let Some(env) = self.env { if self.url.is_some() || self.ws.is_some() @@ -132,7 +138,17 @@ impl SetConfigCliCommand { #[cfg(test)] mod tests { + use doublezero_cli_core::testing::cli_context_default_for_tests; use serial_test::serial; + use tokio::runtime::Builder; + + fn block_on(f: F) -> F::Output { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(f) + } use tempfile::TempDir; use doublezero_sdk::{create_new_pubkey_user, ClientConfig}; @@ -150,20 +166,23 @@ mod tests { temp_env::with_var(CONFIG_ENV_VAR, Some(&config_path.to_str().unwrap()), || { let client = create_test_client(); + let ctx = cli_context_default_for_tests(); write_doublezero_config(&cfg).unwrap(); let mut output = Vec::new(); - SetConfigCliCommand { - env: Some(Environment::Devnet.to_string()), - url: None, - ws: None, - keypair: None, - program_id: None, - geo_program_id: None, - tenant: None, - no_tenant: false, - } - .execute(&client, &mut output) + block_on( + SetConfigCliCommand { + env: Some(Environment::Devnet.to_string()), + url: None, + ws: None, + keypair: None, + program_id: None, + geo_program_id: None, + tenant: None, + no_tenant: false, + } + .execute(&ctx, &client, &mut output), + ) .unwrap(); let output_str = String::from_utf8(output).unwrap(); let devnet_config = Environment::Devnet.config().unwrap(); @@ -185,19 +204,22 @@ mod tests { create_new_pubkey_user(false, Some(cfg.keypair_path.clone())).unwrap(); let client = create_test_client(); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - SetConfigCliCommand { - env: None, - url: None, - ws: None, - keypair: None, - program_id: None, - geo_program_id: None, - tenant: None, - no_tenant: false, - } - .execute(&client, &mut output) + block_on( + SetConfigCliCommand { + env: None, + url: None, + ws: None, + keypair: None, + program_id: None, + geo_program_id: None, + tenant: None, + no_tenant: false, + } + .execute(&ctx, &client, &mut output), + ) .unwrap(); let output_str = String::from_utf8(output).unwrap(); assert_eq!(output_str, "No arguments provided\n"); @@ -214,19 +236,22 @@ mod tests { create_new_pubkey_user(false, Some(cfg.keypair_path.clone())).unwrap(); let client = create_test_client(); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - SetConfigCliCommand { - env: Some(Environment::Devnet.to_string()), - url: None, - ws: None, - keypair: None, - program_id: None, - geo_program_id: None, - tenant: None, - no_tenant: false, - } - .execute(&client, &mut output) + block_on( + SetConfigCliCommand { + env: Some(Environment::Devnet.to_string()), + url: None, + ws: None, + keypair: None, + program_id: None, + geo_program_id: None, + tenant: None, + no_tenant: false, + } + .execute(&ctx, &client, &mut output), + ) .unwrap(); let output_str = String::from_utf8(output).unwrap(); @@ -249,19 +274,22 @@ mod tests { create_new_pubkey_user(false, Some(cfg.keypair_path.clone())).unwrap(); let client = create_test_client(); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - SetConfigCliCommand { - env: None, - url: Some("https://example.com".to_string()), - ws: None, - keypair: None, - program_id: None, - geo_program_id: None, - tenant: None, - no_tenant: false, - } - .execute(&client, &mut output) + block_on( + SetConfigCliCommand { + env: None, + url: Some("https://example.com".to_string()), + ws: None, + keypair: None, + program_id: None, + geo_program_id: None, + tenant: None, + no_tenant: false, + } + .execute(&ctx, &client, &mut output), + ) .unwrap(); let output_str = String::from_utf8(output).unwrap(); @@ -284,19 +312,22 @@ mod tests { create_new_pubkey_user(false, Some(cfg.keypair_path.clone())).unwrap(); let client = create_test_client(); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - SetConfigCliCommand { - env: None, - url: None, - ws: None, - keypair: None, - program_id: Some("1234567890".to_string()), - geo_program_id: None, - tenant: None, - no_tenant: false, - } - .execute(&client, &mut output) + block_on( + SetConfigCliCommand { + env: None, + url: None, + ws: None, + keypair: None, + program_id: Some("1234567890".to_string()), + geo_program_id: None, + tenant: None, + no_tenant: false, + } + .execute(&ctx, &client, &mut output), + ) .unwrap(); let output_str = String::from_utf8(output).unwrap(); @@ -341,19 +372,22 @@ mod tests { create_new_pubkey_user(false, Some(cfg.keypair_path.clone())).unwrap(); let client = create_test_client(); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - SetConfigCliCommand { - env: None, - url: None, - ws: None, - keypair: None, - program_id: None, - geo_program_id: None, - tenant: Some("my-tenant".to_string()), - no_tenant: false, - } - .execute(&client, &mut output) + block_on( + SetConfigCliCommand { + env: None, + url: None, + ws: None, + keypair: None, + program_id: None, + geo_program_id: None, + tenant: Some("my-tenant".to_string()), + no_tenant: false, + } + .execute(&ctx, &client, &mut output), + ) .unwrap(); let output_str = String::from_utf8(output).unwrap(); assert!(output_str.contains("Tenant: my-tenant")); @@ -376,19 +410,22 @@ mod tests { create_new_pubkey_user(false, Some(cfg.keypair_path.clone())).unwrap(); let client = create_test_client(); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - SetConfigCliCommand { - env: None, - url: None, - ws: None, - keypair: None, - program_id: None, - geo_program_id: None, - tenant: None, - no_tenant: true, - } - .execute(&client, &mut output) + block_on( + SetConfigCliCommand { + env: None, + url: None, + ws: None, + keypair: None, + program_id: None, + geo_program_id: None, + tenant: None, + no_tenant: true, + } + .execute(&ctx, &client, &mut output), + ) .unwrap(); let output_str = String::from_utf8(output).unwrap(); assert!(output_str.contains("Tenant: (not set)")); @@ -409,19 +446,22 @@ mod tests { create_new_pubkey_user(false, Some(cfg.keypair_path.clone())).unwrap(); let client = create_test_client(); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - SetConfigCliCommand { - env: None, - url: None, - ws: None, - keypair: None, - program_id: None, - geo_program_id: Some("MyGeoProgram123".to_string()), - tenant: None, - no_tenant: false, - } - .execute(&client, &mut output) + block_on( + SetConfigCliCommand { + env: None, + url: None, + ws: None, + keypair: None, + program_id: None, + geo_program_id: Some("MyGeoProgram123".to_string()), + tenant: None, + no_tenant: false, + } + .execute(&ctx, &client, &mut output), + ) .unwrap(); let output_str = String::from_utf8(output).unwrap(); @@ -442,19 +482,22 @@ mod tests { create_new_pubkey_user(false, Some(cfg.keypair_path.clone())).unwrap(); let client = create_test_client(); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - SetConfigCliCommand { - env: Some(Environment::Devnet.to_string()), - url: None, - ws: None, - keypair: None, - program_id: None, - geo_program_id: Some("MyGeoProgram123".to_string()), - tenant: None, - no_tenant: false, - } - .execute(&client, &mut output) + block_on( + SetConfigCliCommand { + env: Some(Environment::Devnet.to_string()), + url: None, + ws: None, + keypair: None, + program_id: None, + geo_program_id: Some("MyGeoProgram123".to_string()), + tenant: None, + no_tenant: false, + } + .execute(&ctx, &client, &mut output), + ) .unwrap(); let output_str = String::from_utf8(output).unwrap(); assert!(output_str.contains("Invalid flag combination")); diff --git a/smartcontract/cli/src/export.rs b/smartcontract/cli/src/export.rs index 63fced5f48..9b497ed8a9 100644 --- a/smartcontract/cli/src/export.rs +++ b/smartcontract/cli/src/export.rs @@ -1,5 +1,6 @@ use crate::doublezerocommand::CliCommand; use clap::Args; +use doublezero_cli_core::CliContext; use doublezero_program_common::types::parse_utils::bandwidth_to_string; use doublezero_sdk::commands::{ contributor, device::list::ListDeviceCommand, exchange::list::ListExchangeCommand, @@ -94,7 +95,12 @@ struct UserData { } impl ExportCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { let locations = client.list_location(ListLocationCommand)?; let exchanges = client.list_exchange(ListExchangeCommand)?; diff --git a/smartcontract/cli/src/init.rs b/smartcontract/cli/src/init.rs index 97336e1d0a..b166c119fc 100644 --- a/smartcontract/cli/src/init.rs +++ b/smartcontract/cli/src/init.rs @@ -1,8 +1,6 @@ -use crate::{ - doublezerocommand::CliCommand, - requirements::{CHECK_BALANCE, CHECK_ID_JSON}, -}; +use crate::doublezerocommand::CliCommand; use clap::Args; +use doublezero_cli_core::{print_signature, require, CliContext, RequirementCheck}; use doublezero_sdk::commands::globalstate::init::InitGlobalStateCommand; use std::io::Write; @@ -10,12 +8,18 @@ use std::io::Write; pub struct InitCliCommand; impl InitCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { - client.check_requirements(CHECK_ID_JSON | CHECK_BALANCE)?; + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { + require!( + client, + RequirementCheck::KEYPAIR | RequirementCheck::BALANCE + ); let signature = client.init_globalstate(InitGlobalStateCommand)?; - writeln!(out, "Signature: {signature}",)?; - - Ok(()) + print_signature(out, &signature) } } diff --git a/smartcontract/cli/src/keygen.rs b/smartcontract/cli/src/keygen.rs index a410915bd6..a17292a83d 100644 --- a/smartcontract/cli/src/keygen.rs +++ b/smartcontract/cli/src/keygen.rs @@ -1,5 +1,6 @@ use crate::doublezerocommand::CliCommand; use clap::Args; +use doublezero_cli_core::CliContext; use doublezero_sdk::create_new_pubkey_user; use solana_sdk::signer::Signer; use std::{io::Write, path::PathBuf}; @@ -15,7 +16,12 @@ pub struct KeyGenCliCommand { } impl KeyGenCliCommand { - pub fn execute(self, _client: &dyn CliCommand, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + _client: &dyn CliCommand, + out: &mut W, + ) -> eyre::Result<()> { match create_new_pubkey_user(self.force, self.outfile) { Ok(keypair) => { writeln!(out, "Pubkey: {}", keypair.pubkey())?; diff --git a/smartcontract/cli/src/migrate.rs b/smartcontract/cli/src/migrate.rs index 68d503bb29..5bac57a676 100644 --- a/smartcontract/cli/src/migrate.rs +++ b/smartcontract/cli/src/migrate.rs @@ -1,15 +1,20 @@ +use crate::doublezerocommand::CliCommand; use clap::Args; +use doublezero_cli_core::CliContext; use doublezero_sdk::commands::migrate::MigrateCommand; use serde_json::to_writer_pretty; use std::io::Write; -use crate::doublezerocommand::CliCommand; - #[derive(Args, Debug)] pub struct MigrateCliCommand {} impl MigrateCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { let _ = client.migrate(MigrateCommand {})?; to_writer_pretty(out, &"Migration completed successfully")?; Ok(()) diff --git a/smartcontract/cli/src/resource/allocate.rs b/smartcontract/cli/src/resource/allocate.rs index 56aa8aeee5..0c1b288fcb 100644 --- a/smartcontract/cli/src/resource/allocate.rs +++ b/smartcontract/cli/src/resource/allocate.rs @@ -4,6 +4,7 @@ use crate::{ requirements::{CHECK_BALANCE, CHECK_ID_JSON}, }; use clap::Args; +use doublezero_cli_core::CliContext; use doublezero_program_common::types::NetworkV4; use doublezero_sdk::{commands::resource::allocate::AllocateResourceCommand, IdOrIp}; use std::io::Write; @@ -57,7 +58,12 @@ impl From for AllocateResourceCommand { } impl AllocateResourceCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { // Check requirements client.check_requirements(CHECK_ID_JSON | CHECK_BALANCE)?; @@ -76,10 +82,20 @@ impl AllocateResourceCliCommand { mod tests { use super::*; use crate::doublezerocommand::MockCliCommand; + use doublezero_cli_core::testing::cli_context_default_for_tests; use doublezero_sdk::{Device, ResourceType as SdkResourceType}; use mockall::predicate::eq; use solana_sdk::{pubkey::Pubkey, signature::Signature}; use std::io::Cursor; + use tokio::runtime::Builder; + + fn block_on(f: F) -> F::Output { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(f) + } #[test] fn test_execute_success_dzprefixblock() { @@ -111,8 +127,9 @@ mod tests { index: Some(0), requested_allocation: None, }; + let ctx = cli_context_default_for_tests(); let mut out = Cursor::new(Vec::new()); - let result = cmd.execute(&mock, &mut out); + let result = block_on(cmd.execute(&ctx, &mock, &mut out)); assert!(result.is_ok()); let output = String::from_utf8(out.into_inner()).unwrap(); assert!(output.contains("Signature:")); @@ -139,8 +156,9 @@ mod tests { index: Some(1), requested_allocation: None, }; + let ctx = cli_context_default_for_tests(); let mut out = Cursor::new(Vec::new()); - let result = cmd.execute(&mock, &mut out); + let result = block_on(cmd.execute(&ctx, &mock, &mut out)); assert!(result.is_err()); assert_eq!( result.unwrap_err().to_string(), diff --git a/smartcontract/cli/src/resource/close.rs b/smartcontract/cli/src/resource/close.rs index 97547e59e1..dcea0ef2c6 100644 --- a/smartcontract/cli/src/resource/close.rs +++ b/smartcontract/cli/src/resource/close.rs @@ -4,6 +4,7 @@ use crate::{ requirements::{CHECK_BALANCE, CHECK_ID_JSON}, }; use clap::Args; +use doublezero_cli_core::CliContext; use doublezero_sdk::commands::resource::{ closeaccount::CloseResourceCommand, get::GetResourceCommand, }; @@ -23,7 +24,12 @@ pub struct CloseResourceCliCommand { } impl CloseResourceCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { // Check requirements client.check_requirements(CHECK_ID_JSON | CHECK_BALANCE)?; @@ -48,12 +54,22 @@ impl CloseResourceCliCommand { mod tests { use super::*; use crate::tests::utils::create_test_client; + use doublezero_cli_core::testing::cli_context_default_for_tests; use doublezero_sdk::{ commands::resource::{closeaccount::CloseResourceCommand, get::GetResourceCommand}, get_resource_extension_pda, AccountType, ResourceExtensionOwned, }; use mockall::predicate; use solana_sdk::{pubkey::Pubkey, signature::Signature}; + use tokio::runtime::Builder; + + fn block_on(f: F) -> F::Output { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(f) + } #[test] fn test_cli_resource_close() { @@ -102,13 +118,16 @@ mod tests { .with(predicate::eq(CloseResourceCommand { resource_type })) .returning(move |_| Ok(signature)); + let ctx = cli_context_default_for_tests(); let mut output = Vec::new(); - let res = CloseResourceCliCommand { - resource_type: ResourceType::DeviceTunnelBlock, - associated_pubkey: None, - index: None, - } - .execute(&client, &mut output); + let res = block_on( + CloseResourceCliCommand { + resource_type: ResourceType::DeviceTunnelBlock, + associated_pubkey: None, + index: None, + } + .execute(&ctx, &client, &mut output), + ); assert!(res.is_ok()); let output_str = String::from_utf8(output).unwrap(); assert_eq!( diff --git a/smartcontract/cli/src/resource/create.rs b/smartcontract/cli/src/resource/create.rs index 33715281fc..b9721a1a9a 100644 --- a/smartcontract/cli/src/resource/create.rs +++ b/smartcontract/cli/src/resource/create.rs @@ -4,6 +4,7 @@ use crate::{ requirements::{CHECK_BALANCE, CHECK_ID_JSON}, }; use clap::Args; +use doublezero_cli_core::CliContext; use doublezero_sdk::commands::resource::create::CreateResourceCommand; use std::io::Write; @@ -33,7 +34,12 @@ impl From for CreateResourceCommand { } impl CreateResourceCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { // Check requirements client.check_requirements(CHECK_ID_JSON | CHECK_BALANCE)?; @@ -52,10 +58,20 @@ impl CreateResourceCliCommand { mod tests { use super::*; use crate::doublezerocommand::MockCliCommand; + use doublezero_cli_core::testing::cli_context_default_for_tests; use doublezero_sdk::{Device, ResourceType as SdkResourceType}; use mockall::predicate::eq; use solana_sdk::{pubkey::Pubkey, signature::Signature}; use std::io::Cursor; + use tokio::runtime::Builder; + + fn block_on(f: F) -> F::Output { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(f) + } #[test] fn test_execute_success_dzprefixblock() { @@ -85,8 +101,9 @@ mod tests { associated_pubkey: Some(device_pk.to_string()), index: Some(0), }; + let ctx = cli_context_default_for_tests(); let mut out = Cursor::new(Vec::new()); - let result = cmd.execute(&mock, &mut out); + let result = block_on(cmd.execute(&ctx, &mock, &mut out)); assert!(result.is_ok()); let output = String::from_utf8(out.into_inner()).unwrap(); assert!(output.contains("Signature:")); @@ -112,8 +129,9 @@ mod tests { associated_pubkey: Some(device_pk.to_string()), index: Some(1), }; + let ctx = cli_context_default_for_tests(); let mut out = Cursor::new(Vec::new()); - let result = cmd.execute(&mock, &mut out); + let result = block_on(cmd.execute(&ctx, &mock, &mut out)); assert!(result.is_err()); assert_eq!( result.unwrap_err().to_string(), diff --git a/smartcontract/cli/src/resource/deallocate.rs b/smartcontract/cli/src/resource/deallocate.rs index 492685b75a..d0b8b8872a 100644 --- a/smartcontract/cli/src/resource/deallocate.rs +++ b/smartcontract/cli/src/resource/deallocate.rs @@ -4,6 +4,7 @@ use crate::{ requirements::{CHECK_BALANCE, CHECK_ID_JSON}, }; use clap::Args; +use doublezero_cli_core::CliContext; use doublezero_program_common::types::NetworkV4; use doublezero_sdk::{commands::resource::deallocate::DeallocateResourceCommand, IdOrIp}; use std::io::Write; @@ -59,7 +60,12 @@ impl From for DeallocateResourceCommand { } impl DeallocateResourceCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { // Check requirements client.check_requirements(CHECK_ID_JSON | CHECK_BALANCE)?; @@ -78,10 +84,20 @@ impl DeallocateResourceCliCommand { mod tests { use super::*; use crate::doublezerocommand::MockCliCommand; + use doublezero_cli_core::testing::cli_context_default_for_tests; use doublezero_sdk::{Device, ResourceType as SdkResourceType}; use mockall::predicate::eq; use solana_sdk::{pubkey::Pubkey, signature::Signature}; use std::io::Cursor; + use tokio::runtime::Builder; + + fn block_on(f: F) -> F::Output { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(f) + } #[test] fn test_execute_success_dzprefixblock() { @@ -113,8 +129,9 @@ mod tests { index: Some(0), value: "1.2.3.2/32".to_string(), }; + let ctx = cli_context_default_for_tests(); let mut out = Cursor::new(Vec::new()); - let result = cmd.execute(&mock, &mut out); + let result = block_on(cmd.execute(&ctx, &mock, &mut out)); assert!(result.is_ok()); let output = String::from_utf8(out.into_inner()).unwrap(); assert!(output.contains("Signature:")); @@ -141,8 +158,9 @@ mod tests { index: Some(1), value: "1.2.3.2/32".to_string(), }; + let ctx = cli_context_default_for_tests(); let mut out = Cursor::new(Vec::new()); - let result = cmd.execute(&mock, &mut out); + let result = block_on(cmd.execute(&ctx, &mock, &mut out)); assert!(result.is_err()); assert_eq!( result.unwrap_err().to_string(), diff --git a/smartcontract/cli/src/resource/get.rs b/smartcontract/cli/src/resource/get.rs index 3936293b86..db964b0c4e 100644 --- a/smartcontract/cli/src/resource/get.rs +++ b/smartcontract/cli/src/resource/get.rs @@ -1,6 +1,7 @@ use super::ResourceType; use crate::doublezerocommand::CliCommand; use clap::Args; +use doublezero_cli_core::CliContext; use doublezero_sdk::commands::resource::get::GetResourceCommand; use serde::Serialize; use std::io::Write; @@ -40,7 +41,12 @@ pub struct ResourceDisplay { } impl GetResourceCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { let json = self.json; let (_, resource_extension) = client.get_resource(self.into())?; @@ -68,6 +74,7 @@ impl GetResourceCliCommand { mod tests { use super::*; use crate::doublezerocommand::MockCliCommand; + use doublezero_cli_core::testing::cli_context_default_for_tests; use doublezero_sdk::{AccountType, ResourceType as SdkResourceType}; use doublezero_serviceability::{ id_allocator::IdAllocator, @@ -75,6 +82,15 @@ mod tests { }; use solana_program::pubkey::Pubkey; use std::io::Cursor; + use tokio::runtime::Builder; + + fn block_on(f: F) -> F::Output { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(f) + } #[test] fn test_from_cli_to_command() { @@ -110,8 +126,9 @@ mod tests { .expect_get_resource() .withf(|cmd: &GetResourceCommand| cmd.resource_type == SdkResourceType::LinkIds) .returning(move |_| Ok((Pubkey::default(), resource_ext.clone()))); + let ctx = cli_context_default_for_tests(); let mut output = Cursor::new(Vec::new()); - let result = cli_cmd.execute(&mock_client, &mut output); + let result = block_on(cli_cmd.execute(&ctx, &mock_client, &mut output)); assert!(result.is_ok()); let output_str = String::from_utf8(output.into_inner()).unwrap(); assert!( @@ -145,8 +162,9 @@ mod tests { .expect_get_resource() .withf(|cmd: &GetResourceCommand| cmd.resource_type == SdkResourceType::LinkIds) .returning(move |_| Ok((Pubkey::default(), resource_ext.clone()))); + let ctx = cli_context_default_for_tests(); let mut output = Cursor::new(Vec::new()); - let result = cli_cmd.execute(&mock_client, &mut output); + let result = block_on(cli_cmd.execute(&ctx, &mock_client, &mut output)); assert!(result.is_ok()); let json: serde_json::Value = serde_json::from_str(&String::from_utf8(output.into_inner()).unwrap()).unwrap(); diff --git a/smartcontract/cli/src/resource/verify.rs b/smartcontract/cli/src/resource/verify.rs index 6a25ca3085..9727e26788 100644 --- a/smartcontract/cli/src/resource/verify.rs +++ b/smartcontract/cli/src/resource/verify.rs @@ -1,5 +1,6 @@ use crate::doublezerocommand::CliCommand; use clap::Args; +use doublezero_cli_core::CliContext; use doublezero_program_common::types::NetworkV4; use doublezero_sdk::commands::resource::{ allocate::AllocateResourceCommand, closeaccount::CloseResourceByPubkeyCommand, @@ -91,7 +92,12 @@ pub struct VerifyResourceCliCommand { } impl VerifyResourceCliCommand { - pub fn execute(self, client: &C, out: &mut W) -> eyre::Result<()> { + pub async fn execute( + self, + _ctx: &CliContext, + client: &C, + out: &mut W, + ) -> eyre::Result<()> { let result = verify_resources(client)?; // Print summary @@ -1192,6 +1198,7 @@ fn check_discrepancies( mod tests { use super::*; use crate::doublezerocommand::MockCliCommand; + use doublezero_cli_core::testing::cli_context_default_for_tests; use doublezero_program_common::types::NetworkV4; use doublezero_sdk::AccountType; use doublezero_serviceability::{ @@ -1200,6 +1207,15 @@ mod tests { state::resource_extension::{Allocator, ResourceExtensionOwned}, }; use std::io::Cursor; + use tokio::runtime::Builder; + + fn block_on(f: F) -> F::Output { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(f) + } fn create_resource_extension_ip( program_id: &Pubkey, @@ -1520,8 +1536,9 @@ mod tests { .returning(move || Ok(accounts.clone())); let cmd = VerifyResourceCliCommand { fix: false }; + let ctx = cli_context_default_for_tests(); let mut output = Cursor::new(Vec::new()); - let result = cmd.execute(&mock_client, &mut output); + let result = block_on(cmd.execute(&ctx, &mock_client, &mut output)); assert!(result.is_ok()); let output_str = String::from_utf8(output.into_inner()).unwrap(); @@ -1603,8 +1620,9 @@ mod tests { .returning(move || Ok(accounts.clone())); let cmd = VerifyResourceCliCommand { fix: false }; + let ctx = cli_context_default_for_tests(); let mut output = Cursor::new(Vec::new()); - let result = cmd.execute(&mock_client, &mut output); + let result = block_on(cmd.execute(&ctx, &mock_client, &mut output)); assert!(result.is_ok()); let output_str = String::from_utf8(output.into_inner()).unwrap(); @@ -2096,8 +2114,9 @@ mod tests { .returning(move || Ok(accounts.clone())); let cmd = VerifyResourceCliCommand { fix: false }; + let ctx = cli_context_default_for_tests(); let mut output = Cursor::new(Vec::new()); - cmd.execute(&mock_client, &mut output).unwrap(); + block_on(cmd.execute(&ctx, &mock_client, &mut output)).unwrap(); let output_str = String::from_utf8(output.into_inner()).unwrap(); assert!(output_str.contains("Orphaned resource extensions")); assert!(output_str.contains(&orphan_pda.to_string())); @@ -2444,8 +2463,9 @@ mod tests { .returning(move || Ok(accounts.clone())); let cmd = VerifyResourceCliCommand { fix: false }; + let ctx = cli_context_default_for_tests(); let mut output = Cursor::new(Vec::new()); - cmd.execute(&mock_client, &mut output).unwrap(); + block_on(cmd.execute(&ctx, &mock_client, &mut output)).unwrap(); let output_str = String::from_utf8(output.into_inner()).unwrap(); assert!( From 7b4c9bc2e927c027d2a7e153114bf5c7208f6b09 Mon Sep 17 00:00:00 2001 From: Juan Olveira Date: Thu, 28 May 2026 18:24:36 +0000 Subject: [PATCH 2/2] cli: use shared block_on helper in accesspass, resource, and config verb tests Drop the per-verb local block_on copies in favor of the doublezero_cli_core::testing::block_on helper lifted in the location PR, matching the location reference verb. --- smartcontract/cli/src/accesspass/close.rs | 11 +---------- smartcontract/cli/src/accesspass/fund.rs | 11 +---------- smartcontract/cli/src/accesspass/get.rs | 11 +---------- smartcontract/cli/src/accesspass/list.rs | 11 +---------- smartcontract/cli/src/accesspass/set.rs | 11 +---------- smartcontract/cli/src/config/get.rs | 11 +---------- smartcontract/cli/src/config/set.rs | 11 +---------- smartcontract/cli/src/resource/allocate.rs | 11 +---------- smartcontract/cli/src/resource/close.rs | 11 +---------- smartcontract/cli/src/resource/create.rs | 11 +---------- smartcontract/cli/src/resource/deallocate.rs | 11 +---------- smartcontract/cli/src/resource/get.rs | 11 +---------- smartcontract/cli/src/resource/verify.rs | 11 +---------- 13 files changed, 13 insertions(+), 130 deletions(-) diff --git a/smartcontract/cli/src/accesspass/close.rs b/smartcontract/cli/src/accesspass/close.rs index 96b2944b7a..986c452ed6 100644 --- a/smartcontract/cli/src/accesspass/close.rs +++ b/smartcontract/cli/src/accesspass/close.rs @@ -42,20 +42,11 @@ mod tests { requirements::{CHECK_BALANCE, CHECK_ID_JSON}, tests::utils::create_test_client, }; - use doublezero_cli_core::testing::cli_context_default_for_tests; + use doublezero_cli_core::testing::{block_on, cli_context_default_for_tests}; use doublezero_sdk::commands::accesspass::close::CloseAccessPassCommand; use doublezero_serviceability::pda::get_accesspass_pda; use mockall::predicate; use solana_sdk::{pubkey::Pubkey, signature::Signature}; - use tokio::runtime::Builder; - - fn block_on(f: F) -> F::Output { - Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(f) - } #[test] fn test_cli_device_create() { diff --git a/smartcontract/cli/src/accesspass/fund.rs b/smartcontract/cli/src/accesspass/fund.rs index 3397a27029..d32cf22964 100644 --- a/smartcontract/cli/src/accesspass/fund.rs +++ b/smartcontract/cli/src/accesspass/fund.rs @@ -188,21 +188,12 @@ impl FundAccessPassCliCommand { mod tests { use super::*; use crate::tests::utils::create_test_client; - use doublezero_cli_core::testing::cli_context_default_for_tests; + use doublezero_cli_core::testing::{block_on, cli_context_default_for_tests}; use doublezero_sdk::AccountType; use doublezero_serviceability::state::accesspass::{ AccessPass, AccessPassStatus, AccessPassType, }; use solana_sdk::{account::Account, pubkey::Pubkey}; - use tokio::runtime::Builder; - - fn block_on(f: F) -> F::Output { - Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(f) - } const RENT_PER_USER: u64 = 1_000_000; // needs_rent for 1 remaining slot = 1_000_000 + 250_000 = 1_250_000 diff --git a/smartcontract/cli/src/accesspass/get.rs b/smartcontract/cli/src/accesspass/get.rs index 8227a2f1fe..a57807acb8 100644 --- a/smartcontract/cli/src/accesspass/get.rs +++ b/smartcontract/cli/src/accesspass/get.rs @@ -130,7 +130,7 @@ impl GetAccessPassCliCommand { #[cfg(test)] mod tests { use crate::{accesspass::get::GetAccessPassCliCommand, tests::utils::create_test_client}; - use doublezero_cli_core::testing::cli_context_default_for_tests; + use doublezero_cli_core::testing::{block_on, cli_context_default_for_tests}; use doublezero_sdk::{ commands::{ accesspass::get::GetAccessPassCommand, multicastgroup::list::ListMulticastGroupCommand, @@ -145,15 +145,6 @@ mod tests { use mockall::predicate; use solana_sdk::pubkey::Pubkey; use std::{collections::HashMap, net::Ipv4Addr}; - use tokio::runtime::Builder; - - fn block_on(f: F) -> F::Output { - Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(f) - } #[test] fn test_cli_accesspass_get() { diff --git a/smartcontract/cli/src/accesspass/list.rs b/smartcontract/cli/src/accesspass/list.rs index c4f5f520f6..d4384a1363 100644 --- a/smartcontract/cli/src/accesspass/list.rs +++ b/smartcontract/cli/src/accesspass/list.rs @@ -277,22 +277,13 @@ impl ListAccessPassCliCommand { #[cfg(test)] mod tests { use crate::{accesspass::list::ListAccessPassCliCommand, tests::utils::create_test_client}; - use doublezero_cli_core::testing::cli_context_default_for_tests; + use doublezero_cli_core::testing::{block_on, cli_context_default_for_tests}; use doublezero_sdk::AccountType; use doublezero_serviceability::state::accesspass::{ AccessPass, AccessPassStatus, AccessPassType, IS_DYNAMIC, }; use solana_sdk::pubkey::Pubkey; use std::{collections::HashMap, net::Ipv4Addr}; - use tokio::runtime::Builder; - - fn block_on(f: F) -> F::Output { - Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(f) - } #[test] fn test_cli_accesspass_list() { diff --git a/smartcontract/cli/src/accesspass/set.rs b/smartcontract/cli/src/accesspass/set.rs index e6a97f7ef3..b4f40bb6e3 100644 --- a/smartcontract/cli/src/accesspass/set.rs +++ b/smartcontract/cli/src/accesspass/set.rs @@ -141,7 +141,7 @@ mod tests { requirements::{CHECK_BALANCE, CHECK_ID_JSON}, tests::utils::create_test_client, }; - use doublezero_cli_core::testing::cli_context_default_for_tests; + use doublezero_cli_core::testing::{block_on, cli_context_default_for_tests}; use doublezero_sdk::commands::accesspass::set::SetAccessPassCommand; use doublezero_serviceability::{ pda::{get_accesspass_pda, get_tenant_pda}, @@ -149,18 +149,9 @@ mod tests { }; use mockall::predicate; use solana_sdk::{pubkey::Pubkey, signature::Signature}; - use tokio::runtime::Builder; use super::*; - fn block_on(f: F) -> F::Output { - Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(f) - } - #[test] fn test_cli_accesspass_set_prepaid() { let mut client = create_test_client(); diff --git a/smartcontract/cli/src/config/get.rs b/smartcontract/cli/src/config/get.rs index b373f142cf..6330484d6a 100644 --- a/smartcontract/cli/src/config/get.rs +++ b/smartcontract/cli/src/config/get.rs @@ -45,9 +45,8 @@ impl GetConfigCliCommand { mod tests { use serial_test::serial; use tempfile::TempDir; - use tokio::runtime::Builder; - use doublezero_cli_core::testing::cli_context_default_for_tests; + use doublezero_cli_core::testing::{block_on, cli_context_default_for_tests}; use doublezero_sdk::{create_new_pubkey_user, write_doublezero_config, ClientConfig}; use crate::tests::utils::create_test_client; @@ -57,14 +56,6 @@ mod tests { const CONFIG_ENV_VAR: &str = "DOUBLEZERO_CONFIG_FILE"; - fn block_on(f: F) -> F::Output { - Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(f) - } - #[test] #[serial] fn test_cli_config_get() { diff --git a/smartcontract/cli/src/config/set.rs b/smartcontract/cli/src/config/set.rs index 462fd3f410..c63c4b164e 100644 --- a/smartcontract/cli/src/config/set.rs +++ b/smartcontract/cli/src/config/set.rs @@ -138,17 +138,8 @@ impl SetConfigCliCommand { #[cfg(test)] mod tests { - use doublezero_cli_core::testing::cli_context_default_for_tests; + use doublezero_cli_core::testing::{block_on, cli_context_default_for_tests}; use serial_test::serial; - use tokio::runtime::Builder; - - fn block_on(f: F) -> F::Output { - Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(f) - } use tempfile::TempDir; use doublezero_sdk::{create_new_pubkey_user, ClientConfig}; diff --git a/smartcontract/cli/src/resource/allocate.rs b/smartcontract/cli/src/resource/allocate.rs index 0c1b288fcb..a693b89312 100644 --- a/smartcontract/cli/src/resource/allocate.rs +++ b/smartcontract/cli/src/resource/allocate.rs @@ -82,20 +82,11 @@ impl AllocateResourceCliCommand { mod tests { use super::*; use crate::doublezerocommand::MockCliCommand; - use doublezero_cli_core::testing::cli_context_default_for_tests; + use doublezero_cli_core::testing::{block_on, cli_context_default_for_tests}; use doublezero_sdk::{Device, ResourceType as SdkResourceType}; use mockall::predicate::eq; use solana_sdk::{pubkey::Pubkey, signature::Signature}; use std::io::Cursor; - use tokio::runtime::Builder; - - fn block_on(f: F) -> F::Output { - Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(f) - } #[test] fn test_execute_success_dzprefixblock() { diff --git a/smartcontract/cli/src/resource/close.rs b/smartcontract/cli/src/resource/close.rs index dcea0ef2c6..1d19b50d87 100644 --- a/smartcontract/cli/src/resource/close.rs +++ b/smartcontract/cli/src/resource/close.rs @@ -54,22 +54,13 @@ impl CloseResourceCliCommand { mod tests { use super::*; use crate::tests::utils::create_test_client; - use doublezero_cli_core::testing::cli_context_default_for_tests; + use doublezero_cli_core::testing::{block_on, cli_context_default_for_tests}; use doublezero_sdk::{ commands::resource::{closeaccount::CloseResourceCommand, get::GetResourceCommand}, get_resource_extension_pda, AccountType, ResourceExtensionOwned, }; use mockall::predicate; use solana_sdk::{pubkey::Pubkey, signature::Signature}; - use tokio::runtime::Builder; - - fn block_on(f: F) -> F::Output { - Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(f) - } #[test] fn test_cli_resource_close() { diff --git a/smartcontract/cli/src/resource/create.rs b/smartcontract/cli/src/resource/create.rs index b9721a1a9a..8008aaa122 100644 --- a/smartcontract/cli/src/resource/create.rs +++ b/smartcontract/cli/src/resource/create.rs @@ -58,20 +58,11 @@ impl CreateResourceCliCommand { mod tests { use super::*; use crate::doublezerocommand::MockCliCommand; - use doublezero_cli_core::testing::cli_context_default_for_tests; + use doublezero_cli_core::testing::{block_on, cli_context_default_for_tests}; use doublezero_sdk::{Device, ResourceType as SdkResourceType}; use mockall::predicate::eq; use solana_sdk::{pubkey::Pubkey, signature::Signature}; use std::io::Cursor; - use tokio::runtime::Builder; - - fn block_on(f: F) -> F::Output { - Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(f) - } #[test] fn test_execute_success_dzprefixblock() { diff --git a/smartcontract/cli/src/resource/deallocate.rs b/smartcontract/cli/src/resource/deallocate.rs index d0b8b8872a..d4e53cf016 100644 --- a/smartcontract/cli/src/resource/deallocate.rs +++ b/smartcontract/cli/src/resource/deallocate.rs @@ -84,20 +84,11 @@ impl DeallocateResourceCliCommand { mod tests { use super::*; use crate::doublezerocommand::MockCliCommand; - use doublezero_cli_core::testing::cli_context_default_for_tests; + use doublezero_cli_core::testing::{block_on, cli_context_default_for_tests}; use doublezero_sdk::{Device, ResourceType as SdkResourceType}; use mockall::predicate::eq; use solana_sdk::{pubkey::Pubkey, signature::Signature}; use std::io::Cursor; - use tokio::runtime::Builder; - - fn block_on(f: F) -> F::Output { - Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(f) - } #[test] fn test_execute_success_dzprefixblock() { diff --git a/smartcontract/cli/src/resource/get.rs b/smartcontract/cli/src/resource/get.rs index db964b0c4e..b71e144157 100644 --- a/smartcontract/cli/src/resource/get.rs +++ b/smartcontract/cli/src/resource/get.rs @@ -74,7 +74,7 @@ impl GetResourceCliCommand { mod tests { use super::*; use crate::doublezerocommand::MockCliCommand; - use doublezero_cli_core::testing::cli_context_default_for_tests; + use doublezero_cli_core::testing::{block_on, cli_context_default_for_tests}; use doublezero_sdk::{AccountType, ResourceType as SdkResourceType}; use doublezero_serviceability::{ id_allocator::IdAllocator, @@ -82,15 +82,6 @@ mod tests { }; use solana_program::pubkey::Pubkey; use std::io::Cursor; - use tokio::runtime::Builder; - - fn block_on(f: F) -> F::Output { - Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(f) - } #[test] fn test_from_cli_to_command() { diff --git a/smartcontract/cli/src/resource/verify.rs b/smartcontract/cli/src/resource/verify.rs index 9727e26788..d1b2ac3942 100644 --- a/smartcontract/cli/src/resource/verify.rs +++ b/smartcontract/cli/src/resource/verify.rs @@ -1198,7 +1198,7 @@ fn check_discrepancies( mod tests { use super::*; use crate::doublezerocommand::MockCliCommand; - use doublezero_cli_core::testing::cli_context_default_for_tests; + use doublezero_cli_core::testing::{block_on, cli_context_default_for_tests}; use doublezero_program_common::types::NetworkV4; use doublezero_sdk::AccountType; use doublezero_serviceability::{ @@ -1207,15 +1207,6 @@ mod tests { state::resource_extension::{Allocator, ResourceExtensionOwned}, }; use std::io::Cursor; - use tokio::runtime::Builder; - - fn block_on(f: F) -> F::Output { - Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(f) - } fn create_resource_extension_ip( program_id: &Pubkey,