@@ -240,6 +240,129 @@ mod tests {
240240 use crate :: label:: { LabelName , LabelValue } ;
241241 use crate :: sample:: Sample ;
242242
243+ /// It removes leading and trailing whitespace from each line, and empty lines.
244+ fn format_prometheus_output ( output : & str ) -> String {
245+ output
246+ . lines ( )
247+ . map ( str:: trim)
248+ . filter ( |line| !line. is_empty ( ) )
249+ . collect :: < Vec < _ > > ( )
250+ . join ( "\n " )
251+ }
252+
253+ /// Fixture for testing serialization and deserialization of `MetricCollection`.
254+ ///
255+ /// It contains a default `MetricCollection` object, its JSON representation,
256+ /// and its Prometheus format representation.
257+ struct MetricCollectionFixture {
258+ pub object : MetricCollection ,
259+ pub json : String ,
260+ pub prometheus : String ,
261+ }
262+
263+ impl Default for MetricCollectionFixture {
264+ fn default ( ) -> Self {
265+ Self {
266+ object : Self :: object ( ) ,
267+ json : Self :: json ( ) ,
268+ prometheus : Self :: prometheus ( ) ,
269+ }
270+ }
271+ }
272+
273+ impl MetricCollectionFixture {
274+ fn deconstruct ( & self ) -> ( MetricCollection , String , String ) {
275+ ( self . object . clone ( ) , self . json . clone ( ) , self . prometheus . clone ( ) )
276+ }
277+
278+ fn object ( ) -> MetricCollection {
279+ let time = DurationSinceUnixEpoch :: from_secs ( 1_743_552_000 ) ;
280+
281+ let label_set: LabelSet = [
282+ ( LabelName :: new ( "ip_version" ) , LabelValue :: new ( "ipv4" ) ) ,
283+ ( LabelName :: new ( "protocol" ) , LabelValue :: new ( "https" ) ) ,
284+ ( LabelName :: new ( "url" ) , LabelValue :: new ( "https://0.0.0.0:7070" ) ) ,
285+ ]
286+ . into ( ) ;
287+
288+ MetricCollection :: new (
289+ MetricKindCollection :: new ( vec ! [ Metric :: new(
290+ MetricName :: new( "announce_requests_received_total" ) ,
291+ SampleCollection :: new( vec![ Sample :: new( Counter :: new( 1 ) , time, label_set. clone( ) ) ] ) ,
292+ ) ] ) ,
293+ MetricKindCollection :: new ( vec ! [ Metric :: new(
294+ MetricName :: new( "udp_avg_connect_processing_time_ns" ) ,
295+ SampleCollection :: new( vec![ Sample :: new( Gauge :: new( 1.0 ) , time, label_set. clone( ) ) ] ) ,
296+ ) ] ) ,
297+ )
298+ }
299+
300+ fn json ( ) -> String {
301+ r#"
302+ [
303+ {
304+ "kind": "counter",
305+ "name": "announce_requests_received_total",
306+ "samples": [
307+ {
308+ "value": 1,
309+ "update_at": "2025-04-02T00:00:00+00:00",
310+ "labels": [
311+ {
312+ "name": "ip_version",
313+ "value": "ipv4"
314+ },
315+ {
316+ "name": "protocol",
317+ "value": "https"
318+ },
319+ {
320+ "name": "url",
321+ "value": "https://0.0.0.0:7070"
322+ }
323+ ]
324+ }
325+ ]
326+ },
327+ {
328+ "kind": "gauge",
329+ "name": "udp_avg_connect_processing_time_ns",
330+ "samples": [
331+ {
332+ "value": 1.0,
333+ "update_at": "2025-04-02T00:00:00+00:00",
334+ "labels": [
335+ {
336+ "name": "ip_version",
337+ "value": "ipv4"
338+ },
339+ {
340+ "name": "protocol",
341+ "value": "https"
342+ },
343+ {
344+ "name": "url",
345+ "value": "https://0.0.0.0:7070"
346+ }
347+ ]
348+ }
349+ ]
350+ }
351+ ]
352+ "#
353+ . to_owned ( )
354+ }
355+
356+ fn prometheus ( ) -> String {
357+ format_prometheus_output (
358+ r#"
359+ announce_requests_received_total{ip_version="ipv4",protocol="https",url="https://0.0.0.0:7070"} 1
360+ udp_avg_connect_processing_time_ns{ip_version="ipv4",protocol="https",url="https://0.0.0.0:7070"} 1
361+ "# ,
362+ )
363+ }
364+ }
365+
243366 #[ test]
244367 fn it_should_increase_a_preexistent_counter ( ) {
245368 let time = DurationSinceUnixEpoch :: from_secs ( 1_743_552_000 ) ;
@@ -346,150 +469,32 @@ mod tests {
346469
347470 #[ test]
348471 fn it_should_allow_serializing_to_json ( ) {
349- let time = DurationSinceUnixEpoch :: from_secs ( 1_743_552_000 ) ;
350- let label_set: LabelSet = [
351- ( LabelName :: new ( "ip_version" ) , LabelValue :: new ( "ipv4" ) ) ,
352- ( LabelName :: new ( "protocol" ) , LabelValue :: new ( "https" ) ) ,
353- ( LabelName :: new ( "url" ) , LabelValue :: new ( "https://0.0.0.0:7070" ) ) ,
354- ]
355- . into ( ) ;
356-
357- let metric_collection = MetricCollection :: new (
358- MetricKindCollection :: new ( vec ! [ Metric :: new(
359- MetricName :: new( "announce_requests_received_total" ) ,
360- SampleCollection :: new( vec![ Sample :: new( Counter :: new( 1 ) , time, label_set. clone( ) ) ] ) ,
361- ) ] ) ,
362- MetricKindCollection :: new ( vec ! [ ] ) ,
363- ) ;
472+ let ( metric_collection, expected_json, _expected_prometheus) = MetricCollectionFixture :: default ( ) . deconstruct ( ) ;
364473
365474 let json = serde_json:: to_string_pretty ( & metric_collection) . unwrap ( ) ;
366475
367- let expected_json = r#"
368- [
369- {
370- "kind": "counter",
371- "name": "announce_requests_received_total",
372- "samples": [
373- {
374- "value": 1,
375- "update_at": "2025-04-02T00:00:00+00:00",
376- "labels": [
377- {
378- "name": "ip_version",
379- "value": "ipv4"
380- },
381- {
382- "name": "protocol",
383- "value": "https"
384- },
385- {
386- "name": "url",
387- "value": "https://0.0.0.0:7070"
388- }
389- ]
390- }
391- ]
392- }
393- ]
394- "# ;
395-
396476 assert_eq ! (
397477 serde_json:: from_str:: <serde_json:: Value >( & json) . unwrap( ) ,
398- serde_json:: from_str:: <serde_json:: Value >( expected_json) . unwrap( )
478+ serde_json:: from_str:: <serde_json:: Value >( & expected_json) . unwrap( )
399479 ) ;
400480 }
401481
402482 #[ test]
403483 fn it_should_allow_deserializing_from_json ( ) {
404- let json = r#"
405- [
406- {
407- "kind": "counter",
408- "name": "announce_requests_received_total",
409- "samples": [
410- {
411- "value": 1,
412- "update_at": "2025-04-02T00:00:00+00:00",
413- "labels": [
414- {
415- "name": "ip_version",
416- "value": "ipv4"
417- },
418- {
419- "name": "protocol",
420- "value": "https"
421- },
422- {
423- "name": "url",
424- "value": "https://0.0.0.0:7070"
425- }
426- ]
427- }
428- ]
429- }
430- ]
431- "# ;
432-
433- let metric_collection: MetricCollection = serde_json:: from_str ( json) . unwrap ( ) ;
434-
435- let time = DurationSinceUnixEpoch :: from_secs ( 1_743_552_000 ) ;
436-
437- let label_set: LabelSet = [
438- ( LabelName :: new ( "ip_version" ) , LabelValue :: new ( "ipv4" ) ) ,
439- ( LabelName :: new ( "protocol" ) , LabelValue :: new ( "https" ) ) ,
440- ( LabelName :: new ( "url" ) , LabelValue :: new ( "https://0.0.0.0:7070" ) ) ,
441- ]
442- . into ( ) ;
484+ let ( expected_metric_collection, metric_collection_json, _expected_prometheus) =
485+ MetricCollectionFixture :: default ( ) . deconstruct ( ) ;
443486
444- let expected_metric_collection = MetricCollection :: new (
445- MetricKindCollection :: new ( vec ! [ Metric :: new(
446- MetricName :: new( "announce_requests_received_total" ) ,
447- SampleCollection :: new( vec![ Sample :: new( Counter :: new( 1 ) , time, label_set. clone( ) ) ] ) ,
448- ) ] ) ,
449- MetricKindCollection :: new ( vec ! [ ] ) ,
450- ) ;
487+ let metric_collection: MetricCollection = serde_json:: from_str ( & metric_collection_json) . unwrap ( ) ;
451488
452489 assert_eq ! ( metric_collection, expected_metric_collection) ;
453490 }
454491
455492 #[ test]
456493 fn it_should_allow_serializing_to_prometheus_format ( ) {
457- let time = DurationSinceUnixEpoch :: from_secs ( 1_743_552_000 ) ;
458- let label_set: LabelSet = [
459- ( LabelName :: new ( "ip_version" ) , LabelValue :: new ( "ipv4" ) ) ,
460- ( LabelName :: new ( "protocol" ) , LabelValue :: new ( "https" ) ) ,
461- ( LabelName :: new ( "url" ) , LabelValue :: new ( "https://0.0.0.0:7070" ) ) ,
462- ]
463- . into ( ) ;
464-
465- let metric_collection = MetricCollection :: new (
466- MetricKindCollection :: new ( vec ! [ Metric :: new(
467- MetricName :: new( "announce_requests_received_total" ) ,
468- SampleCollection :: new( vec![ Sample :: new( Counter :: new( 1 ) , time, label_set. clone( ) ) ] ) ,
469- ) ] ) ,
470- MetricKindCollection :: new ( vec ! [ Metric :: new(
471- MetricName :: new( "udp_avg_connect_processing_time_ns" ) ,
472- SampleCollection :: new( vec![ Sample :: new( Gauge :: new( 1.0 ) , time, label_set. clone( ) ) ] ) ,
473- ) ] ) ,
474- ) ;
494+ let ( metric_collection, _expected_json, expected_prometheus) = MetricCollectionFixture :: default ( ) . deconstruct ( ) ;
475495
476496 let prometheus_output = metric_collection. to_prometheus ( ) ;
477497
478- let expected = r#"
479- announce_requests_received_total{ip_version="ipv4",protocol="https",url="https://0.0.0.0:7070"} 1
480- udp_avg_connect_processing_time_ns{ip_version="ipv4",protocol="https",url="https://0.0.0.0:7070"} 1
481- "# ;
482-
483- assert_eq ! ( prometheus_output, format_prometheus_output( expected) ) ;
484- }
485-
486- /// It removes leading and trailing whitespace from each line, and empty lines.
487- fn format_prometheus_output ( output : & str ) -> String {
488- output
489- . lines ( )
490- . map ( str:: trim)
491- . filter ( |line| !line. is_empty ( ) )
492- . collect :: < Vec < _ > > ( )
493- . join ( "\n " )
498+ assert_eq ! ( prometheus_output, expected_prometheus) ;
494499 }
495500}
0 commit comments