1+ // Copyright 2019-2025 Parity Technologies (UK) Ltd.
2+ // This file is dual-licensed as Apache-2.0 or GPL-3.0.
3+ // see LICENSE for license details.
4+
5+ //! This module exposes a [`MockRpcClient`], which is useful for testing.
6+
7+ use super :: { RpcClientT , RawRpcFuture , RawRpcSubscription } ;
8+ use crate :: Error ;
9+ use core:: future:: Future ;
10+ use serde_json:: value:: RawValue ;
11+
12+ type MethodHandlerFn = Box < dyn Fn ( & str , Option < Box < serde_json:: value:: RawValue > > ) -> RawRpcFuture < ' static , Box < RawValue > > + Send + Sync + ' static > ;
13+ type SubscriptionHandlerFn = Box < dyn Fn ( & str , Option < Box < serde_json:: value:: RawValue > > , & str ) -> RawRpcFuture < ' static , RawRpcSubscription > + Send + Sync + ' static > ;
14+
15+ /// A mock RPC client that responds programmatically to requests.
16+ /// Useful for testing.
17+ pub struct MockRpcClient {
18+ method_handler : MethodHandlerFn ,
19+ subscription_handler : SubscriptionHandlerFn
20+ }
21+
22+ impl MockRpcClient {
23+ /// Create a [`MockRpcClient`] by providing a function to handle method calls
24+ /// and a function to handle subscription calls.
25+ pub fn from_handlers < M , S , MA , SA > ( method_handler : M , subscription_handler : S ) -> MockRpcClient
26+ where
27+ M : IntoMethodHandler < MA > ,
28+ S : IntoSubscriptionHandler < SA > ,
29+ {
30+ MockRpcClient {
31+ method_handler : method_handler. into_method_handler ( ) ,
32+ subscription_handler : subscription_handler. into_subscription_handler ( )
33+ }
34+ }
35+ }
36+
37+ impl RpcClientT for MockRpcClient {
38+ fn request_raw < ' a > (
39+ & ' a self ,
40+ method : & ' a str ,
41+ params : Option < Box < serde_json:: value:: RawValue > > ,
42+ ) -> RawRpcFuture < ' a , Box < serde_json:: value:: RawValue > > {
43+ ( self . method_handler ) ( method, params)
44+ }
45+
46+ fn subscribe_raw < ' a > (
47+ & ' a self ,
48+ sub : & ' a str ,
49+ params : Option < Box < serde_json:: value:: RawValue > > ,
50+ unsub : & ' a str ,
51+ ) -> RawRpcFuture < ' a , RawRpcSubscription > {
52+ ( self . subscription_handler ) ( sub, params, unsub)
53+ }
54+ }
55+
56+ /// Return responses wrapped in this to have them serialized to JSON.
57+ pub struct Json < T > ( T ) ;
58+
59+ /// Anything that can be converted into a valid handler response implements this.
60+ pub trait IntoHandlerResponse {
61+ /// Convert self into a handler response.
62+ fn into_handler_response ( self ) -> Result < Box < RawValue > , Error > ;
63+ }
64+
65+ impl < T : serde:: Serialize > IntoHandlerResponse for Result < T , Error > {
66+ fn into_handler_response ( self ) -> Result < Box < RawValue > , Error > {
67+ self . and_then ( |val| serialize_to_raw_value ( & val) )
68+ }
69+ }
70+
71+ impl IntoHandlerResponse for Box < RawValue > {
72+ fn into_handler_response ( self ) -> Result < Box < RawValue > , Error > {
73+ Ok ( self )
74+ }
75+ }
76+
77+ impl IntoHandlerResponse for serde_json:: Value {
78+ fn into_handler_response ( self ) -> Result < Box < RawValue > , Error > {
79+ serialize_to_raw_value ( & self )
80+ }
81+ }
82+
83+ impl < T : serde:: Serialize > IntoHandlerResponse for Json < T > {
84+ fn into_handler_response ( self ) -> Result < Box < RawValue > , Error > {
85+ serialize_to_raw_value ( & self . 0 )
86+ }
87+ }
88+
89+ fn serialize_to_raw_value < T : serde:: Serialize > ( val : & T ) -> Result < Box < RawValue > , Error > {
90+ let res = serde_json:: to_string ( val) . map_err ( Error :: Deserialization ) ?;
91+ let raw_value = RawValue :: from_string ( res) . map_err ( Error :: Deserialization ) ?;
92+ Ok ( raw_value)
93+ }
94+
95+ /// Anything that is a valid method handler implements this trait.
96+ pub trait IntoMethodHandler < A > {
97+ /// Convert self into a method handler function.
98+ fn into_method_handler ( self ) -> MethodHandlerFn ;
99+ }
100+
101+ enum SyncMethodHandler { }
102+ impl < F , R > IntoMethodHandler < SyncMethodHandler > for F
103+ where
104+ F : Fn ( & str , Option < Box < serde_json:: value:: RawValue > > ) -> R + Send + Sync + ' static ,
105+ R : IntoHandlerResponse + Send + ' static ,
106+ {
107+ fn into_method_handler ( self ) -> MethodHandlerFn {
108+ Box :: new ( move |method : & str , params : Option < Box < serde_json:: value:: RawValue > > | {
109+ let res = self ( method, params) ;
110+ Box :: pin ( async move { res. into_handler_response ( ) } )
111+ } )
112+ }
113+ }
114+
115+ enum AsyncMethodHandler { }
116+ impl < F , Fut , R > IntoMethodHandler < AsyncMethodHandler > for F
117+ where
118+ F : Fn ( & str , Option < Box < serde_json:: value:: RawValue > > ) -> Fut + Send + Sync + ' static ,
119+ Fut : Future < Output = R > + Send + ' static ,
120+ R : IntoHandlerResponse + Send + ' static ,
121+ {
122+ fn into_method_handler ( self ) -> MethodHandlerFn {
123+ Box :: new ( move |method : & str , params : Option < Box < serde_json:: value:: RawValue > > | {
124+ let fut = self ( method, params) ;
125+ Box :: pin ( async move { fut. await . into_handler_response ( ) } )
126+ } )
127+ }
128+ }
129+
130+ /// Anything that is a valid subscription handler implements this trait.
131+ pub trait IntoSubscriptionHandler < A > {
132+ /// Convert self into a subscription handler function.
133+ fn into_subscription_handler ( self ) -> SubscriptionHandlerFn ;
134+ }
135+
136+ enum SyncSubscriptionHandler { }
137+ impl < F , R > IntoMethodHandler < SyncMethodHandler > for F
138+ where
139+ F : Fn ( & str , Option < Box < serde_json:: value:: RawValue > > ) -> R + Send + Sync + ' static ,
140+ R : IntoHandlerResponse + Send + ' static ,
141+ {
142+ fn into_method_handler ( self ) -> MethodHandlerFn {
143+ Box :: new ( move |method : & str , params : Option < Box < serde_json:: value:: RawValue > > | {
144+ let res = self ( method, params) ;
145+ Box :: pin ( async move { res. into_handler_response ( ) } )
146+ } )
147+ }
148+ }
0 commit comments