@@ -12,7 +12,7 @@ use mithril_common::{
12
12
api_version:: APIVersionProvider ,
13
13
entities:: { ClientError , ServerError } ,
14
14
logging:: LoggerExtensions ,
15
- messages:: EpochSettingsMessage ,
15
+ messages:: { AggregatorFeaturesMessage , EpochSettingsMessage } ,
16
16
} ;
17
17
18
18
const JSON_CONTENT_TYPE : HeaderValue = HeaderValue :: from_static ( "application/json" ) ;
@@ -124,6 +124,11 @@ pub trait AggregatorClient: Sync + Send {
124
124
async fn retrieve_epoch_settings (
125
125
& self ,
126
126
) -> Result < Option < EpochSettingsMessage > , AggregatorClientError > ;
127
+
128
+ /// Retrieves aggregator features message from the aggregator
129
+ async fn retrieve_aggregator_features (
130
+ & self ,
131
+ ) -> Result < AggregatorFeaturesMessage , AggregatorClientError > ;
127
132
}
128
133
129
134
/// AggregatorHTTPClient is a http client for an aggregator
@@ -244,6 +249,32 @@ impl AggregatorClient for AggregatorHTTPClient {
244
249
Err ( err) => Err ( AggregatorClientError :: RemoteServerUnreachable ( anyhow ! ( err) ) ) ,
245
250
}
246
251
}
252
+
253
+ async fn retrieve_aggregator_features (
254
+ & self ,
255
+ ) -> Result < AggregatorFeaturesMessage , AggregatorClientError > {
256
+ debug ! ( self . logger, "Retrieve aggregator features message" ) ;
257
+ let url = format ! ( "{}/" , self . aggregator_endpoint) ;
258
+ let response = self
259
+ . prepare_request_builder ( self . prepare_http_client ( ) ?. get ( url. clone ( ) ) )
260
+ . send ( )
261
+ . await ;
262
+
263
+ match response {
264
+ Ok ( response) => match response. status ( ) {
265
+ StatusCode :: OK => {
266
+ self . warn_if_api_version_mismatch ( & response) ;
267
+
268
+ Ok ( response
269
+ . json :: < AggregatorFeaturesMessage > ( )
270
+ . await
271
+ . map_err ( |e| AggregatorClientError :: JsonParseFailed ( anyhow ! ( e) ) ) ?)
272
+ }
273
+ _ => Err ( AggregatorClientError :: from_response ( response) . await ) ,
274
+ } ,
275
+ Err ( err) => Err ( AggregatorClientError :: RemoteServerUnreachable ( anyhow ! ( err) ) ) ,
276
+ }
277
+ }
247
278
}
248
279
249
280
#[ cfg( test) ]
@@ -258,19 +289,24 @@ pub(crate) mod dumb {
258
289
/// It is driven by a Tester that controls the data it can return, and it can return its internal state for testing.
259
290
pub struct DumbAggregatorClient {
260
291
epoch_settings : RwLock < Option < EpochSettingsMessage > > ,
292
+ aggregator_features : RwLock < AggregatorFeaturesMessage > ,
261
293
}
262
294
263
- // impl DumbAggregatorClient {
264
- // /// Return the last signer that called with the `register` method.
265
- // pub async fn get_last_registered_signer(&self) -> Option<Signer> {
266
- // self.last_registered_signer.read().await.clone()
267
- // }
268
- // }
295
+ impl DumbAggregatorClient {
296
+ pub async fn set_aggregator_features (
297
+ & self ,
298
+ aggregator_features : AggregatorFeaturesMessage ,
299
+ ) {
300
+ let mut aggregator_features_writer = self . aggregator_features . write ( ) . await ;
301
+ * aggregator_features_writer = aggregator_features;
302
+ }
303
+ }
269
304
270
305
impl Default for DumbAggregatorClient {
271
306
fn default ( ) -> Self {
272
307
Self {
273
308
epoch_settings : RwLock :: new ( Some ( EpochSettingsMessage :: dummy ( ) ) ) ,
309
+ aggregator_features : RwLock :: new ( AggregatorFeaturesMessage :: dummy ( ) ) ,
274
310
}
275
311
}
276
312
}
@@ -284,6 +320,13 @@ pub(crate) mod dumb {
284
320
285
321
Ok ( epoch_settings)
286
322
}
323
+
324
+ async fn retrieve_aggregator_features (
325
+ & self ,
326
+ ) -> Result < AggregatorFeaturesMessage , AggregatorClientError > {
327
+ let aggregator_features = self . aggregator_features . read ( ) . await ;
328
+ Ok ( aggregator_features. clone ( ) )
329
+ }
287
330
}
288
331
}
289
332
@@ -305,6 +348,17 @@ mod tests {
305
348
306
349
use super :: * ;
307
350
351
+ macro_rules! assert_is_error {
352
+ ( $error: expr, $error_type: pat) => {
353
+ assert!(
354
+ matches!( $error, $error_type) ,
355
+ "Expected {} error, got '{:?}'." ,
356
+ stringify!( $error_type) ,
357
+ $error
358
+ ) ;
359
+ } ;
360
+ }
361
+
308
362
fn setup_client < U : Into < String > > ( server_url : U ) -> AggregatorHTTPClient {
309
363
let discriminant_source = DummyApiVersionDiscriminantSource :: new ( "dummy" ) ;
310
364
let api_version_provider = APIVersionProvider :: new ( Arc :: new ( discriminant_source) ) ;
@@ -366,52 +420,108 @@ mod tests {
366
420
} ;
367
421
}
368
422
369
- #[ tokio:: test]
370
- async fn test_epoch_settings_ok_200 ( ) {
371
- let ( server, client) = setup_server_and_client ( ) ;
372
- let epoch_settings_expected = EpochSettingsMessage :: dummy ( ) ;
373
- let _server_mock = server. mock ( |when, then| {
374
- when. path ( "/epoch-settings" ) ;
375
- then. status ( 200 ) . body ( json ! ( epoch_settings_expected) . to_string ( ) ) ;
376
- } ) ;
423
+ mod epoch_settings {
424
+ use super :: * ;
377
425
378
- let epoch_settings = client. retrieve_epoch_settings ( ) . await ;
379
- epoch_settings. as_ref ( ) . expect ( "unexpected error" ) ;
380
- assert_eq ! ( epoch_settings_expected, epoch_settings. unwrap( ) . unwrap( ) ) ;
381
- }
426
+ #[ tokio:: test]
427
+ async fn test_epoch_settings_ok_200 ( ) {
428
+ let ( server, client) = setup_server_and_client ( ) ;
429
+ let epoch_settings_expected = EpochSettingsMessage :: dummy ( ) ;
430
+ let _server_mock = server. mock ( |when, then| {
431
+ when. path ( "/epoch-settings" ) ;
432
+ then. status ( 200 ) . body ( json ! ( epoch_settings_expected) . to_string ( ) ) ;
433
+ } ) ;
382
434
383
- #[ tokio:: test]
384
- async fn test_epoch_settings_ko_500 ( ) {
385
- let ( server, client) = setup_server_and_client ( ) ;
386
- let _server_mock = server. mock ( |when, then| {
387
- when. path ( "/epoch-settings" ) ;
388
- then. status ( 500 ) . body ( "an error occurred" ) ;
389
- } ) ;
435
+ let epoch_settings = client. retrieve_epoch_settings ( ) . await ;
436
+ epoch_settings. as_ref ( ) . expect ( "unexpected error" ) ;
437
+ assert_eq ! ( epoch_settings_expected, epoch_settings. unwrap( ) . unwrap( ) ) ;
438
+ }
390
439
391
- match client. retrieve_epoch_settings ( ) . await . unwrap_err ( ) {
392
- AggregatorClientError :: RemoteServerTechnical ( _) => ( ) ,
393
- e => panic ! ( "Expected Aggregator::RemoteServerTechnical error, got '{e:?}'." ) ,
394
- } ;
440
+ #[ tokio:: test]
441
+ async fn test_epoch_settings_ko_500 ( ) {
442
+ let ( server, client) = setup_server_and_client ( ) ;
443
+ let _server_mock = server. mock ( |when, then| {
444
+ when. path ( "/epoch-settings" ) ;
445
+ then. status ( 500 ) . body ( "an error occurred" ) ;
446
+ } ) ;
447
+
448
+ match client. retrieve_epoch_settings ( ) . await . unwrap_err ( ) {
449
+ AggregatorClientError :: RemoteServerTechnical ( _) => ( ) ,
450
+ e => panic ! ( "Expected Aggregator::RemoteServerTechnical error, got '{e:?}'." ) ,
451
+ } ;
452
+ }
453
+
454
+ #[ tokio:: test]
455
+ async fn test_epoch_settings_timeout ( ) {
456
+ let ( server, mut client) = setup_server_and_client ( ) ;
457
+ client. timeout_duration = Some ( Duration :: from_millis ( 10 ) ) ;
458
+ let _server_mock = server. mock ( |when, then| {
459
+ when. path ( "/epoch-settings" ) ;
460
+ then. delay ( Duration :: from_millis ( 100 ) ) ;
461
+ } ) ;
462
+
463
+ let error = client
464
+ . retrieve_epoch_settings ( )
465
+ . await
466
+ . expect_err ( "retrieve_epoch_settings should fail" ) ;
467
+
468
+ assert ! (
469
+ matches!( error, AggregatorClientError :: RemoteServerUnreachable ( _) ) ,
470
+ "unexpected error type: {error:?}"
471
+ ) ;
472
+ }
395
473
}
396
474
397
- #[ tokio:: test]
398
- async fn test_epoch_settings_timeout ( ) {
399
- let ( server, mut client) = setup_server_and_client ( ) ;
400
- client. timeout_duration = Some ( Duration :: from_millis ( 10 ) ) ;
401
- let _server_mock = server. mock ( |when, then| {
402
- when. path ( "/epoch-settings" ) ;
403
- then. delay ( Duration :: from_millis ( 100 ) ) ;
404
- } ) ;
475
+ mod aggregator_features {
476
+ use super :: * ;
405
477
406
- let error = client
407
- . retrieve_epoch_settings ( )
408
- . await
409
- . expect_err ( "retrieve_epoch_settings should fail" ) ;
478
+ #[ tokio:: test]
479
+ async fn test_aggregator_features_ok_200 ( ) {
480
+ let ( server, client) = setup_server_and_client ( ) ;
481
+ let message_expected = AggregatorFeaturesMessage :: dummy ( ) ;
482
+ let _server_mock = server. mock ( |when, then| {
483
+ when. path ( "/" ) ;
484
+ then. status ( 200 ) . body ( json ! ( message_expected) . to_string ( ) ) ;
485
+ } ) ;
410
486
411
- assert ! (
412
- matches!( error, AggregatorClientError :: RemoteServerUnreachable ( _) ) ,
413
- "unexpected error type: {error:?}"
414
- ) ;
487
+ let message = client. retrieve_aggregator_features ( ) . await . unwrap ( ) ;
488
+
489
+ assert_eq ! ( message_expected, message) ;
490
+ }
491
+
492
+ #[ tokio:: test]
493
+ async fn test_aggregator_features_ko_500 ( ) {
494
+ let ( server, client) = setup_server_and_client ( ) ;
495
+ set_returning_500 ( & server) ;
496
+
497
+ let error = client. retrieve_aggregator_features ( ) . await . unwrap_err ( ) ;
498
+
499
+ assert_is_error ! ( error, AggregatorClientError :: RemoteServerTechnical ( _) ) ;
500
+ }
501
+
502
+ #[ tokio:: test]
503
+ async fn test_aggregator_features_ko_json_serialization ( ) {
504
+ let ( server, client) = setup_server_and_client ( ) ;
505
+ set_unparsable_json ( & server) ;
506
+
507
+ let error = client. retrieve_aggregator_features ( ) . await . unwrap_err ( ) ;
508
+
509
+ assert_is_error ! ( error, AggregatorClientError :: JsonParseFailed ( _) ) ;
510
+ }
511
+
512
+ #[ tokio:: test]
513
+ async fn test_aggregator_features_timeout ( ) {
514
+ let ( server, mut client) = setup_server_and_client ( ) ;
515
+ client. timeout_duration = Some ( Duration :: from_millis ( 10 ) ) ;
516
+ let _server_mock = server. mock ( |when, then| {
517
+ when. path ( "/" ) ;
518
+ then. delay ( Duration :: from_millis ( 100 ) ) ;
519
+ } ) ;
520
+
521
+ let error = client. retrieve_aggregator_features ( ) . await . unwrap_err ( ) ;
522
+
523
+ assert_is_error ! ( error, AggregatorClientError :: RemoteServerUnreachable ( _) ) ;
524
+ }
415
525
}
416
526
417
527
#[ tokio:: test]
0 commit comments