Skip to content

Commit b453594

Browse files
committed
Add full e2e smoke test: search → select → view concatenated trace
Exercises the complete browser flow against s3s-fs: 1. Upload gzipped trace segments 2. Search via /api/search 3. Build trace URL from search results (like browser.html does) 4. Fetch concatenated trace via /api/trace 5. Verify both segments present in output
1 parent 44df421 commit b453594

3 files changed

Lines changed: 72 additions & 0 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dial9-viewer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ s3s = "0.13.0"
3333
s3s-fs = "0.13.0"
3434
s3s-aws = "0.13.0"
3535
tempfile = "3"
36+
urlencoding = "2"

dial9-viewer/tests/server_test.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)