77//! 2. Explicit timestamps > 1e12 (the Prometheus wire unit: epoch
88//! MILLISECONDS) are normalized to SECONDS (/1000). This is the fact
99//! that forces the vtab's "default_ts is seconds" unit decision.
10- //! 3. Malformed non-comment lines and NaN values are COUNTED as errors
10+ //! 3. Malformed non-comment lines and non-finite values are COUNTED as errors
1111//! but never abort the body — partial success is the contract.
1212//! 4. Comments / HELP / TYPE / blank lines are free: neither samples nor
1313//! errors.
@@ -38,13 +38,18 @@ fn prometheus_ingest_semantics() {
3838 http_requests_total 1027\n \
3939 node_temp_celsius{sensor=\" cpu0\" ,host=\" pvm1\" } 42.5 1753000000123\n \
4040 this line is definitely not prometheus !!!\n \
41- bad_metric NaN\n ";
41+ bad_metric NaN\n \
42+ positive_inf Inf\n \
43+ negative_inf -Inf\n ";
4244
4345 // default_ts is deterministic here (no wall clock in a unit test).
4446 let default_ts: i64 = 1_800_000_000 ;
4547 let ( count, errors) = engine. ingest_prometheus ( body, default_ts) . unwrap ( ) ;
4648 assert_eq ! ( count, 2 , "counter + gauge ingested; comments are free" ) ;
47- assert_eq ! ( errors, 2 , "malformed line + NaN line each count once" ) ;
49+ assert_eq ! (
50+ errors, 4 ,
51+ "malformed line + three non-finite lines count once"
52+ ) ;
4853
4954 // Rule 1: no-timestamp sample carries default_ts VERBATIM (seconds).
5055 let sid = engine
@@ -73,6 +78,29 @@ fn prometheus_ingest_semantics() {
7378 "explicit 1753000000123 ms must be stored as 1753000000 s"
7479 ) ;
7580
81+ let escaped_body =
82+ b"escaped_labels{path=\" a\\ \" b\" ,note=\" x\\ \\ y\" } 7 1800000000000\r \n crlf_metric 9\r \n " ;
83+ let ( count, errors) = engine. ingest_prometheus ( escaped_body, default_ts) . unwrap ( ) ;
84+ assert_eq ! ( ( count, errors) , ( 2 , 0 ) ) ;
85+ let escaped: HashMap < String , String > = [
86+ ( "path" . to_string ( ) , r#"a\"b"# . to_string ( ) ) ,
87+ ( "note" . to_string ( ) , r#"x\\y"# . to_string ( ) ) ,
88+ ]
89+ . into_iter ( )
90+ . collect ( ) ;
91+ let sid = engine. resolve_cached ( "escaped_labels" , & escaped) . unwrap ( ) ;
92+ assert_eq ! (
93+ engine. query_range_by_id( sid, 0 , i64 :: MAX - 1 ) . unwrap( ) ,
94+ vec![ ( 1_800_000_000 , 7.0 ) ]
95+ ) ;
96+ let sid = engine
97+ . resolve_cached ( "crlf_metric" , & HashMap :: new ( ) )
98+ . unwrap ( ) ;
99+ assert_eq ! (
100+ engine. query_range_by_id( sid, 0 , i64 :: MAX - 1 ) . unwrap( ) ,
101+ vec![ ( default_ts, 9.0 ) ]
102+ ) ;
103+
76104 // Rule 3 corollary: an all-garbage body yields (0, N) — the vtab
77105 // turns exactly that shape into its "0 samples ingested" error.
78106 let ( count, errors) = engine
@@ -81,9 +109,9 @@ fn prometheus_ingest_semantics() {
81109 assert_eq ! ( ( count, errors) , ( 0 , 2 ) ) ;
82110
83111 let info = engine. info ( ) ;
84- assert_eq ! ( info. prometheus_ingest_batches, 2 ) ;
85- assert_eq ! ( info. prometheus_ingest_points, 2 ) ;
86- assert_eq ! ( info. prometheus_ingest_errors, 4 ) ;
112+ assert_eq ! ( info. prometheus_ingest_batches, 3 ) ;
113+ assert_eq ! ( info. prometheus_ingest_points, 4 ) ;
114+ assert_eq ! ( info. prometheus_ingest_errors, 6 ) ;
87115 assert ! ( info. prometheus_ingest_total_ns > 0 ) ;
88116
89117 engine. shutdown ( ) . unwrap ( ) ;
0 commit comments