@@ -2202,7 +2202,11 @@ mod tests {
22022202 fn create_range_engine_self_keyed (
22032203 metric : & str ,
22042204 aggregated_label : & str ,
2205- data : Vec < ( u64 , CountMinSketchWithHeapAccumulator ) > ,
2205+ data : Vec < (
2206+ u64 ,
2207+ Option < KeyByLabelValues > ,
2208+ CountMinSketchWithHeapAccumulator ,
2209+ ) > ,
22062210 promql_query : & str ,
22072211 ) -> SimpleEngine {
22082212 let mut aggregation_configs = HashMap :: new ( ) ;
@@ -2239,8 +2243,8 @@ mod tests {
22392243 CleanupPolicy :: NoCleanup ,
22402244 ) ) ;
22412245
2242- for ( timestamp, acc) in data {
2243- let output = PrecomputedOutput :: new ( timestamp - WINDOW_MS , timestamp, None , 1 ) ;
2246+ for ( timestamp, key , acc) in data {
2247+ let output = PrecomputedOutput :: new ( timestamp - WINDOW_MS , timestamp, key , 1 ) ;
22442248 store
22452249 . insert_precomputed_output ( output, Box :: new ( acc) )
22462250 . unwrap ( ) ;
@@ -2287,8 +2291,12 @@ mod tests {
22872291 sketch. inner . update ( "host-c" , 10.0 ) ;
22882292
22892293 let query = "topk(5, transfer_events)" ;
2290- let engine =
2291- create_range_engine_self_keyed ( "transfer_events" , "srcip" , vec ! [ ( 1000 , sketch) ] , query) ;
2294+ let engine = create_range_engine_self_keyed (
2295+ "transfer_events" ,
2296+ "srcip" ,
2297+ vec ! [ ( 1000 , None , sketch) ] ,
2298+ query,
2299+ ) ;
22922300
22932301 let result = engine
22942302 . handle_range_query_promql ( query. to_string ( ) , 1.0 , 1.5 , 1.0 )
@@ -2333,6 +2341,61 @@ mod tests {
23332341 ) ;
23342342 }
23352343
2344+ #[ tokio:: test( flavor = "multi_thread" ) ]
2345+ async fn range_query_empty_self_keyed_topk_does_not_emit_outer_key ( ) {
2346+ let query = "topk(5, transfer_events)" ;
2347+ let engine = create_range_engine_self_keyed (
2348+ "transfer_events" ,
2349+ "srcip" ,
2350+ vec ! [ (
2351+ 1000 ,
2352+ Some ( KeyByLabelValues :: new( ) ) ,
2353+ CountMinSketchWithHeapAccumulator :: new( 3 , 1024 , 32 ) ,
2354+ ) ] ,
2355+ query,
2356+ ) ;
2357+
2358+ let result = engine
2359+ . handle_range_query_promql ( query. to_string ( ) , 1.0 , 1.5 , 1.0 )
2360+ . expect ( "native query execution should not fail" ) ;
2361+ let ( _, query_result) = result. expect ( "range query failed" ) ;
2362+
2363+ assert ! (
2364+ matrix_values( query_result) . is_empty( ) ,
2365+ "an empty self-keyed heap must not become an unlabeled result"
2366+ ) ;
2367+ }
2368+
2369+ #[ tokio:: test( flavor = "multi_thread" ) ]
2370+ async fn range_query_global_sum_returns_one_unlabeled_series ( ) {
2371+ let query = "sum(requests)" ;
2372+ let engine = create_engine_multi_timestamp_with_window (
2373+ "requests" ,
2374+ AggregationType :: Sum ,
2375+ vec ! [ ] ,
2376+ vec ! [ (
2377+ 1000 ,
2378+ None ,
2379+ Box :: new( SumAccumulator :: with_sum( 42.0 ) ) as Box <dyn AggregateCore >,
2380+ ) ] ,
2381+ query,
2382+ 1000 ,
2383+ 1000 ,
2384+ WindowType :: Tumbling ,
2385+ ) ;
2386+
2387+ let result = engine
2388+ . handle_range_query_promql ( query. to_string ( ) , 1.0 , 1.5 , 1.0 )
2389+ . expect ( "native query execution should not fail" ) ;
2390+ let ( _, query_result) = result. expect ( "range query failed" ) ;
2391+ let elements = matrix_values ( query_result) ;
2392+
2393+ assert_eq ! ( elements. len( ) , 1 ) ;
2394+ assert_eq ! ( elements[ 0 ] . labels, KeyByLabelValues :: new( ) ) ;
2395+ assert_eq ! ( elements[ 0 ] . samples. len( ) , 1 ) ;
2396+ assert_eq ! ( elements[ 0 ] . samples[ 0 ] . value, 42.0 ) ;
2397+ }
2398+
23362399 #[ tokio:: test( flavor = "multi_thread" ) ]
23372400 async fn range_query_set_aggregator_merges_multiple_buckets_within_one_window ( ) {
23382401 // Every other SetAggregator test uses window_size_ms == bucket
0 commit comments