@@ -5,12 +5,19 @@ use metrics_exporter_prometheus::PrometheusBuilder;
55use reqwest:: StatusCode ;
66use rstest:: rstest;
77use starknet_api:: block:: { BlockInfo , BlockNumber } ;
8+ use url:: Url ;
89
910use super :: { CendeAmbassador , RECORDER_WRITE_BLOB_PATH } ;
10- use crate :: cende:: { BlobParameters , CendeConfig , CendeContext } ;
11+ use crate :: cende:: {
12+ BlobParameters ,
13+ CendeConfig ,
14+ CendeContext ,
15+ GetLatestBlobResponse ,
16+ RECORDER_GET_LATEST_BLOB_PATH ,
17+ } ;
1118use crate :: metrics:: {
1219 register_metrics,
13- CendeWriteFailureReason ,
20+ CendeWritePrevHeightFailureReason ,
1421 CENDE_LAST_PREPARED_BLOB_BLOCK_NUMBER ,
1522 CENDE_WRITE_BLOB_FAILURE ,
1623 CENDE_WRITE_BLOB_SUCCESS ,
@@ -32,6 +39,7 @@ struct ExpectedMetrics {
3239 failure_block_height_mismatch : usize ,
3340 failure_recorder_error : usize ,
3441 failure_skip_write_height : usize ,
42+ failure_communication_error : usize ,
3543}
3644
3745impl ExpectedMetrics {
@@ -51,39 +59,82 @@ impl ExpectedMetrics {
5159 Self { failure_recorder_error : 1 , ..Default :: default ( ) }
5260 }
5361
62+ fn skip_write_height ( ) -> Self {
63+ Self { failure_skip_write_height : 1 , ..Default :: default ( ) }
64+ }
65+
66+ fn communication_error ( ) -> Self {
67+ Self { failure_communication_error : 1 , ..Default :: default ( ) }
68+ }
69+
5470 fn verify_metrics ( & self , metrics : & str ) {
5571 CENDE_WRITE_BLOB_FAILURE . assert_eq (
5672 metrics,
5773 self . failure_skip_write_height ,
58- & [ ( LABEL_CENDE_FAILURE_REASON , CendeWriteFailureReason :: SkipWriteHeight . into ( ) ) ] ,
74+ & [ (
75+ LABEL_CENDE_FAILURE_REASON ,
76+ CendeWritePrevHeightFailureReason :: SkipWriteHeight . into ( ) ,
77+ ) ] ,
5978 ) ;
6079 CENDE_WRITE_BLOB_FAILURE . assert_eq (
6180 metrics,
6281 self . failure_no_prev_blob ,
63- & [ ( LABEL_CENDE_FAILURE_REASON , CendeWriteFailureReason :: BlobNotAvailable . into ( ) ) ] ,
82+ & [ (
83+ LABEL_CENDE_FAILURE_REASON ,
84+ CendeWritePrevHeightFailureReason :: BlobNotAvailable . into ( ) ,
85+ ) ] ,
6486 ) ;
6587 CENDE_WRITE_BLOB_FAILURE . assert_eq (
6688 metrics,
6789 self . failure_block_height_mismatch ,
68- & [ ( LABEL_CENDE_FAILURE_REASON , CendeWriteFailureReason :: HeightMismatch . into ( ) ) ] ,
90+ & [ (
91+ LABEL_CENDE_FAILURE_REASON ,
92+ CendeWritePrevHeightFailureReason :: HeightMismatch . into ( ) ,
93+ ) ] ,
6994 ) ;
7095 CENDE_WRITE_BLOB_FAILURE . assert_eq (
7196 metrics,
7297 self . failure_recorder_error ,
73- & [ ( LABEL_CENDE_FAILURE_REASON , CendeWriteFailureReason :: CendeRecorderError . into ( ) ) ] ,
98+ & [ (
99+ LABEL_CENDE_FAILURE_REASON ,
100+ CendeWritePrevHeightFailureReason :: CendeRecorderError . into ( ) ,
101+ ) ] ,
102+ ) ;
103+ CENDE_WRITE_BLOB_FAILURE . assert_eq (
104+ metrics,
105+ self . failure_communication_error ,
106+ & [ (
107+ LABEL_CENDE_FAILURE_REASON ,
108+ CendeWritePrevHeightFailureReason :: CommunicationError . into ( ) ,
109+ ) ] ,
74110 ) ;
75111 CENDE_WRITE_BLOB_SUCCESS . assert_eq ( metrics, self . success ) ;
76112 }
77113}
78114
79115#[ rstest]
80- #[ case:: success( 200 , Some ( 9 ) , 1 , true , ExpectedMetrics :: success( ) ) ]
81- #[ case:: no_prev_block( 200 , None , 0 , false , ExpectedMetrics :: no_prev_blob( ) ) ]
82- #[ case:: prev_block_height_mismatch( 200 , Some ( 7 ) , 0 , false , ExpectedMetrics :: height_mismatch( ) ) ]
83- #[ case:: recorder_return_fatal_error( 400 , Some ( 9 ) , 1 , false , ExpectedMetrics :: recorder_error( ) ) ]
116+ #[ case:: success( 200 , HEIGHT_TO_WRITE , Some ( 9 ) , 1 , true , ExpectedMetrics :: success( ) ) ]
117+ #[ case:: height_zero( 200 , BlockNumber ( 0 ) , None , 0 , true , ExpectedMetrics :: skip_write_height( ) ) ]
118+ #[ case:: prev_block_height_mismatch(
119+ 200 ,
120+ HEIGHT_TO_WRITE ,
121+ Some ( 7 ) ,
122+ 0 ,
123+ false ,
124+ ExpectedMetrics :: height_mismatch( )
125+ ) ]
126+ #[ case:: recorder_return_fatal_error(
127+ 400 ,
128+ HEIGHT_TO_WRITE ,
129+ Some ( 9 ) ,
130+ 1 ,
131+ false ,
132+ ExpectedMetrics :: recorder_error( )
133+ ) ]
84134#[ tokio:: test]
85135async fn write_prev_height_blob (
86136 #[ case] mock_status_code : usize ,
137+ #[ case] current_height : BlockNumber ,
87138 #[ case] prev_block : Option < u64 > ,
88139 #[ case] expected_calls : usize ,
89140 #[ case] expected_result : bool ,
@@ -111,7 +162,7 @@ async fn write_prev_height_blob(
111162 . unwrap ( ) ;
112163 }
113164
114- let receiver = cende_ambassador. write_prev_height_blob ( HEIGHT_TO_WRITE ) ;
165+ let receiver = cende_ambassador. write_prev_height_blob ( current_height ) ;
115166
116167 assert_eq ! ( receiver. await . unwrap( ) , expected_result) ;
117168 mock. expect ( expected_calls) . assert ( ) ;
@@ -181,29 +232,127 @@ async fn prepare_blob_for_next_height() {
181232}
182233
183234#[ tokio:: test]
184- async fn no_write_at_skipped_height ( ) {
235+ async fn write_prev_height_blob_no_prev_blob_and_cende_communication_error ( ) {
236+ const CURRENT_HEIGHT : BlockNumber = BlockNumber ( 10 ) ;
237+
185238 let recorder = PrometheusBuilder :: new ( ) . build_recorder ( ) ;
186239 let _recorder_guard = metrics:: set_default_local_recorder ( & recorder) ;
187240 register_metrics ( ) ;
188241
189- const SKIP_WRITE_HEIGHT : BlockNumber = HEIGHT_TO_WRITE ;
190242 let cende_ambassador = CendeAmbassador :: new (
191- CendeConfig { skip_write_height : Some ( SKIP_WRITE_HEIGHT ) , ..Default :: default ( ) } ,
243+ CendeConfig {
244+ // We do not create a mock server and we send a bogus URL. This should cause
245+ // request_builder.send to return an Err.
246+ recorder_url : Url :: parse ( "https://bogus.url" ) . unwrap ( ) ,
247+ ..Default :: default ( )
248+ } ,
192249 Arc :: new ( MockClassManagerClient :: new ( ) ) ,
193250 ) ;
194251
195- // Returns false since the blob is missing and the height is different than skip_write_height.
196- assert ! (
197- !cende_ambassador. write_prev_height_blob( HEIGHT_TO_WRITE . unchecked_next( ) ) . await . unwrap( )
252+ let receiver = cende_ambassador. write_prev_height_blob ( CURRENT_HEIGHT ) ;
253+
254+ assert ! ( !receiver. await . unwrap( ) ) ;
255+
256+ ExpectedMetrics :: communication_error ( ) . verify_metrics ( & recorder. handle ( ) . render ( ) ) ;
257+ }
258+
259+ #[ tokio:: test]
260+ async fn write_prev_height_blob_no_prev_blob_and_cende_recorder_error ( ) {
261+ const CURRENT_HEIGHT : BlockNumber = BlockNumber ( 10 ) ;
262+
263+ let recorder = PrometheusBuilder :: new ( ) . build_recorder ( ) ;
264+ let _recorder_guard = metrics:: set_default_local_recorder ( & recorder) ;
265+ register_metrics ( ) ;
266+
267+ let mut server = mockito:: Server :: new_async ( ) . await ;
268+ let url = server. url ( ) ;
269+
270+ let get_latest_blob_mock = server
271+ . mock ( "GET" , RECORDER_GET_LATEST_BLOB_PATH )
272+ // Return a non retryable error.
273+ . with_status ( StatusCode :: METHOD_NOT_ALLOWED . as_u16 ( ) . into ( ) )
274+ . create ( ) ;
275+
276+ let cende_ambassador = CendeAmbassador :: new (
277+ CendeConfig { recorder_url : url. parse ( ) . unwrap ( ) , ..Default :: default ( ) } ,
278+ Arc :: new ( MockClassManagerClient :: new ( ) ) ,
198279 ) ;
199280
200- assert ! ( cende_ambassador. write_prev_height_blob( HEIGHT_TO_WRITE ) . await . unwrap( ) ) ;
281+ let receiver = cende_ambassador. write_prev_height_blob ( CURRENT_HEIGHT ) ;
282+
283+ assert ! ( !receiver. await . unwrap( ) ) ;
284+ get_latest_blob_mock. expect ( 1 ) . assert ( ) ;
285+
286+ ExpectedMetrics :: recorder_error ( ) . verify_metrics ( & recorder. handle ( ) . render ( ) ) ;
287+ }
288+
289+ #[ tokio:: test]
290+ async fn write_prev_height_blob_no_prev_blob_and_not_written_to_cende ( ) {
291+ const CURRENT_HEIGHT : BlockNumber = BlockNumber ( 10 ) ;
292+
293+ let recorder = PrometheusBuilder :: new ( ) . build_recorder ( ) ;
294+ let _recorder_guard = metrics:: set_default_local_recorder ( & recorder) ;
295+ register_metrics ( ) ;
201296
202- // Verify metrics.
203- let expected_metrics = ExpectedMetrics {
204- failure_no_prev_blob : 1 ,
205- failure_skip_write_height : 1 ,
206- ..Default :: default ( )
297+ let mut server = mockito:: Server :: new_async ( ) . await ;
298+ let url = server. url ( ) ;
299+
300+ let latest_blob_response = GetLatestBlobResponse {
301+ // The latest blob is at height 2 below current, so we don't have the previous blob.
302+ height : CURRENT_HEIGHT . prev ( ) . unwrap ( ) . prev ( ) . unwrap ( ) ,
303+ proposal_commitment : "Unused" . to_string ( ) ,
207304 } ;
208- expected_metrics. verify_metrics ( & recorder. handle ( ) . render ( ) ) ;
305+
306+ let get_latest_blob_mock = server
307+ . mock ( "GET" , RECORDER_GET_LATEST_BLOB_PATH )
308+ . with_body ( serde_json:: to_string ( & latest_blob_response) . unwrap ( ) )
309+ . create ( ) ;
310+
311+ let cende_ambassador = CendeAmbassador :: new (
312+ CendeConfig { recorder_url : url. parse ( ) . unwrap ( ) , ..Default :: default ( ) } ,
313+ Arc :: new ( MockClassManagerClient :: new ( ) ) ,
314+ ) ;
315+
316+ let receiver = cende_ambassador. write_prev_height_blob ( CURRENT_HEIGHT ) ;
317+
318+ assert ! ( !receiver. await . unwrap( ) ) ;
319+ get_latest_blob_mock. expect ( 1 ) . assert ( ) ;
320+
321+ ExpectedMetrics :: no_prev_blob ( ) . verify_metrics ( & recorder. handle ( ) . render ( ) ) ;
322+ }
323+
324+ #[ tokio:: test]
325+ async fn write_prev_height_blob_no_prev_blob_but_it_was_written_to_cende ( ) {
326+ const CURRENT_HEIGHT : BlockNumber = BlockNumber ( 10 ) ;
327+
328+ let recorder = PrometheusBuilder :: new ( ) . build_recorder ( ) ;
329+ let _recorder_guard = metrics:: set_default_local_recorder ( & recorder) ;
330+ register_metrics ( ) ;
331+
332+ let mut server = mockito:: Server :: new_async ( ) . await ;
333+ let url = server. url ( ) ;
334+
335+ let latest_blob_response = GetLatestBlobResponse {
336+ // The latest blob is at height 1 below current, so we don't have to write the previous
337+ // blob.
338+ height : CURRENT_HEIGHT . prev ( ) . unwrap ( ) ,
339+ proposal_commitment : "Unused" . to_string ( ) ,
340+ } ;
341+
342+ let get_latest_blob_mock = server
343+ . mock ( "GET" , RECORDER_GET_LATEST_BLOB_PATH )
344+ . with_body ( serde_json:: to_string ( & latest_blob_response) . unwrap ( ) )
345+ . create ( ) ;
346+
347+ let cende_ambassador = CendeAmbassador :: new (
348+ CendeConfig { recorder_url : url. parse ( ) . unwrap ( ) , ..Default :: default ( ) } ,
349+ Arc :: new ( MockClassManagerClient :: new ( ) ) ,
350+ ) ;
351+
352+ let receiver = cende_ambassador. write_prev_height_blob ( CURRENT_HEIGHT ) ;
353+
354+ assert ! ( receiver. await . unwrap( ) ) ;
355+ get_latest_blob_mock. expect ( 1 ) . assert ( ) ;
356+
357+ ExpectedMetrics :: skip_write_height ( ) . verify_metrics ( & recorder. handle ( ) . render ( ) ) ;
209358}
0 commit comments