1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
use std::collections::BTreeMap;
use parity_scale_codec::{Decode, Encode};
use crate::{
errors::LangError,
mock::{error::MockingError, MockedCallResult},
};
/// Alias for a 4-byte selector.
pub type Selector = [u8; 4];
/// An untyped message mock.
///
/// Notice that in the end, we cannot operate on specific argument/return types. Rust won't let us
/// have a collection of differently typed closures. Fortunately, we can assume that all types are
/// en/decodable, so we can use `Vec<u8>` as a common denominator.
pub type MessageMock = Box<dyn Fn(Vec<u8>) -> MockedCallResult + Send + Sync>;
/// A contract mock.
pub struct ContractMock {
messages: BTreeMap<Selector, MessageMock>,
}
impl ContractMock {
/// Creates a new mock without any message.
pub fn new() -> Self {
Self {
messages: BTreeMap::new(),
}
}
/// Adds a message mock.
pub fn with_message(mut self, selector: Selector, message: MessageMock) -> Self {
self.messages.insert(selector, message);
self
}
/// Try to call a message mock. Returns an error if there is no message mock for `selector`.
pub fn call(&self, selector: Selector, input: Vec<u8>) -> MockedCallResult {
match self.messages.get(&selector) {
None => Err(MockingError::MessageNotFound(selector)),
Some(message) => message(input),
}
}
}
impl Default for ContractMock {
fn default() -> Self {
Self::new()
}
}
/// A helper function to create a message mock out of a typed closure.
///
/// In particular, it takes care of decoding the input and encoding the output. Also, wraps the
/// return value in a `Result`, which is normally done implicitly by ink!.
pub fn mock_message<Args: Decode, Ret: Encode, Body: Fn(Args) -> Ret + Send + Sync + 'static>(
body: Body,
) -> MessageMock {
Box::new(move |encoded_input| {
let input = Decode::decode(&mut &*encoded_input).map_err(MockingError::ArgumentDecoding)?;
Ok(Ok::<Ret, LangError>(body(input)).encode())
})
}