#![cfg(feature = "host")]
#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::disallowed_methods,
clippy::disallowed_macros,
clippy::err_expect,
clippy::print_stdout,
clippy::useless_conversion
)]
mod support;
use std::sync::Arc;
use std::time::Duration;
use agent_first_http::host::bootstrap::{
BrowserChoice, DisplayMode, HealthPublic, HostArgs, ProfileChoice, Takeover,
};
use agent_first_http::host::{browser, listener::router_for_tests, listener::test_state};
use agent_first_http::sdk::Client;
use tokio::net::TcpListener;
async fn spawn_chromium_host() -> Option<(String, tempfile::TempDir)> {
support::ensure_rustls_provider();
let bin = support::env::discover_browser()?;
let args = HostArgs {
listen: "tcp:127.0.0.1:0".into(),
profile: ProfileChoice::Ephemeral,
display: DisplayMode::Headless,
takeover: Takeover::Off,
display_quality: 100,
browser: BrowserChoice::Chromium,
browser_bin: Some(bin),
token: None,
takeover_enabled: true,
health_enabled: true,
health_public: HealthPublic::Off,
engine_envs: Vec::new(),
browser_args: Vec::new(),
proxy: None,
recent_requests_cap: 0,
};
let handle = browser::launch(&args).await.expect("browser launch");
let state = test_state(None, HealthPublic::Off).with_default_browser(Arc::new(handle));
let app = router_for_tests(state);
let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind");
let addr = listener.local_addr().expect("local_addr");
tokio::spawn(async move {
let _ = axum::serve(listener, app).await;
});
tokio::time::sleep(Duration::from_millis(50)).await;
let tmp = tempfile::tempdir().expect("tmpdir");
Some((format!("ws://{addr}"), tmp))
}
#[tokio::test]
async fn target_get_close_round_trip() {
let Some((endpoint, _tmp)) = spawn_chromium_host().await else {
println!("(skipping: no chromium)");
return;
};
let client = Client::connect(&endpoint).expect("client");
let create = client
.cdp("Target.createTarget")
.params(serde_json::json!({ "url": "about:blank" }))
.send()
.await
.expect("Target.createTarget");
let target_id = create["targetId"]
.as_str()
.expect("targetId in createTarget response")
.to_string();
let listed = client
.cdp("Target.getTargets")
.send()
.await
.expect("Target.getTargets");
let targets = listed["targetInfos"].as_array().expect("targetInfos array");
assert!(
targets
.iter()
.any(|t| t["targetId"].as_str() == Some(target_id.as_str())),
"target {target_id} missing from getTargets; got {targets:?}",
);
let close = client
.cdp("Target.closeTarget")
.params(serde_json::json!({ "targetId": target_id.clone() }))
.send()
.await
.expect("Target.closeTarget");
assert_eq!(close["success"], serde_json::Value::Bool(true));
tokio::time::sleep(Duration::from_millis(100)).await;
let after = client
.cdp("Target.getTargets")
.send()
.await
.expect("Target.getTargets after close");
let after_targets = after["targetInfos"]
.as_array()
.expect("after targetInfos array");
assert!(
!after_targets
.iter()
.any(|t| t["targetId"].as_str() == Some(target_id.as_str())),
"target {target_id} still listed after close",
);
}