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

Implementing all messages handling in mutlitest App #398

Merged
merged 5 commits into from
Sep 7, 2021
Merged

Conversation

hashedone
Copy link
Contributor

Closes #388

@hashedone hashedone marked this pull request as draft September 1, 2021 11:44
@hashedone hashedone marked this pull request as ready for review September 1, 2021 16:15
Copy link
Contributor

@maurolacy maurolacy left a comment

Choose a reason for hiding this comment

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

Looks good.

Handling this into two separate MRs (builder pattern for App, and custom messages handling) would have made reviewing easy for me.

/// Also `ExecC` is the custom message which is handled by custom message handler.
///
/// `QueryC` is custom query message handled by custom message handler.
pub struct App<ExecC = Empty, QueryC = Empty>
Copy link
Contributor

Choose a reason for hiding this comment

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

Just a detail, but why not naming these E, Q?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am basically enemy of single letter names, they are semanticless. Sometimes in very obvious cases T makes its jobs, but besides I find it wasting more time reading, that saving writing.

let bank = BankKeeper::new();

App::new(api, env.block, bank, MockStorage::new())
AppBuilder::new().build()
Copy link
Contributor

Choose a reason for hiding this comment

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

👍🏼


#[derive(Debug, Clone, Serialize, Deserialize, Derivative)]
#[derivative(Default(bound = "", new = "true"))]
pub struct Message<ExecC>
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pub struct Message<ExecC>
pub struct Message<ExecC = Empty>

? (not sure that's possible / useful).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is possible for sure and I think it is even used. Empty message is still a message, good enough for testing purposes.

packages/multi-test/src/wasm.rs Outdated Show resolved Hide resolved
@hashedone
Copy link
Contributor Author

Yea, it went bigger than I expected :/ I wanted to start with all other messages (stargate and so), but I decided that I will wait for this to be reviewed as it would be too much spam.

Copy link
Member

@ethanfrey ethanfrey left a comment

Choose a reason for hiding this comment

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

In general, this looks good. I like the builder pattern and thanks for adding some Custom support.

I am rather strongly against using mockall here, as we will not need that in any real usage of multitest (a proper mock object should be provided by one chain). I support a minimal implementation of a custom handler (without mockall) that is just pub(crate) and used for the internal tests.

Each blockchain can provide a proper implementation along with the bindings where they define CosmosMsg and QueryRequest extensions.


/// Simplified version of `CustomHandler` having only arguments which are not app internals - they
/// are just discarded. Usefull for simpler mocking.
#[automock(type QueryResult = Binary;)]
Copy link
Member

Choose a reason for hiding this comment

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

I would feel happier removing this and just implementing the 2 needed methods, then mockall can be a dev-dependency only.

Copy link
Member

Choose a reason for hiding this comment

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

In reality, we will likely have much larger blockchain specific logic in the custom handlers and just applying mocks here doesn't make too much sense usually.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Basically this is the idea - specific blockchain impl just adds its own implementation. Auto-generated mock is just to make internal testing easier, but I will get rid of this in this place.

self
}

pub fn build(self) -> App<ExecC, QueryC> {
Copy link
Member

Choose a reason for hiding this comment

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

Nice way to provide defaults that can be overridden

/// Overwrites default wasm executor. Panic if already set.
#[track_caller]
pub fn with_wasm(mut self, wasm: impl Wasm<ExecC, QueryC> + 'static) -> Self {
assert!(self.wasm.is_none(), "Wasm executor already overwritten");
Copy link
Member

Choose a reason for hiding this comment

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

We don't allow overriding it twice? I guess no need, but seems odd to enforce.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you overwrite something twice in your builder then you made some mistake. Better to catch early.

pub wasm: Box<dyn Wasm<C>>,
pub bank: Box<dyn Bank>,
pub struct Router<ExecC, QueryC> {
pub(crate) wasm: Box<dyn Wasm<ExecC, QueryC>>,
Copy link
Member

Choose a reason for hiding this comment

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

I would actually leave these public. But that can be a different PR when there is a need.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why? Any reason why user of this crate should in any point get access to internals of structure?

Copy link
Member

@ethanfrey ethanfrey Sep 6, 2021

Choose a reason for hiding this comment

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

Not now, I have an idea, but the need will show up in a future PR, and we can discuss there.
Out of scope here. For now pub(crate) is good.

fn query(&self, block: &BlockInfo, msg: QueryC) -> AnyResult<Binary>;
}

impl<ExecC, QueryC, T: SimpleCustomHandler<ExecC, QueryC>> CustomHandler<ExecC, QueryC> for T {
Copy link
Member

Choose a reason for hiding this comment

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

I would just have this implementation return error (so you can test it works). For any real usage, we will have time to build a module to define the semantics of this. We will want to mock out the real blockchain behaviour, so I would tend against one-off mocks.

Copy link
Member

Choose a reason for hiding this comment

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

Or maybe a FailingCustomHandler, which returns a pre-defined error (set in new?)
And SuccessCustomHandler, which returns a fixed response (set in new?)

One or both just to help in your testing in other packages, but those don't even have to be public outside of this crate.

/// code is in-memory lookup that stands in for wasm code
/// this can only be edited on the WasmRouter, and just read in caches
codes: HashMap<usize, Box<dyn Contract<C>>>,
codes: HashMap<usize, Box<dyn Contract<ExecC>>>,
/// Just marker to make type elision fork when using it as `Wasm` trait
Copy link
Member

Choose a reason for hiding this comment

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

Actually, the contract should have QueryC in it's type.

Update: I looked at this code (also in cosmwasm-std) and it seems the query types are not enforced in DepsMut (which is where they are used). This is proper here, but maybe we can look at making cosmwasm-std more type safe

(TODO: @ethanfrey make an issue on cosmwasm-std)

Copy link
Member

@ethanfrey ethanfrey left a comment

Choose a reason for hiding this comment

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

Looks better.
I would still hide some more types (make them pub(crate)) to ensure/signal they are only for internal testing of multi-test

/// is implemented, custom messages should not be send.
pub struct PanickingCustomHandler;

impl<ExecC, QueryC> SimpleCustomHandler<ExecC, QueryC> for PanickingCustomHandler
Copy link
Member

Choose a reason for hiding this comment

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

Why even have the SimpleCustomHandler trait? Why not just implement CustomHandler directly?

Also, I think CustomHandler is the only type in this file that should be pub (rather than pub(crate)). These other types are only meaningful for us to test multi-test itself (not for blockchains that actually use custom types)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea, probably in contract we should not use in contracts tests. SimpleCustomHandler is here to make implementations kind of simpler as I assume that app is somehow accessible on the side, but it may be an overkill.

@@ -1594,6 +1582,13 @@ mod test {
&[],
)
.unwrap();

assert_eq!(
Copy link
Member

Choose a reason for hiding this comment

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

This looks nice.

@hashedone hashedone merged commit a918a0f into main Sep 7, 2021
@hashedone hashedone deleted the mt-msg-handling branch September 7, 2021 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Use builder pattern for App init
3 participants