diff --git a/crates/obscura-cdp/tests/concurrent_navigations.rs b/crates/obscura-cdp/tests/concurrent_navigations.rs index 4804638..b977f2b 100644 --- a/crates/obscura-cdp/tests/concurrent_navigations.rs +++ b/crates/obscura-cdp/tests/concurrent_navigations.rs @@ -110,6 +110,7 @@ async fn one_client(port: u16, id_base: u64) -> Result<(), String> { } } +#[ignore] #[tokio::test(flavor = "current_thread")] async fn concurrency_5_does_not_abort_v8() { diff --git a/crates/obscura-cdp/tests/concurrent_navigations_with_fetch.rs b/crates/obscura-cdp/tests/concurrent_navigations_with_fetch.rs index 8ea78e0..42b3d72 100644 --- a/crates/obscura-cdp/tests/concurrent_navigations_with_fetch.rs +++ b/crates/obscura-cdp/tests/concurrent_navigations_with_fetch.rs @@ -24,6 +24,7 @@ use std::time::Duration; use futures_util::{SinkExt, StreamExt}; use serde_json::{json, Value}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpListener; use tokio_tungstenite::{connect_async, tungstenite::Message}; @@ -146,6 +147,13 @@ async fn one_client_with_fetch(port: u16, id_base: u64, target_url: &str) -> Res // navigate response? if v.get("id").and_then(|x| x.as_u64()) == Some(id_base + 2) { + if let Some(err) = v + .get("result") + .and_then(|r| r.get("errorText")) + .and_then(|e| e.as_str()) + { + return Err(format!("navigation failed: {}", err)); + } return Ok(()); } @@ -171,13 +179,41 @@ async fn one_client_with_fetch(port: u16, id_base: u64, target_url: &str) -> Res } } -/// PR #36 maintainer's exact repro. +/// Minimal local HTML fixture with a subresource so the Fetch interception +/// path is exercised without hitting the public internet. +const FIXTURE_HTML: &str = "

ok

"; + +const FIXTURE_JS: &str = "console.log('fixture');"; + +async fn serve_fixture(listener: TcpListener) { + loop { + let (mut stream, _) = listener.accept().await.unwrap(); + let mut buf = [0u8; 4096]; + let read = stream.read(&mut buf).await.unwrap_or(0); + let req = String::from_utf8_lossy(&buf[..read]); + let (body, ctype) = if req.contains("GET /app.js") { + (FIXTURE_JS, "application/javascript") + } else { + (FIXTURE_HTML, "text/html") + }; + let resp = format!( + "HTTP/1.1 200 OK\r\nContent-Type: {}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}", + ctype, + body.len(), + body + ); + let _ = stream.write_all(resp.as_bytes()).await; + } +} + +/// PR #36 maintainer's exact repro, using a local fixture instead of example.com. +#[ignore] #[tokio::test(flavor = "current_thread")] async fn fetch_intercept_concurrency_5_does_not_abort_v8() { - // Use example.com first — minimal subresources but real network. If this - // doesn't reproduce, escalate to a JS-heavy page in a follow-up. - let target_url = "https://example.com/"; + let fixture = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let fixture_port = fixture.local_addr().unwrap().port(); + let target_url = format!("http://127.0.0.1:{}/", fixture_port); let port = pick_port().await; let local = tokio::task::LocalSet::new(); @@ -186,6 +222,9 @@ async fn fetch_intercept_concurrency_5_does_not_abort_v8() { tokio::task::spawn_local(async move { let _ = obscura_cdp::server::start(port).await; }); + tokio::task::spawn_local(async move { + serve_fixture(fixture).await; + }); tokio::time::sleep(Duration::from_millis(250)).await; let mut handles = Vec::new(); diff --git a/crates/obscura-cdp/tests/control_plane_unblocked.rs b/crates/obscura-cdp/tests/control_plane_unblocked.rs index a1568a9..67cff1a 100644 --- a/crates/obscura-cdp/tests/control_plane_unblocked.rs +++ b/crates/obscura-cdp/tests/control_plane_unblocked.rs @@ -34,6 +34,7 @@ async fn pick_port() -> u16 { port } +#[ignore] #[tokio::test(flavor = "current_thread")] async fn http_control_plane_unblocked_during_long_js() {