@@ -6,26 +6,48 @@ use crate::{
6
6
Error ,
7
7
} ;
8
8
use async_trait:: async_trait;
9
- use dashmap:: { mapref:: entry:: Entry , DashMap } ;
10
9
11
- use once_cell:: sync:: Lazy ;
12
10
use primitives:: {
13
- Address , Chain , ChainId , ChainOf , Channel , ChannelId , ToETHChecksum , ValidatorId ,
11
+ config :: ChainInfo , Address , ChainId , ChainOf , Channel , ToETHChecksum , ValidatorId ,
14
12
} ;
15
- use std:: { collections:: HashMap , sync:: Arc } ;
13
+ use std:: collections:: HashMap ;
14
+
15
+ #[ doc( inline) ]
16
+ pub use self :: deposit:: Deposits ;
16
17
17
18
pub type Adapter < S > = crate :: Adapter < Dummy , S > ;
18
19
19
- /// The Dummy Chain to be used with this adapter
20
- /// The Chain is not applicable to the adapter, however, it is required for
21
- /// applications because of the `authentication` & [`Channel`] interactions.
22
- pub static DUMMY_CHAIN : Lazy < Chain > = Lazy :: new ( || Chain {
23
- chain_id : ChainId :: new ( 1 ) ,
24
- rpc : "http://dummy.com" . parse ( ) . expect ( "Should parse ApiUrl" ) ,
25
- outpace : "0x0000000000000000000000000000000000000000"
26
- . parse ( )
27
- . unwrap ( ) ,
28
- } ) ;
20
+ #[ cfg( feature = "test-util" ) ]
21
+ #[ cfg_attr( docsrs, doc( cfg( feature = "test-util" ) ) ) ]
22
+ pub mod test_util {
23
+ use once_cell:: sync:: Lazy ;
24
+ use primitives:: {
25
+ config:: { ChainInfo , TokenInfo , DUMMY_CONFIG } ,
26
+ Chain ,
27
+ } ;
28
+ pub static DUMMY_TOKEN : Lazy < TokenInfo > =
29
+ Lazy :: new ( || DUMMY_CONFIG . chains [ "Dummy" ] . tokens [ "DUMMY" ] . clone ( ) ) ;
30
+
31
+ pub static DUMMY_CHAIN_INFO : Lazy < ChainInfo > =
32
+ Lazy :: new ( || DUMMY_CONFIG . chains [ "Dummy" ] . clone ( ) ) ;
33
+
34
+ /// The Dummy Chain to be used with this adapter
35
+ /// The Chain is not applicable to the adapter, however, it is required for
36
+ /// applications because of the `authentication` & [`Channel`] interactions.
37
+ pub static DUMMY_CHAIN : Lazy < Chain > = Lazy :: new ( || DUMMY_CHAIN_INFO . chain . clone ( ) ) ;
38
+ }
39
+
40
+ #[ derive( Debug , Clone ) ]
41
+ pub struct Options {
42
+ /// The identity used for the Adapter.
43
+ pub dummy_identity : ValidatorId ,
44
+ /// The authentication tokens that will be used by the adapter
45
+ /// for returning & validating authentication tokens of requests.
46
+ pub dummy_auth_tokens : HashMap < Address , String > ,
47
+ /// The [`ChainInfo`] that will be used for the [`Session`]s and
48
+ /// also for the deposits.
49
+ pub dummy_chain : ChainInfo ,
50
+ }
29
51
30
52
/// Dummy adapter implementation intended for testing.
31
53
#[ derive( Debug , Clone ) ]
@@ -34,63 +56,51 @@ pub struct Dummy {
34
56
identity : ValidatorId ,
35
57
/// Static authentication tokens (address => token)
36
58
authorization_tokens : HashMap < Address , String > ,
59
+ chain_info : ChainInfo ,
37
60
deposits : Deposits ,
38
61
}
39
62
40
- pub struct Options {
41
- pub dummy_identity : ValidatorId ,
42
- pub dummy_auth_tokens : HashMap < Address , String > ,
43
- }
44
-
45
- #[ derive( Debug , Clone , Default ) ]
46
- #[ allow( clippy:: type_complexity) ]
47
- pub struct Deposits ( Arc < DashMap < ( ChannelId , Address ) , ( usize , Vec < Deposit > ) > > ) ;
48
-
49
- impl Deposits {
50
- pub fn add_deposit ( & self , channel : ChannelId , address : Address , deposit : Deposit ) {
51
- match self . 0 . entry ( ( channel, address) ) {
52
- Entry :: Occupied ( mut deposit_calls) => {
53
- // add the new deposit to the Vec
54
- deposit_calls. get_mut ( ) . 1 . push ( deposit) ;
55
- }
56
- Entry :: Vacant ( empty) => {
57
- // add the new `(ChannelId, Address)` key and init with index 0 and the passed Deposit
58
- empty. insert ( ( 0 , vec ! [ deposit] ) ) ;
59
- }
60
- }
61
- }
62
-
63
- pub fn get_next_deposit ( & self , channel : ChannelId , address : Address ) -> Option < Deposit > {
64
- match self . 0 . entry ( ( channel, address) ) {
65
- Entry :: Occupied ( mut entry) => {
66
- let ( call_index, deposit_calls) = entry. get_mut ( ) ;
67
-
68
- let deposit = deposit_calls. get ( * call_index) . cloned ( ) ?;
69
-
70
- // increment the index for the next call
71
- * call_index = call_index
72
- . checked_add ( 1 )
73
- . expect ( "Deposit call index has overflowed" ) ;
74
- Some ( deposit)
75
- }
76
- Entry :: Vacant ( _) => None ,
77
- }
78
- }
79
- }
80
-
81
63
impl Dummy {
82
64
pub fn init ( opts : Options ) -> Self {
83
65
Self {
84
66
identity : opts. dummy_identity ,
85
67
authorization_tokens : opts. dummy_auth_tokens ,
68
+ chain_info : opts. dummy_chain ,
86
69
deposits : Default :: default ( ) ,
87
70
}
88
71
}
89
72
90
- pub fn add_deposit_call ( & self , channel : ChannelId , address : Address , deposit : Deposit ) {
91
- self . deposits . add_deposit ( channel, address, deposit)
73
+ /// Set the deposit that you want the adapter to return every time
74
+ /// when the [`get_deposit()`](Locked::get_deposit) get's called
75
+ /// for the give [`ChannelId`] and [`Address`].
76
+ ///
77
+ /// If [`Deposit`] is set to [`None`], it remove the mocked deposit.
78
+ ///
79
+ /// # Panics
80
+ ///
81
+ /// When [`None`] is passed but there was no mocked deposit.
82
+ pub fn set_deposit < D : Into < Option < Deposit > > > (
83
+ & self ,
84
+ channel_context : & ChainOf < Channel > ,
85
+ depositor : Address ,
86
+ deposit : D ,
87
+ ) {
88
+ use deposit:: Key ;
89
+
90
+ let key = Key :: from_chain_of ( channel_context, depositor) ;
91
+ match deposit. into ( ) {
92
+ Some ( deposit) => {
93
+ self . deposits . 0 . insert ( key, deposit) ;
94
+ }
95
+ None => {
96
+ self . deposits . 0 . remove ( & key) . unwrap_or_else ( || {
97
+ panic ! ( "Couldn't remove a deposit which doesn't exist for {key:?}" )
98
+ } ) ;
99
+ }
100
+ } ;
92
101
}
93
102
}
103
+
94
104
#[ async_trait]
95
105
impl Locked for Dummy {
96
106
type Error = Error ;
@@ -131,7 +141,7 @@ impl Locked for Dummy {
131
141
Some ( ( address, _token) ) => Ok ( Session {
132
142
uid : * address,
133
143
era : 0 ,
134
- chain : DUMMY_CHAIN . clone ( ) ,
144
+ chain : self . chain_info . chain . clone ( ) ,
135
145
} ) ,
136
146
None => Err ( Error :: authentication ( format ! (
137
147
"No identity found that matches authentication token: {}" ,
@@ -145,11 +155,31 @@ impl Locked for Dummy {
145
155
channel_context : & ChainOf < Channel > ,
146
156
depositor_address : Address ,
147
157
) -> Result < Deposit , crate :: Error > {
158
+ // validate that the same chain & token are used for the Channel Context
159
+ // as the ones setup in the Dummy adapter.
160
+ if channel_context. token . address != channel_context. context . token {
161
+ return Err ( Error :: adapter (
162
+ "Token context of channel & channel token addresses are different" . to_string ( ) ,
163
+ ) ) ;
164
+ }
165
+
166
+ if self . chain_info . chain != channel_context. chain
167
+ || self
168
+ . chain_info
169
+ . find_token ( channel_context. context . token )
170
+ . is_none ( )
171
+ {
172
+ return Err ( Error :: adapter (
173
+ "Channel's Token & Chain not aligned with Dummy adapter's chain" . to_string ( ) ,
174
+ ) ) ;
175
+ }
176
+
148
177
self . deposits
149
- . get_next_deposit ( channel_context. context . id ( ) , depositor_address)
178
+ . get_deposit ( channel_context, depositor_address)
150
179
. ok_or_else ( || {
151
180
Error :: adapter ( format ! (
152
- "No more mocked deposits found for depositor {:?}" ,
181
+ "No mocked deposit found for {:?} & depositor {:?}" ,
182
+ channel_context. context. id( ) ,
153
183
depositor_address
154
184
) )
155
185
} )
@@ -190,43 +220,91 @@ impl Unlockable for Dummy {
190
220
}
191
221
}
192
222
223
+ mod deposit {
224
+ use crate :: primitives:: Deposit ;
225
+ use dashmap:: DashMap ;
226
+ use primitives:: { Address , ChainId , ChainOf , Channel , ChannelId } ;
227
+ use std:: sync:: Arc ;
228
+
229
+ #[ derive( Debug , Clone , Copy , Hash , Eq , PartialEq ) ]
230
+ pub struct Key {
231
+ channel_id : ChannelId ,
232
+ chain_id : ChainId ,
233
+ depositor : Address ,
234
+ }
235
+
236
+ impl Key {
237
+ pub fn from_chain_of ( channel_context : & ChainOf < Channel > , depositor : Address ) -> Self {
238
+ Self {
239
+ channel_id : channel_context. context . id ( ) ,
240
+ chain_id : channel_context. chain . chain_id ,
241
+ depositor,
242
+ }
243
+ }
244
+ }
245
+
246
+ /// Mocked deposits for the Dummy adapter.
247
+ ///
248
+ /// These deposits can be set once and the adapter will return
249
+ /// the set deposit on every call to [`get_deposit()`](`Locked::get_deposit`).
250
+ #[ derive( Debug , Clone , Default ) ]
251
+ pub struct Deposits ( pub Arc < DashMap < Key , Deposit > > ) ;
252
+
253
+ impl Deposits {
254
+ pub fn new ( ) -> Self {
255
+ Self :: default ( )
256
+ }
257
+
258
+ /// Get's the set deposit for the give [`ChannelId`] and [`Address`].
259
+ ///
260
+ /// This method will return [`None`] if the deposit for the pair
261
+ /// [`ChannelId`] & [`Address`] was not set.
262
+ pub fn get_deposit (
263
+ & self ,
264
+ channel : & ChainOf < Channel > ,
265
+ depositor : Address ,
266
+ ) -> Option < Deposit > {
267
+ self . 0
268
+ . get ( & Key :: from_chain_of ( channel, depositor) )
269
+ . map ( |dashmap_ref| dashmap_ref. value ( ) . clone ( ) )
270
+ }
271
+ }
272
+ }
273
+
193
274
#[ cfg( test) ]
194
275
mod test {
195
- use std:: num:: NonZeroU8 ;
196
-
197
276
use primitives:: {
198
- config:: TokenInfo ,
199
- test_util:: { CREATOR , DUMMY_CAMPAIGN , IDS , LEADER } ,
200
- BigNum , ChainOf , UnifiedNum ,
277
+ test_util:: { CREATOR , DUMMY_CAMPAIGN , IDS , LEADER , PUBLISHER } ,
278
+ BigNum , ChainOf ,
201
279
} ;
202
280
203
- use super :: * ;
281
+ use super :: {
282
+ test_util:: { DUMMY_CHAIN , DUMMY_CHAIN_INFO , DUMMY_TOKEN } ,
283
+ * ,
284
+ } ;
204
285
205
286
#[ tokio:: test]
206
287
async fn test_deposits_calls ( ) {
207
288
let channel = DUMMY_CAMPAIGN . channel ;
208
289
209
290
let channel_context = ChainOf {
210
291
context : channel,
211
- token : TokenInfo {
212
- min_campaign_budget : 1_u64 . into ( ) ,
213
- min_validator_fee : 1_u64 . into ( ) ,
214
- precision : NonZeroU8 :: new ( UnifiedNum :: PRECISION ) . expect ( "Non zero u8" ) ,
215
- address : channel. token ,
216
- } ,
292
+ token : DUMMY_TOKEN . clone ( ) ,
217
293
chain : DUMMY_CHAIN . clone ( ) ,
218
294
} ;
219
295
220
296
let dummy_client = Dummy :: init ( Options {
221
297
dummy_identity : IDS [ & LEADER ] ,
222
298
dummy_auth_tokens : Default :: default ( ) ,
299
+ dummy_chain : DUMMY_CHAIN_INFO . clone ( ) ,
223
300
} ) ;
224
301
225
- let address = * CREATOR ;
302
+ let creator = * CREATOR ;
303
+ let publisher = * PUBLISHER ;
226
304
227
305
// no mocked deposit calls should cause an Error
228
306
{
229
- let result = dummy_client. get_deposit ( & channel_context, address ) . await ;
307
+ let result = dummy_client. get_deposit ( & channel_context, creator ) . await ;
230
308
231
309
assert ! ( result. is_err( ) ) ;
232
310
}
@@ -235,32 +313,50 @@ mod test {
235
313
total : BigNum :: from ( total) ,
236
314
} ;
237
315
238
- // add two deposit and call 3 times
239
- // also check if different address does not have access to these calls
316
+ // add two deposit for CREATOR & PUBLISHER
240
317
{
241
- let deposits = [ get_deposit ( 6969 ) , get_deposit ( 1000 ) ] ;
242
- dummy_client. add_deposit_call ( channel. id ( ) , address, deposits[ 0 ] . clone ( ) ) ;
243
- dummy_client. add_deposit_call ( channel. id ( ) , address, deposits[ 1 ] . clone ( ) ) ;
318
+ let creator_deposit = get_deposit ( 6969 ) ;
319
+ let publisher_deposit = get_deposit ( 1000 ) ;
320
+
321
+ dummy_client. set_deposit ( & channel_context, creator, creator_deposit. clone ( ) ) ;
322
+ dummy_client. set_deposit ( & channel_context, publisher, publisher_deposit. clone ( ) ) ;
244
323
245
- let first_call = dummy_client
246
- . get_deposit ( & channel_context, address )
324
+ let creator_actual = dummy_client
325
+ . get_deposit ( & channel_context, creator )
247
326
. await
248
- . expect ( "Should get first mocked deposit" ) ;
249
- assert_eq ! ( & deposits [ 0 ] , & first_call ) ;
327
+ . expect ( "Should get mocked deposit" ) ;
328
+ assert_eq ! ( & creator_deposit , & creator_actual ) ;
250
329
251
- // should not affect the Mocked deposit calls and should cause an error
330
+ // calling an non-mocked address, should cause an error
252
331
let different_address_call = dummy_client. get_deposit ( & channel_context, * LEADER ) . await ;
253
332
assert ! ( different_address_call. is_err( ) ) ;
254
333
255
- let second_call = dummy_client
256
- . get_deposit ( & channel_context, address )
334
+ let publisher_actual = dummy_client
335
+ . get_deposit ( & channel_context, publisher )
257
336
. await
258
- . expect ( "Should get second mocked deposit" ) ;
259
- assert_eq ! ( & deposits[ 1 ] , & second_call) ;
260
-
261
- // Third call should error, we've only mocked 2 calls!
262
- let third_call = dummy_client. get_deposit ( & channel_context, address) . await ;
263
- assert ! ( third_call. is_err( ) ) ;
337
+ . expect ( "Should get mocked deposit" ) ;
338
+ assert_eq ! ( & publisher_deposit, & publisher_actual) ;
264
339
}
265
340
}
341
+
342
+ #[ test]
343
+ #[ should_panic]
344
+ fn test_set_deposit_to_none_should_panic_on_non_mocked_deposits ( ) {
345
+ let channel = DUMMY_CAMPAIGN . channel ;
346
+
347
+ let channel_context = ChainOf {
348
+ context : channel,
349
+ token : DUMMY_TOKEN . clone ( ) ,
350
+ chain : DUMMY_CHAIN . clone ( ) ,
351
+ } ;
352
+
353
+ let dummy_client = Dummy :: init ( Options {
354
+ dummy_identity : IDS [ & LEADER ] ,
355
+ dummy_auth_tokens : Default :: default ( ) ,
356
+ dummy_chain : DUMMY_CHAIN_INFO . clone ( ) ,
357
+ } ) ;
358
+
359
+ // It should panic when no deposit is set and we try to set it to None
360
+ dummy_client. set_deposit ( & channel_context, * LEADER , None ) ;
361
+ }
266
362
}
0 commit comments