Skip to content
This repository has been archived by the owner on Aug 30, 2022. It is now read-only.

Mock the aggregator RPC client #346

Closed
wants to merge 1 commit into from
Closed

Conversation

little-dude
Copy link
Contributor

@little-dude little-dude commented Mar 25, 2020

Description

The main tasks, ie that tasks that implement most of the business logic are aggregator::Service for the aggregator and coordinator::Service for the coordinator. This is where we'll focus our effort for testing at the beginning. In order to test these two Service, we need to control their inputs and outputs.

Let's focus on the coordinator, since the situation is pretty similar for the aggregator anyway. The question is: what are the sources of IO. We have:

  • an RPC server that receives requests from the network and sends back responses
  • an RPC client that sends requests to the network and receives responses back
  • an REST API that receives requests from the network, and sends back responses

However, two of these are not direct sources of the Service. The API and RPC server layers are already isolated and run in their own task. They communicate with the Service via a Handle as illustrated below:

                                           +-------------+
                                           |     API     |
                                           | +------+    |
    +---------------------------+      +----->Handle|    <---+
    |           Service         |      |   | +------+    |   |
    | +----------+   +--------+ |      |   +-------------+   |
  +--->RPC client|   |Receiver<--------+   +-------------+   |
  | | +----------+   +--------+ |      |   |  RPC server |   |
  | +---------------------------+      |   | +------+    |   |
  |                                    +----->Handle|    <---+
  |                                        | +------+    |   |
  |                                        +-------------+   |
  |                                                          |
  |                   +---------+                            |
  |                   |         |                            |
  +-------------------> Network <----------------------------+
                      |         |
                      +---------+

We already control these handles and we can use them to send requests to the service. Thus, there is no need to mock the API and RPC server and our system under test can already be simplified:

                                           +------+
  +---------------------------+      +----->Handle|
  |           Service         |      |     +------+
  | +----------+   +--------+ |      |
+--->RPC client|   |Receiver<--------+
| | +----------+   +--------+ |      |
| +---------------------------+      |     +------+
|                                    +----->Handle|
|                                          +------+
|
|
|                   +---------+
|                   |         |
+-------------------> Network |
                    |         |
                    +---------+

The RPC client is the only remaining piece, and the idea of the PR is to mock it away so that our system under test is finally fully under our control:

                                         +------+
+---------------------------+      +----->Handle|
|           Service         |      |     +------+
| +----------+   +--------+ |      |
| |  Mock    |   |Receiver<--------+
| +----------+   +--------+ |      |
+---------------------------+      |     +------+
                                   +----->Handle|
                                         +------+

Implementation

For the mocking itself, we use the mockall crate. The idea is to leverage rust's conditional compilation to replace the rpc::Client by our mock when running cargo test. For this, we use the #[cfg(test)] and #[cfg(not(test))] attributes.

The mock itself doesn't expose the exact same API than the rpc::Client: the types in some of the function signatures are slightly simpler. That's works well in Rust because most APIs are designed around traits.

For instance the real rpc::Client::select method looks like this:

pub fn select(
    &mut self,
    ctx: Context,
    credentials: Credentials
) -> impl Future<Output = Result<Result<(), ()>>> + '_

Thus, the return type is _some type that implements Future<Output=io::Result<Result<(), ()>>>, and an anonymous lifetime (the '_ part). But in our mock, the return type is an actual concrete type:

fn select(&mut self, ctx: Context, credentials: Credentials) -> future::Ready<io::Result<Result<(), ()>>>;

It works, because that concrete type satisfies the Future<Output = Result<Result<(), ()>>> + '_ trait bound.

@little-dude little-dude force-pushed the PB-564-mock-rpc-client branch 2 times, most recently from 8413c1d to 1c517fc Compare March 25, 2020 17:20
@little-dude little-dude changed the title Mock the RPC clients Mock the aggregator RPC client Mar 25, 2020
@little-dude little-dude added the good first issue Good for newcomers label Mar 26, 2020
Copy link
Contributor

@janpetschexain janpetschexain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks a lot for the great diagram and explanations! :) it helped a lot to get the idea.

@little-dude little-dude force-pushed the PB-564-mock-rpc-client branch from 1c517fc to 406ba68 Compare March 26, 2020 13:01
@little-dude
Copy link
Contributor Author

Closing in favor of #352

@little-dude little-dude deleted the PB-564-mock-rpc-client branch April 9, 2020 12:57
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
good first issue Good for newcomers
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants