Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal Rosetta Testing Framework #158

Closed
Lohann opened this issue Aug 9, 2023 · 1 comment · Fixed by #211
Closed

Proposal Rosetta Testing Framework #158

Lohann opened this issue Aug 9, 2023 · 1 comment · Fixed by #211
Assignees

Comments

@Lohann
Copy link
Collaborator

Lohann commented Aug 9, 2023

I would like to propose deprecate the rosetta-docker and replace it by a more generic testing framework.

Issues of the current approach:

  • Starting a new node for each test is slow
  • doesn’t supporting all environments
  • Doesn't work for chains that have complex multi-stage setup, like Polygon.
  • Test arguments are static and immutable in the chain-config (cannot dynamically change node args).
  • You must use Docker (what if you prefer to run the chain locally?)

Proposal

Instead of start a new client for each test (which is slow and expensive), use the same rpc-node and connector for all tests, and just do the necessary setup before each test to assure it runs in a predictable way, ex: create a new set of accounts for each test.

1 - Refactor the BlockchainConfig to be a trait instead a struct. Also remove all testing related stuff like node_command, node_image, etc.
2 - Create an agnostic trait for setup the tests

trait TestSupport {
    /// Arguments the user provides for initialize the test context
    type Args;
    
    // Represents the test context, may include faucet accounts, smart-contract addresses, etc.
    type Context;

    /// Get parameters to construct a `Env` suitable for testing.
    ///
    /// - `env_name`: test unique identifier, is equals to: concat!(module_path!(), "::", stringify!(#test_name))
    /// - `config`: The parameters necessary for setup the test
    fn test_context(env_name: &str, config: Self::Args) -> BoxFuture<'_, Self::Context>;

    /// Do the necessary cleanup after the test runs, ex: stop docker containers, delete accounts, etc.
    ///
    /// - `env_name`: test unique identifier, is equals to: concat!(module_path!(), "::", stringify!(#test_name))
    fn cleanup_test(env_name: &str) -> BoxFuture<'_, Result<(), Error>>;
}

and then implement it in the chain config crate, example:

pub struct AstarTestEnv {
    ephemeral_wallets: [Wallet; 10],
    client: AstarClient,
}

#[async_trait]
impl TestSupport for AstarTestEnv {
    type Args = BlockchainConfig;
    type Context = Self;
    
    async fn test_context(env_name: &str, config: BlockchainConfig) -> Self {
      // Setup falcet accounts, move fund, etc..
    }
    
    async fn cleanup_test(env_name: &str) {
      // Cleanup the env if necessary (stop docker node, delete account, etc..)
    }
}

3 - Start the RPC nodes before running the unit tests.
4 - Use test_context for create a new set of accounts for each test
5 - Create a macro similar to what sqlx does here and test_context for the unit tests, ex:

use rosetta_config_astar::{
    config as astar_config,
    AstarTestEnv,
};

#[rosetta_client::test(config = astar_config("dev"), ctx = AstarTestEnv)]
async fn task_executor_astar_sc_call(env: AstarTestEnv) -> anyhow::Result<()> {
    // ... test body here
}

the output of the macro above is:

#[tokio::test]
async fn task_executor_astar_sc_call(env: AstarTestEnv::TestArgs) -> anyhow::Result<()> {
    async fn inner(env: AstarTestEnv) -> anyhow::Result<()> {
        // ... test body here
    }
    let env_name = concat!(module_path!(), "::", stringify!(task_executor_astar_sc_call));
    
    // Create the test context
    let ctx = <AstarTestEnv as ::rosetta_core::test::TestSupport>::test_context(env_name, astar_config("dev")).await;
    
    // Run the test in a new thread, so we can perform cleanup when it panics
    let result = tokio::spawn(async {
        inner(ctx).await
    }).await;
    
    // cleanup
    <AstarTestEnv as ::rosetta_core::test::TestSupport>::cleanup_test(env_name).await;
    
    // Now is safe to panic
    if let Err(err) = result {
        // Resume the panic on the main task
        panic::resume_unwind(err.into_panic());
    }
}
@Lohann
Copy link
Collaborator Author

Lohann commented Aug 9, 2023

This proposal is inspired by SQLX approach:
https://github.com/launchbadge/sqlx/blob/v0.7.1/sqlx-core/src/testing/mod.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants