use std::path::PathBuf;
use std::time::Duration;
use aa_runtime::config::RuntimeConfig;
use aa_runtime::pipeline::enforcement::DEFAULT_MAX_FIELD_BYTES;
fn lifecycle_config(agent_id: &str) -> RuntimeConfig {
RuntimeConfig {
agent_id: agent_id.to_string(),
agent_team_id: String::new(),
agent_org_id: String::new(),
worker_threads: 0,
shutdown_timeout_secs: 10,
ipc_max_connections: 8,
pipeline_input_buffer: 1_000,
pipeline_batch_size: 16,
pipeline_flush_interval_ms: 50,
pipeline_broadcast_capacity: 256,
metrics_addr: "127.0.0.1:0".to_string(),
policy_path: None,
gateway_endpoint: None,
correlation_window_ms: 500,
correlation_interval_ms: 50,
nats_config_path: None,
audit_buffer_path: std::env::temp_dir().join(format!("aa-audit-buffer-{agent_id}.db")),
enforcement_max_field_bytes: DEFAULT_MAX_FIELD_BYTES,
gateway_fail_closed: true,
}
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn run_starts_subsystems_and_shuts_down_on_sigterm() {
let agent_id = format!("lifecycle-test-{}", std::process::id());
let socket_path = PathBuf::from(format!("/tmp/aa-runtime-{agent_id}.sock"));
let _ = std::fs::remove_file(&socket_path);
let config = lifecycle_config(&agent_id);
let runtime = tokio::spawn(aa_runtime::run(config));
let mut bound = false;
for _ in 0..200 {
if socket_path.exists() {
bound = true;
break;
}
tokio::time::sleep(Duration::from_millis(25)).await;
}
assert!(bound, "runtime never bound its IPC socket — startup did not complete");
tokio::time::sleep(Duration::from_millis(500)).await;
let rc = unsafe { libc::raise(libc::SIGTERM) };
assert_eq!(rc, 0, "failed to raise SIGTERM");
let result = tokio::time::timeout(Duration::from_secs(15), runtime).await;
let join = result.expect("run() did not return within 15s of SIGTERM — shutdown hung");
join.expect("run() task panicked during shutdown");
assert!(
!socket_path.exists(),
"IPC socket should be cleaned up after graceful shutdown"
);
}