@@ -1503,4 +1503,185 @@ mod json_serde {
15031503 }
15041504 }
15051505 }
1506+ #[ cfg( feature = "metrics" ) ]
1507+ mod metrics_with_nan {
1508+ use super :: * ;
1509+ use opentelemetry_proto:: tonic:: metrics:: v1:: summary_data_point:: ValueAtQuantile ;
1510+ use opentelemetry_proto:: tonic:: metrics:: v1:: Summary ;
1511+ use opentelemetry_proto:: tonic:: metrics:: v1:: SummaryDataPoint ;
1512+
1513+ fn value_with_nan ( ) -> ExportMetricsServiceRequest {
1514+ ExportMetricsServiceRequest {
1515+ resource_metrics : vec ! [ ResourceMetrics {
1516+ resource: Some ( Resource {
1517+ attributes: vec![ KeyValue {
1518+ key: String :: from( "service.name" ) ,
1519+ value: None ,
1520+ } ] ,
1521+ dropped_attributes_count: 0 ,
1522+ } ) ,
1523+ scope_metrics: vec![ ScopeMetrics {
1524+ scope: None ,
1525+ metrics: vec![ Metric {
1526+ name: String :: from( "example_metric" ) ,
1527+ description: String :: from( "A sample metric with NaN values" ) ,
1528+ unit: String :: from( "1" ) ,
1529+ metadata: vec![ ] ,
1530+ data: Some (
1531+ opentelemetry_proto:: tonic:: metrics:: v1:: metric:: Data :: Summary (
1532+ Summary {
1533+ data_points: vec![ SummaryDataPoint {
1534+ attributes: vec![ ] ,
1535+ start_time_unix_nano: 0 ,
1536+ time_unix_nano: 0 ,
1537+ count: 100 ,
1538+ sum: 500.0 ,
1539+ quantile_values: vec![
1540+ ValueAtQuantile {
1541+ quantile: 0.5 ,
1542+ value: f64 :: NAN ,
1543+ } ,
1544+ ValueAtQuantile {
1545+ quantile: 0.9 ,
1546+ value: f64 :: NAN ,
1547+ } ,
1548+ ] ,
1549+ flags: 0 ,
1550+ } ] ,
1551+ } ,
1552+ ) ,
1553+ ) ,
1554+ } ] ,
1555+ schema_url: String :: new( ) ,
1556+ } ] ,
1557+ schema_url: String :: new( ) ,
1558+ } ] ,
1559+ }
1560+ }
1561+
1562+ // language=json
1563+ const CANONICAL_WITH_NAN : & str = r#"{
1564+ "resourceMetrics": [
1565+ {
1566+ "resource": {
1567+ "attributes": [
1568+ {
1569+ "key": "service.name",
1570+ "value": null
1571+ }
1572+ ],
1573+ "droppedAttributesCount": 0
1574+ },
1575+ "scopeMetrics": [
1576+ {
1577+ "scope": null,
1578+ "metrics": [
1579+ {
1580+ "name": "example_metric",
1581+ "description": "A sample metric with NaN values",
1582+ "unit": "1",
1583+ "metadata": [],
1584+ "summary": {
1585+ "dataPoints": [
1586+ {
1587+ "attributes": [],
1588+ "startTimeUnixNano": "0",
1589+ "timeUnixNano": "0",
1590+ "count": "100",
1591+ "sum": 500.0,
1592+ "quantileValues": [
1593+ {
1594+ "quantile": 0.5,
1595+ "value": "NaN"
1596+ },
1597+ {
1598+ "quantile": 0.9,
1599+ "value": "NaN"
1600+ }
1601+ ],
1602+ "flags": 0
1603+ }
1604+ ]
1605+ }
1606+ }
1607+ ],
1608+ "schemaUrl": ""
1609+ }
1610+ ],
1611+ "schemaUrl": ""
1612+ }
1613+ ]
1614+ }"# ;
1615+
1616+ #[ test]
1617+ fn serialize_with_nan ( ) {
1618+ let input: ExportMetricsServiceRequest = value_with_nan ( ) ;
1619+
1620+ // Serialize the structure to JSON
1621+ let actual = serde_json:: to_string_pretty ( & input) . expect ( "serialization must succeed" ) ;
1622+
1623+ // Normalize both the actual and expected JSON for comparison
1624+ let actual_value: serde_json:: Value =
1625+ serde_json:: from_str ( & actual) . expect ( "valid JSON" ) ;
1626+ let expected_value: serde_json:: Value =
1627+ serde_json:: from_str ( CANONICAL_WITH_NAN ) . expect ( "valid JSON" ) ;
1628+
1629+ // Compare the normalized JSON values
1630+ assert_eq ! ( actual_value, expected_value) ;
1631+ }
1632+
1633+ #[ test]
1634+ fn deserialize_with_nan ( ) {
1635+ let actual: ExportMetricsServiceRequest =
1636+ serde_json:: from_str ( CANONICAL_WITH_NAN ) . expect ( "deserialization must succeed" ) ;
1637+
1638+ // Ensure the deserialized structure matches the expected values
1639+ assert_eq ! ( actual. resource_metrics. len( ) , 1 ) ;
1640+
1641+ let resource_metric = & actual. resource_metrics [ 0 ] ;
1642+ assert_eq ! (
1643+ resource_metric. resource. as_ref( ) . unwrap( ) . attributes. len( ) ,
1644+ 1
1645+ ) ;
1646+ assert_eq ! (
1647+ resource_metric. resource. as_ref( ) . unwrap( ) . attributes[ 0 ] . key,
1648+ "service.name"
1649+ ) ;
1650+ assert ! ( resource_metric. resource. as_ref( ) . unwrap( ) . attributes[ 0 ]
1651+ . value
1652+ . is_none( ) ) ;
1653+
1654+ assert_eq ! ( resource_metric. scope_metrics. len( ) , 1 ) ;
1655+
1656+ let scope_metric = & resource_metric. scope_metrics [ 0 ] ;
1657+ assert ! ( scope_metric. scope. is_none( ) ) ;
1658+ assert_eq ! ( scope_metric. metrics. len( ) , 1 ) ;
1659+
1660+ let metric = & scope_metric. metrics [ 0 ] ;
1661+ assert_eq ! ( metric. name, "example_metric" ) ;
1662+ assert_eq ! ( metric. description, "A sample metric with NaN values" ) ;
1663+ assert_eq ! ( metric. unit, "1" ) ;
1664+
1665+ if let Some ( opentelemetry_proto:: tonic:: metrics:: v1:: metric:: Data :: Summary ( summary) ) =
1666+ & metric. data
1667+ {
1668+ assert_eq ! ( summary. data_points. len( ) , 1 ) ;
1669+
1670+ let data_point = & summary. data_points [ 0 ] ;
1671+ assert_eq ! ( data_point. attributes. len( ) , 0 ) ;
1672+ assert_eq ! ( data_point. start_time_unix_nano, 0 ) ;
1673+ assert_eq ! ( data_point. time_unix_nano, 0 ) ;
1674+ assert_eq ! ( data_point. count, 100 ) ;
1675+ assert_eq ! ( data_point. sum, 500.0 ) ;
1676+
1677+ assert_eq ! ( data_point. quantile_values. len( ) , 2 ) ;
1678+
1679+ // Verify that quantile values are NaN
1680+ assert ! ( data_point. quantile_values[ 0 ] . value. is_nan( ) ) ;
1681+ assert ! ( data_point. quantile_values[ 1 ] . value. is_nan( ) ) ;
1682+ } else {
1683+ panic ! ( "Expected metric data to be of type Summary" ) ;
1684+ }
1685+ }
1686+ }
15061687}
0 commit comments