@@ -141,3 +141,160 @@ where
141
141
Box :: new ( self . next . send_request ( request) )
142
142
}
143
143
}
144
+
145
+ #[ cfg( test) ]
146
+ mod tests {
147
+ use super :: * ;
148
+ use futures:: { future:: ok, Future } ;
149
+ use interledger_ildcp:: IldcpAccount ;
150
+ use interledger_packet:: { Address , FulfillBuilder , PrepareBuilder } ;
151
+ use interledger_service:: { outgoing_service_fn, Account } ;
152
+ use std:: collections:: HashMap ;
153
+ use std:: str:: FromStr ;
154
+ use std:: {
155
+ sync:: { Arc , Mutex } ,
156
+ time:: SystemTime ,
157
+ } ;
158
+
159
+ #[ test]
160
+ fn exchange_rate_ok ( ) {
161
+ let ret = exchange_rate ( 100 , 1 , 1.0 , 1 , 2.0 ) ;
162
+ assert_eq ! ( ret. 1 [ 0 ] . prepare. amount( ) , 200 ) ;
163
+
164
+ let ret = exchange_rate ( 1_000_000 , 1 , 3.0 , 1 , 2.0 ) ;
165
+ assert_eq ! ( ret. 1 [ 0 ] . prepare. amount( ) , 666_666 ) ;
166
+ }
167
+
168
+ #[ test]
169
+ fn exchange_conversion_error ( ) {
170
+ // rejects f64 that does not fit in u64
171
+ let ret = exchange_rate ( std:: u64:: MAX , 1 , 1.0 , 1 , 2.0 ) ;
172
+ let reject = ret. 0 . unwrap_err ( ) ;
173
+ assert_eq ! ( reject. code( ) , ErrorCode :: F08_AMOUNT_TOO_LARGE ) ;
174
+ assert ! ( reject. message( ) . starts_with( b"Could not cast" ) ) ;
175
+
176
+ // `Convert` errored
177
+ let ret = exchange_rate ( std:: u64:: MAX , 1 , 1.0 , 255 , std:: f64:: MAX ) ;
178
+ let reject = ret. 0 . unwrap_err ( ) ;
179
+ assert_eq ! ( reject. code( ) , ErrorCode :: F08_AMOUNT_TOO_LARGE ) ;
180
+ assert ! ( reject. message( ) . starts_with( b"Could not convert" ) ) ;
181
+ }
182
+
183
+ // Instantiates an exchange rate service and returns the fulfill/reject
184
+ // packet and the outgoing request after performing an asset conversion
185
+ fn exchange_rate (
186
+ amount : u64 ,
187
+ scale1 : u8 ,
188
+ rate1 : f64 ,
189
+ scale2 : u8 ,
190
+ rate2 : f64 ,
191
+ ) -> ( Result < Fulfill , Reject > , Vec < OutgoingRequest < TestAccount > > ) {
192
+ let requests = Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ;
193
+ let requests_clone = requests. clone ( ) ;
194
+ let outgoing = outgoing_service_fn ( move |request| {
195
+ requests_clone. lock ( ) . unwrap ( ) . push ( request) ;
196
+ Box :: new ( ok ( FulfillBuilder {
197
+ fulfillment : & [ 0 ; 32 ] ,
198
+ data : b"hello!" ,
199
+ }
200
+ . build ( ) ) )
201
+ } ) ;
202
+ let mut service = test_service ( rate1, rate2, outgoing) ;
203
+ let result = service
204
+ . send_request ( OutgoingRequest {
205
+ from : TestAccount :: new ( "ABC" . to_owned ( ) , scale1) ,
206
+ to : TestAccount :: new ( "XYZ" . to_owned ( ) , scale2) ,
207
+ original_amount : amount,
208
+ prepare : PrepareBuilder {
209
+ destination : Address :: from_str ( "example.destination" ) . unwrap ( ) ,
210
+ amount,
211
+ expires_at : SystemTime :: now ( ) ,
212
+ execution_condition : & [ 1 ; 32 ] ,
213
+ data : b"hello" ,
214
+ }
215
+ . build ( ) ,
216
+ } )
217
+ . wait ( ) ;
218
+
219
+ let reqs = requests. lock ( ) . unwrap ( ) ;
220
+ ( result, reqs. clone ( ) )
221
+ }
222
+
223
+ #[ derive( Debug , Clone ) ]
224
+ struct TestAccount {
225
+ ilp_address : Address ,
226
+ asset_code : String ,
227
+ asset_scale : u8 ,
228
+ }
229
+ impl TestAccount {
230
+ fn new ( asset_code : String , asset_scale : u8 ) -> Self {
231
+ TestAccount {
232
+ ilp_address : Address :: from_str ( "example.alice" ) . unwrap ( ) ,
233
+ asset_code,
234
+ asset_scale,
235
+ }
236
+ }
237
+ }
238
+
239
+ impl Account for TestAccount {
240
+ type AccountId = u64 ;
241
+
242
+ fn id ( & self ) -> u64 {
243
+ 0
244
+ }
245
+ }
246
+
247
+ impl IldcpAccount for TestAccount {
248
+ fn asset_code ( & self ) -> & str {
249
+ & self . asset_code
250
+ }
251
+
252
+ fn asset_scale ( & self ) -> u8 {
253
+ self . asset_scale
254
+ }
255
+
256
+ fn client_address ( & self ) -> & Address {
257
+ & self . ilp_address
258
+ }
259
+ }
260
+
261
+ #[ derive( Debug , Clone ) ]
262
+ struct TestStore {
263
+ rates : HashMap < Vec < String > , ( f64 , f64 ) > ,
264
+ }
265
+
266
+ impl ExchangeRateStore for TestStore {
267
+ fn get_exchange_rates ( & self , asset_codes : & [ & str ] ) -> Result < Vec < f64 > , ( ) > {
268
+ let mut ret = Vec :: new ( ) ;
269
+ let key = vec ! [ asset_codes[ 0 ] . to_owned( ) , asset_codes[ 1 ] . to_owned( ) ] ;
270
+ let v = self . rates . get ( & key) ;
271
+ if let Some ( v) = v {
272
+ ret. push ( v. 0 ) ;
273
+ ret. push ( v. 1 ) ;
274
+ } else {
275
+ return Err ( ( ) ) ;
276
+ }
277
+ Ok ( ret)
278
+ }
279
+ }
280
+
281
+ fn test_store ( rate1 : f64 , rate2 : f64 ) -> TestStore {
282
+ let mut rates = HashMap :: new ( ) ;
283
+ rates. insert ( vec ! [ "ABC" . to_owned( ) , "XYZ" . to_owned( ) ] , ( rate1, rate2) ) ;
284
+ TestStore { rates }
285
+ }
286
+
287
+ fn test_service (
288
+ rate1 : f64 ,
289
+ rate2 : f64 ,
290
+ handler : impl OutgoingService < TestAccount > + Clone + Send + Sync ,
291
+ ) -> ExchangeRateService <
292
+ TestStore ,
293
+ impl OutgoingService < TestAccount > + Clone + Send + Sync ,
294
+ TestAccount ,
295
+ > {
296
+ let store = test_store ( rate1, rate2) ;
297
+ ExchangeRateService :: new ( Address :: from_str ( "example.bob" ) . unwrap ( ) , store, handler)
298
+ }
299
+
300
+ }
0 commit comments