3

I'm trying to test a Custom RPC. Let's make it a simple pass through.

p_api::decl_runtime_apis! {
    pub trait SimpleApi<AccountId> where
        AccountId: Codec,
    {
        fn is_true() -> Result<bool, DispatchError>;
    }
}

Client Server Trait

#[rpc(client, server)]
pub trait SimpleApi<BlockHash, AccountId> {
    #[method(name = "simple_isTrue")]
    fn is_true(
        &self,
    ) -> RpcResult<bool>>;
}

pub struct SimpleHandler<C, M> {
    client: Arc<C>,
    _marker: std::marker::PhantomData<M>,
}

impl<C, M> SimpleHandler<C, M> {
    pub fn new(client: Arc<C>) -> Self {
        Self { client, _marker: Default::default() }
    }
}

#[async_trait]
impl<C, Block, AccountId> SimpleServer<<Block as BlockT>::Hash, AccountId> for SimpleHandler<C, Block>
where
    Block: BlockT,
    C: Send + Sync + 'static,
    C: ProvideRuntimeApi<Block>,
    C: HeaderBackend<Block>,
    C::Api: SimpleRuntimeApi<Block, AccountId>,
    AccountId: Codec,
{
  fn is_true(&self) -> RpcResult<bool> {
    let api = self.client.runtime_api();
    let at = BlockId::hash(self.client.info().best_hash);
    let runtime_api_result = api.is_true(&at);
    map_rpc_result(runtime_api_result)
  }
}

So following the test setup from https://github.com/paritytech/substrate/blob/master/client/rpc/src/author/tests.rs

use substrate_test_runtime_client::{
    runtime::Block, Client, Backend, DefaultTestClientBuilderExt,
};

struct TestSetup {
    pub client: Arc<Client<Backend>>
}

impl Default for TestSetup {
    fn default() -> Self {
        let client_builder = substrate_test_runtime_client::TestClientBuilder::new();
        let client = Arc::new(client_builder.build());

        TestSetup { client }
    }
}

impl TestSetup {
    fn simple(&self) -> SimpleHandler<Client<Backend>,Block> {
        SimpleHandler {
            client: self.client.clone(),
            _marker: Default::default(),
        }
    }

    fn into_rpc() -> RpcModule<SimpleHandler<Client<Backend>, Block>> {
        Self::default().simple().into_rpc()
    }
}

#[tokio::test]
async fn testing_simple() {
    let api = TestSetup::default().simple();

    let rpc = api.into_rpc();

    let response = rpc.call("").await.unwrap();
}

This fails with:

let rpc = api.into_rpc();
         ^^^^^^^^ method cannot be called on `SimpleHandler<...>` due to unsatisfied trait bounds
// ...
C::Api: SimpleRuntimeApi<Block, AccountId>,
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound introduced here

Which makes since, as nothing in the test setup has an implementation for SimpleRuntimeApi. So how do I mock one and tell the client?

My assumption is that I am missing part of the setup for substrate_test_runtime_client (Or perhaps I should actually be using substrate_test_client ?) but the examples around it are few. Always possible I'm barking up the wrong tree entirely.

1 Answer 1

3

tldr: used the sp_api::mock_impl_runtime_apis! macro to mock the implementation.

Example is in this PR along with a lot of other changes:

https://github.com/LibertyDSNP/frequency/pull/490

See pallets/messages/src/rpc/src/lib.rs for the rpc, pallets/messages/src/rpc/src/tests/mod.rs for the test, and pallets/messages/src/rpc/src/tests/rpc_mock.rs for the general mock setup.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.