@@ -331,3 +331,73 @@ async fn trace_handles_uncompressed_data() {
331331 let body = resp. bytes ( ) . await . unwrap ( ) ;
332332 check ! ( body. as_ref( ) == data) ;
333333}
334+
335+ /// Full end-to-end smoke test: simulates the browser flow.
336+ /// 1. Upload gzipped trace segments to fake S3
337+ /// 2. Search for them via /api/search
338+ /// 3. Pick keys from the search results
339+ /// 4. Fetch concatenated trace via /api/trace
340+ /// 5. Verify the concatenated output matches the original data
341+ #[ tokio:: test]
342+ async fn e2e_search_then_view ( ) {
343+ let ( s3, base, _dir) = setup_s3_test ( "traces-bucket" , Some ( "traces-bucket" . into ( ) ) , None ) . await ;
344+ let client = reqwest:: Client :: new ( ) ;
345+
346+ // Simulate two trace segments from the same time bucket
347+ let seg1 = b"SEGMENT_ONE_BINARY_DATA_HERE" ;
348+ let seg2 = b"SEGMENT_TWO_BINARY_DATA_HERE" ;
349+
350+ put_object (
351+ & s3,
352+ "traces-bucket" ,
353+ "2026-04-09/1910/checkout-api/us-east-1/host1/1000-0.bin.gz" ,
354+ & gzip_bytes ( seg1) ,
355+ )
356+ . await ;
357+ put_object (
358+ & s3,
359+ "traces-bucket" ,
360+ "2026-04-09/1910/checkout-api/us-east-1/host1/1000-1.bin.gz" ,
361+ & gzip_bytes ( seg2) ,
362+ )
363+ . await ;
364+
365+ // Step 1: Search — like the browser would
366+ let search_resp: Vec < ObjectInfo > = client
367+ . get ( format ! (
368+ "{base}/api/search?q=2026-04-09/1910/checkout-api/&bucket=traces-bucket"
369+ ) )
370+ . send ( )
371+ . await
372+ . unwrap ( )
373+ . json ( )
374+ . await
375+ . unwrap ( ) ;
376+
377+ check ! ( search_resp. len( ) == 2 ) ;
378+
379+ // Step 2: Build the trace URL from search results — like the browser's viewSelected()
380+ let keys: Vec < & str > = search_resp. iter ( ) . map ( |o| o. key . as_str ( ) ) . collect ( ) ;
381+ let keys_param = keys. join ( "," ) ;
382+
383+ let trace_resp = client
384+ . get ( format ! (
385+ "{base}/api/trace?keys={}&bucket=traces-bucket" ,
386+ urlencoding:: encode( & keys_param)
387+ ) )
388+ . send ( )
389+ . await
390+ . unwrap ( ) ;
391+
392+ check ! ( trace_resp. status( ) . as_u16( ) == 200 ) ;
393+ let body = trace_resp. bytes ( ) . await . unwrap ( ) ;
394+
395+ // The concatenated output should be seg1 + seg2 (order depends on S3 listing)
396+ check ! ( body. len( ) == seg1. len( ) + seg2. len( ) ) ;
397+ // Both segments should be present
398+ let body_slice = body. as_ref ( ) ;
399+ let has_seg1 = body_slice. windows ( seg1. len ( ) ) . any ( |w| w == seg1. as_slice ( ) ) ;
400+ let has_seg2 = body_slice. windows ( seg2. len ( ) ) . any ( |w| w == seg2. as_slice ( ) ) ;
401+ check ! ( has_seg1) ;
402+ check ! ( has_seg2) ;
403+ }
0 commit comments