@@ -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,45 +249,30 @@ impl AggregatorClient for AggregatorHTTPClient {
244
249
Err ( err) => Err ( AggregatorClientError :: RemoteServerUnreachable ( anyhow ! ( err) ) ) ,
245
250
}
246
251
}
247
- }
248
-
249
- #[ cfg( test) ]
250
- pub ( crate ) mod dumb {
251
- use mithril_common:: test:: double:: Dummy ;
252
- use tokio:: sync:: RwLock ;
253
-
254
- use super :: * ;
255
-
256
- /// This aggregator client is intended to be used by test services.
257
- /// It actually does not communicate with an aggregator host but mimics this behavior.
258
- /// It is driven by a Tester that controls the data it can return, and it can return its internal state for testing.
259
- pub struct DumbAggregatorClient {
260
- epoch_settings : RwLock < Option < EpochSettingsMessage > > ,
261
- }
262
252
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
- // }
269
-
270
- impl Default for DumbAggregatorClient {
271
- fn default ( ) -> Self {
272
- Self {
273
- epoch_settings : RwLock :: new ( Some ( EpochSettingsMessage :: dummy ( ) ) ) ,
274
- }
275
- }
276
- }
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 ;
277
262
278
- #[ async_trait]
279
- impl AggregatorClient for DumbAggregatorClient {
280
- async fn retrieve_epoch_settings (
281
- & self ,
282
- ) -> Result < Option < EpochSettingsMessage > , AggregatorClientError > {
283
- let epoch_settings = self . epoch_settings . read ( ) . await . clone ( ) ;
263
+ match response {
264
+ Ok ( response) => match response. status ( ) {
265
+ StatusCode :: OK => {
266
+ self . warn_if_api_version_mismatch ( & response) ;
284
267
285
- Ok ( epoch_settings)
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) ) ) ,
286
276
}
287
277
}
288
278
}
@@ -305,6 +295,17 @@ mod tests {
305
295
306
296
use super :: * ;
307
297
298
+ macro_rules! assert_is_error {
299
+ ( $error: expr, $error_type: pat) => {
300
+ assert!(
301
+ matches!( $error, $error_type) ,
302
+ "Expected {} error, got '{:?}'." ,
303
+ stringify!( $error_type) ,
304
+ $error
305
+ ) ;
306
+ } ;
307
+ }
308
+
308
309
fn setup_client < U : Into < String > > ( server_url : U ) -> AggregatorHTTPClient {
309
310
let discriminant_source = DummyApiVersionDiscriminantSource :: new ( "dummy" ) ;
310
311
let api_version_provider = APIVersionProvider :: new ( Arc :: new ( discriminant_source) ) ;
@@ -366,52 +367,108 @@ mod tests {
366
367
} ;
367
368
}
368
369
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
- } ) ;
370
+ mod epoch_settings {
371
+ use super :: * ;
377
372
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
- }
373
+ #[ tokio:: test]
374
+ async fn test_epoch_settings_ok_200 ( ) {
375
+ let ( server, client) = setup_server_and_client ( ) ;
376
+ let epoch_settings_expected = EpochSettingsMessage :: dummy ( ) ;
377
+ let _server_mock = server. mock ( |when, then| {
378
+ when. path ( "/epoch-settings" ) ;
379
+ then. status ( 200 ) . body ( json ! ( epoch_settings_expected) . to_string ( ) ) ;
380
+ } ) ;
382
381
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
- } ) ;
382
+ let epoch_settings = client. retrieve_epoch_settings ( ) . await ;
383
+ epoch_settings. as_ref ( ) . expect ( "unexpected error" ) ;
384
+ assert_eq ! ( epoch_settings_expected, epoch_settings. unwrap( ) . unwrap( ) ) ;
385
+ }
390
386
391
- match client. retrieve_epoch_settings ( ) . await . unwrap_err ( ) {
392
- AggregatorClientError :: RemoteServerTechnical ( _) => ( ) ,
393
- e => panic ! ( "Expected Aggregator::RemoteServerTechnical error, got '{e:?}'." ) ,
394
- } ;
387
+ #[ tokio:: test]
388
+ async fn test_epoch_settings_ko_500 ( ) {
389
+ let ( server, client) = setup_server_and_client ( ) ;
390
+ let _server_mock = server. mock ( |when, then| {
391
+ when. path ( "/epoch-settings" ) ;
392
+ then. status ( 500 ) . body ( "an error occurred" ) ;
393
+ } ) ;
394
+
395
+ match client. retrieve_epoch_settings ( ) . await . unwrap_err ( ) {
396
+ AggregatorClientError :: RemoteServerTechnical ( _) => ( ) ,
397
+ e => panic ! ( "Expected Aggregator::RemoteServerTechnical error, got '{e:?}'." ) ,
398
+ } ;
399
+ }
400
+
401
+ #[ tokio:: test]
402
+ async fn test_epoch_settings_timeout ( ) {
403
+ let ( server, mut client) = setup_server_and_client ( ) ;
404
+ client. timeout_duration = Some ( Duration :: from_millis ( 10 ) ) ;
405
+ let _server_mock = server. mock ( |when, then| {
406
+ when. path ( "/epoch-settings" ) ;
407
+ then. delay ( Duration :: from_millis ( 100 ) ) ;
408
+ } ) ;
409
+
410
+ let error = client
411
+ . retrieve_epoch_settings ( )
412
+ . await
413
+ . expect_err ( "retrieve_epoch_settings should fail" ) ;
414
+
415
+ assert ! (
416
+ matches!( error, AggregatorClientError :: RemoteServerUnreachable ( _) ) ,
417
+ "unexpected error type: {error:?}"
418
+ ) ;
419
+ }
395
420
}
396
421
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
- } ) ;
422
+ mod aggregator_features {
423
+ use super :: * ;
424
+
425
+ #[ tokio:: test]
426
+ async fn test_aggregator_features_ok_200 ( ) {
427
+ let ( server, client) = setup_server_and_client ( ) ;
428
+ let message_expected = AggregatorFeaturesMessage :: dummy ( ) ;
429
+ let _server_mock = server. mock ( |when, then| {
430
+ when. path ( "/" ) ;
431
+ then. status ( 200 ) . body ( json ! ( message_expected) . to_string ( ) ) ;
432
+ } ) ;
405
433
406
- let error = client
407
- . retrieve_epoch_settings ( )
408
- . await
409
- . expect_err ( "retrieve_epoch_settings should fail" ) ;
434
+ let message = client. retrieve_aggregator_features ( ) . await . unwrap ( ) ;
410
435
411
- assert ! (
412
- matches!( error, AggregatorClientError :: RemoteServerUnreachable ( _) ) ,
413
- "unexpected error type: {error:?}"
414
- ) ;
436
+ assert_eq ! ( message_expected, message) ;
437
+ }
438
+
439
+ #[ tokio:: test]
440
+ async fn test_aggregator_features_ko_500 ( ) {
441
+ let ( server, client) = setup_server_and_client ( ) ;
442
+ set_returning_500 ( & server) ;
443
+
444
+ let error = client. retrieve_aggregator_features ( ) . await . unwrap_err ( ) ;
445
+
446
+ assert_is_error ! ( error, AggregatorClientError :: RemoteServerTechnical ( _) ) ;
447
+ }
448
+
449
+ #[ tokio:: test]
450
+ async fn test_aggregator_features_ko_json_serialization ( ) {
451
+ let ( server, client) = setup_server_and_client ( ) ;
452
+ set_unparsable_json ( & server) ;
453
+
454
+ let error = client. retrieve_aggregator_features ( ) . await . unwrap_err ( ) ;
455
+
456
+ assert_is_error ! ( error, AggregatorClientError :: JsonParseFailed ( _) ) ;
457
+ }
458
+
459
+ #[ tokio:: test]
460
+ async fn test_aggregator_features_timeout ( ) {
461
+ let ( server, mut client) = setup_server_and_client ( ) ;
462
+ client. timeout_duration = Some ( Duration :: from_millis ( 10 ) ) ;
463
+ let _server_mock = server. mock ( |when, then| {
464
+ when. path ( "/" ) ;
465
+ then. delay ( Duration :: from_millis ( 100 ) ) ;
466
+ } ) ;
467
+
468
+ let error = client. retrieve_aggregator_features ( ) . await . unwrap_err ( ) ;
469
+
470
+ assert_is_error ! ( error, AggregatorClientError :: RemoteServerUnreachable ( _) ) ;
471
+ }
415
472
}
416
473
417
474
#[ tokio:: test]
0 commit comments