use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use tempfile::TempDir;
use super::record::{ManagedSessionId, ManagedSessionState};
use super::tests::make_manager;
struct CountingDaemon {
connections: Arc<AtomicUsize>,
data_dir: TempDir,
}
impl CountingDaemon {
async fn start() -> Self {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let connections = Arc::new(AtomicUsize::new(0));
let counter = Arc::clone(&connections);
tokio::spawn(async move {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
while let Ok((mut sock, _)) = listener.accept().await {
counter.fetch_add(1, Ordering::SeqCst);
let mut buf = [0u8; 2048];
let _ = sock.read(&mut buf).await;
let _ = sock
.write_all(
b"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\
Content-Length: 36\r\n\r\n{\"removed\":true,\"data_deleted\":true}",
)
.await;
let _ = sock.shutdown().await;
}
});
let data_dir = crate::test_support::hermetic_temp_dir();
std::fs::create_dir_all(data_dir.path().join("trusty-search")).unwrap();
std::fs::write(
data_dir.path().join("trusty-search").join("http_addr"),
addr.to_string(),
)
.unwrap();
Self {
connections,
data_dir,
}
}
async fn self_check(&self) {
let body = reqwest::Client::new()
.get(format!(
"http://{}/health",
std::fs::read_to_string(
self.data_dir.path().join("trusty-search").join("http_addr")
)
.unwrap()
))
.send()
.await
.expect("the stand-in daemon must answer — the fixture's premise")
.text()
.await
.unwrap();
assert!(body.contains("removed"), "unexpected body: {body}");
self.connections.store(0, Ordering::SeqCst);
}
fn seen(&self) -> usize {
self.connections.load(Ordering::SeqCst)
}
}
#[serial_test::serial]
#[tokio::test]
async fn decommission_issues_no_request_to_a_live_daemon_under_test() {
let daemon = CountingDaemon::start().await;
daemon.self_check().await;
let dir = crate::test_support::hermetic_temp_dir();
let (mgr, _fake) = make_manager(&dir).await;
let managed_root = crate::test_support::hermetic_temp_dir();
let workspace_path = managed_root.path().join("owner").join("repo").join("full");
std::fs::create_dir_all(&workspace_path).unwrap();
let record = mgr
.create_with_id(
ManagedSessionId::new(),
"task".into(),
Some(workspace_path.clone()),
None,
Some(workspace_path.clone()),
None,
None,
crate::runtime::RuntimeKind::default(),
false,
true, )
.await
.expect("create");
unsafe {
std::env::set_var(trusty_common::DATA_DIR_OVERRIDE_ENV, daemon.data_dir.path());
}
let outcome = mgr
.decommission_with_root(&record.id, managed_root.path(), None)
.await;
unsafe {
std::env::remove_var(trusty_common::DATA_DIR_OVERRIDE_ENV);
}
let (tombstone, _removed) = outcome.expect("full decommission");
assert_eq!(
tombstone.state,
ManagedSessionState::Decommissioned,
"the guard must not change what decommission does to the record"
);
assert_eq!(
daemon.seen(),
0,
"a test process must reach the trusty-search daemon zero times — a DELETE here \
carries ?delete_data=true and destroys a real index named `full` (#4743)"
);
}
#[serial_test::serial]
#[tokio::test]
async fn sweep_makes_no_request_under_a_test_harness() {
let daemon = CountingDaemon::start().await;
daemon.self_check().await;
let dir = crate::test_support::hermetic_temp_dir();
let (mgr, _fake) = make_manager(&dir).await;
unsafe {
std::env::set_var(trusty_common::DATA_DIR_OVERRIDE_ENV, daemon.data_dir.path());
}
let swept = mgr.sweep_orphaned_search_indexes(false).await;
unsafe {
std::env::remove_var(trusty_common::DATA_DIR_OVERRIDE_ENV);
}
assert_eq!(
swept.expect("sweep must not error").len(),
0,
"a refused sweep reports nothing removed"
);
assert_eq!(
daemon.seen(),
0,
"the orphan sweep must not even LIST indexes from a test process (#4743)"
);
}