@@ -1303,6 +1303,77 @@ mod tests {
13031303 assert_eq ! ( stats. total_cost_estimate, 4.0 , "1.5 + 0.5 + 2.0" ) ;
13041304 }
13051305
1306+ /// Exact aggregate values so mutations in get_event_stats (avg, percentile formula) are caught.
1307+ #[ tokio:: test]
1308+ async fn test_get_event_stats_exact_aggregates ( ) {
1309+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
1310+ let backend = JsonStorageBackend :: new ( & dir. path ( ) . join ( "reviews.json" ) ) ;
1311+
1312+ let now = now_ts ( ) ;
1313+ for ( i, & dur) in [ 100_u64 , 200 , 300 ] . iter ( ) . enumerate ( ) {
1314+ let mut s = make_session_with_event (
1315+ & format ! ( "r{}" , i) ,
1316+ now + i as i64 ,
1317+ ReviewStatus :: Complete ,
1318+ "review.completed" ,
1319+ "gpt-4o" ,
1320+ "head" ,
1321+ dur,
1322+ ) ;
1323+ if let Some ( ref mut e) = s. event {
1324+ e. overall_score = Some ( ( i as f32 + 1.0 ) * 2.0 ) ; // 2.0, 4.0, 6.0 -> avg 4.0
1325+ }
1326+ backend. save_review ( & s) . await . unwrap ( ) ;
1327+ }
1328+
1329+ let stats = backend
1330+ . get_event_stats ( & EventFilters :: default ( ) )
1331+ . await
1332+ . unwrap ( ) ;
1333+
1334+ assert_eq ! ( stats. total_reviews, 3 ) ;
1335+ assert_eq ! ( stats. avg_duration_ms, 200.0 , " (100+200+300)/3 " ) ;
1336+ assert_eq ! (
1337+ stats. p50_latency_ms, 200 ,
1338+ " percentile 50 of [100,200,300] "
1339+ ) ;
1340+ assert_eq ! ( stats. p95_latency_ms, 300 , " percentile 95 " ) ;
1341+ assert_eq ! ( stats. p99_latency_ms, 300 , " percentile 99 " ) ;
1342+ assert_eq ! ( stats. avg_score, Some ( 4.0 ) , " (2+4+6)/3 " ) ;
1343+ assert_eq ! ( stats. by_model. len( ) , 1 ) ;
1344+ assert_eq ! ( stats. by_model[ 0 ] . model, "gpt-4o" ) ;
1345+ assert_eq ! ( stats. by_model[ 0 ] . count, 3 ) ;
1346+ assert_eq ! ( stats. by_model[ 0 ] . avg_duration_ms, 200.0 ) ;
1347+ }
1348+
1349+ /// time_to filter: event with created_at == time_to must be included (<=).
1350+ #[ tokio:: test]
1351+ async fn test_list_events_time_to_inclusive ( ) {
1352+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
1353+ let backend = JsonStorageBackend :: new ( & dir. path ( ) . join ( "reviews.json" ) ) ;
1354+
1355+ let t = chrono:: Utc :: now ( ) ;
1356+ let mut s = make_session_with_event (
1357+ "r1" ,
1358+ 0 ,
1359+ ReviewStatus :: Complete ,
1360+ "review.completed" ,
1361+ "gpt-4o" ,
1362+ "head" ,
1363+ 100 ,
1364+ ) ;
1365+ s. event . as_mut ( ) . unwrap ( ) . created_at = Some ( t) ;
1366+ backend. save_review ( & s) . await . unwrap ( ) ;
1367+
1368+ let filters = EventFilters {
1369+ time_from : Some ( t - chrono:: Duration :: hours ( 1 ) ) ,
1370+ time_to : Some ( t) ,
1371+ ..EventFilters :: default ( )
1372+ } ;
1373+ let events = backend. list_events ( & filters) . await . unwrap ( ) ;
1374+ assert_eq ! ( events. len( ) , 1 , "event at exactly time_to must be included" ) ;
1375+ }
1376+
13061377 // ---------------------------------------------------------------
13071378 // 7. is_empty (via internal state check)
13081379 // ---------------------------------------------------------------
0 commit comments