aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-07-24 02:49:33 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-07-24 02:50:58 +0300
commite608a75159fc5cca70485d83ffb8ee681c9da385 (patch)
tree5ffdec62d0bee9a798418d6414ecbddae7ff8ae5 /tests
parent169a4fd90c20a00aec77d684ecec98b9f0f996cc (diff)
fix: make known-agent exit process-owned
refs #1792 refs #1648
Diffstat (limited to 'tests')
-rw-r--r--tests/api_ping.rs48
-rw-r--r--tests/cli/agent_wait.rs4
-rw-r--r--tests/cli/sessions.rs2
-rw-r--r--tests/live_handoff.rs89
4 files changed, 115 insertions, 28 deletions
diff --git a/tests/api_ping.rs b/tests/api_ping.rs
index d05f3f7f..344fe6ec 100644
--- a/tests/api_ping.rs
+++ b/tests/api_ping.rs
@@ -1699,10 +1699,19 @@ fn pane_report_agent_updates_effective_state() {
}
let session_path = base.join("pi-session.jsonl");
+ let session = send_request(
+ &socket_path,
+ &format!(
+ r#"{{"id":"req_hook_session","method":"pane.report_agent_session","params":{{"pane_id":"{}","source":"herdr:pi","agent":"pi","agent_session_path":"{}","session_start_source":"startup","seq":1}}}}"#,
+ pane_id,
+ session_path.display()
+ ),
+ );
+ assert_eq!(session["result"]["type"], "ok");
let hook = send_request(
&socket_path,
&format!(
- r#"{{"id":"req_hook_5","method":"pane.report_agent","params":{{"pane_id":"{}","source":"herdr:pi","agent":"pi","state":"working","message":"thinking","agent_session_path":"{}"}}}}"#,
+ r#"{{"id":"req_hook_5","method":"pane.report_agent","params":{{"pane_id":"{}","source":"herdr:pi","agent":"pi","state":"working","message":"thinking","agent_session_path":"{}","seq":2}}}}"#,
pane_id,
session_path.display()
),
@@ -1875,7 +1884,7 @@ fn pane_report_agent_accepts_unknown_agent_labels() {
#[cfg(not(target_os = "macos"))]
#[test]
-fn pane_release_agent_suppresses_reacquire_during_graceful_exit() {
+fn official_release_waits_for_confirmed_process_exit() {
let _lock = test_lock();
let base = unique_test_dir();
let config_home = base.join("config");
@@ -1960,11 +1969,22 @@ fn pane_release_agent_suppresses_reacquire_during_graceful_exit() {
thread::sleep(Duration::from_millis(100));
}
+ let session_path = base.join("release-session.jsonl");
+ let session = send_request(
+ &socket_path,
+ &format!(
+ r#"{{"id":"req_release_session","method":"pane.report_agent_session","params":{{"pane_id":"{}","source":"herdr:pi","agent":"pi","agent_session_path":"{}","session_start_source":"startup","seq":1}}}}"#,
+ pane_id,
+ session_path.display()
+ ),
+ );
+ assert_eq!(session["result"]["type"], "ok");
let hook = send_request(
&socket_path,
&format!(
- r#"{{"id":"req_release_4","method":"pane.report_agent","params":{{"pane_id":"{}","source":"herdr:pi","agent":"pi","state":"working"}}}}"#,
- pane_id
+ r#"{{"id":"req_release_4","method":"pane.report_agent","params":{{"pane_id":"{}","source":"herdr:pi","agent":"pi","state":"working","agent_session_path":"{}","seq":2}}}}"#,
+ pane_id,
+ session_path.display()
),
);
assert_eq!(hook["result"]["type"], "ok");
@@ -1978,8 +1998,8 @@ fn pane_release_agent_suppresses_reacquire_during_graceful_exit() {
);
assert_eq!(released["result"]["type"], "ok");
- let suppression_deadline = Instant::now() + Duration::from_millis(300);
- while Instant::now() < suppression_deadline {
+ let release_observation_deadline = Instant::now() + Duration::from_millis(300);
+ while Instant::now() < release_observation_deadline {
let pane = send_request(
&socket_path,
&format!(
@@ -1987,11 +2007,11 @@ fn pane_release_agent_suppresses_reacquire_during_graceful_exit() {
pane_id
),
);
- assert!(
- pane["result"]["pane"]["agent"].is_null(),
- "pane reacquired pi during graceful release: {pane}"
+ assert_eq!(
+ pane["result"]["pane"]["agent"], "pi",
+ "official release hid the live Pi process: {pane}"
);
- assert_eq!(pane["result"]["pane"]["agent_status"], "unknown");
+ assert_eq!(pane["result"]["pane"]["agent_status"], "working");
thread::sleep(Duration::from_millis(50));
}
@@ -2013,7 +2033,7 @@ fn pane_release_agent_suppresses_reacquire_during_graceful_exit() {
}
assert!(
Instant::now() < cleared_deadline,
- "pi agent was not cleared promptly after release: {pane}"
+ "pi agent was not cleared promptly after process exit: {pane}"
);
thread::sleep(Duration::from_millis(50));
}
@@ -2443,7 +2463,7 @@ fn metadata_status_subscription_filter_and_ttl_expiry_are_observable() {
let report_agent = send_request(
&socket_path,
&format!(
- r#"{{"id":"req_meta_sub_2","method":"pane.report_agent","params":{{"pane_id":"{}","source":"herdr:pi","agent":"pi","state":"working"}}}}"#,
+ r#"{{"id":"req_meta_sub_2","method":"pane.report_agent","params":{{"pane_id":"{}","source":"custom:pi","agent":"pi","state":"working"}}}}"#,
pane_id
),
);
@@ -2463,7 +2483,7 @@ fn metadata_status_subscription_filter_and_ttl_expiry_are_observable() {
let metadata = send_request(
&socket_path,
&format!(
- r#"{{"id":"req_meta_sub_3","method":"pane.report_metadata","params":{{"pane_id":"{}","source":"user:pi-display","agent":"pi","applies_to_source":"herdr:pi","title":"filtered out"}}}}"#,
+ r#"{{"id":"req_meta_sub_3","method":"pane.report_metadata","params":{{"pane_id":"{}","source":"user:pi-display","agent":"pi","applies_to_source":"custom:pi","title":"filtered out"}}}}"#,
pane_id
),
);
@@ -2489,7 +2509,7 @@ fn metadata_status_subscription_filter_and_ttl_expiry_are_observable() {
let metadata = send_request(
&socket_path,
&format!(
- r#"{{"id":"req_meta_sub_4","method":"pane.report_metadata","params":{{"pane_id":"{}","source":"user:pi-display","agent":"pi","applies_to_source":"herdr:pi","title":"short lived","ttl_ms":100}}}}"#,
+ r#"{{"id":"req_meta_sub_4","method":"pane.report_metadata","params":{{"pane_id":"{}","source":"user:pi-display","agent":"pi","applies_to_source":"custom:pi","title":"short lived","ttl_ms":100}}}}"#,
pane_id
),
);
diff --git a/tests/cli/agent_wait.rs b/tests/cli/agent_wait.rs
index 11997d00..11da467d 100644
--- a/tests/cli/agent_wait.rs
+++ b/tests/cli/agent_wait.rs
@@ -26,7 +26,7 @@ fn agent_wait_exits_immediately_when_status_already_matches() {
let reported = send_request(
&socket_path,
&format!(
- r#"{{"id":"req_cli_immediate_2","method":"pane.report_agent","params":{{"pane_id":"{}","source":"herdr:pi","agent":"pi","state":"idle"}}}}"#,
+ r#"{{"id":"req_cli_immediate_2","method":"pane.report_agent","params":{{"pane_id":"{}","source":"custom:test","agent":"pi","state":"idle"}}}}"#,
pane_id
),
);
@@ -78,7 +78,7 @@ fn agent_wait_times_out_when_status_does_not_match() {
let reported = send_request(
&socket_path,
&format!(
- r#"{{"id":"req_cli_timeout_2","method":"pane.report_agent","params":{{"pane_id":"{}","source":"herdr:pi","agent":"pi","state":"working"}}}}"#,
+ r#"{{"id":"req_cli_timeout_2","method":"pane.report_agent","params":{{"pane_id":"{}","source":"custom:test","agent":"pi","state":"working"}}}}"#,
pane_id
),
);
diff --git a/tests/cli/sessions.rs b/tests/cli/sessions.rs
index 126cb616..b7655542 100644
--- a/tests/cli/sessions.rs
+++ b/tests/cli/sessions.rs
@@ -230,7 +230,7 @@ fn integration_commands_run_locally_when_server_is_missing() {
.unwrap();
assert_eq!(integration_status.status.code(), Some(0));
let status_stdout = String::from_utf8_lossy(&integration_status.stdout);
- assert!(status_stdout.contains("pi: current (v6)"));
+ assert!(status_stdout.contains("pi: current (v7)"));
assert!(status_stdout.contains("claude: not installed"));
let integration_uninstall = Command::new(env!("CARGO_BIN_EXE_herdr"))
diff --git a/tests/live_handoff.rs b/tests/live_handoff.rs
index c93898b6..4d81246a 100644
--- a/tests/live_handoff.rs
+++ b/tests/live_handoff.rs
@@ -1213,6 +1213,8 @@ fn live_handoff_accepts_canonical_pane_id_from_child_env() {
#[test]
fn live_handoff_keeps_unmanaged_agent_name_bound_to_saved_session() {
+ use std::os::unix::fs::PermissionsExt;
+
let _lock = test_lock();
let base = unique_test_dir();
let config_home = base.join("config");
@@ -1220,6 +1222,18 @@ fn live_handoff_keeps_unmanaged_agent_name_bound_to_saved_session() {
let api_socket = runtime_dir.join("herdr.sock");
let old_session = base.join("old-session.jsonl");
let new_session = base.join("new-session.jsonl");
+ let started_marker = base.join("agent-started");
+ let fake_pi = base.join("pi");
+ fs::create_dir_all(&base).unwrap();
+ fs::write(
+ &fake_pi,
+ format!(
+ "#!/bin/sh\nexport HERDR_AGENT=pi\necho started > {}\nexec /bin/sleep 30\n",
+ started_marker.display()
+ ),
+ )
+ .unwrap();
+ fs::set_permissions(&fake_pi, fs::Permissions::from_mode(0o755)).unwrap();
let spawned = spawn_server(&config_home, &runtime_dir, &api_socket);
wait_for_socket(&api_socket, Duration::from_secs(10));
@@ -1239,6 +1253,30 @@ fn live_handoff_keeps_unmanaged_agent_name_bound_to_saved_session() {
assert_ok(request(
&api_socket,
serde_json::json!({
+ "id": "test:pane:start-agent",
+ "method": "pane.send_input",
+ "params": {"pane_id": pane_id, "text": fake_pi, "keys": ["Enter"]}
+ }),
+ ));
+ support::wait_for_file(&started_marker, Duration::from_secs(5));
+ assert_ok(request(
+ &api_socket,
+ serde_json::json!({
+ "id": "test:agent:session",
+ "method": "pane.report_agent_session",
+ "params": {
+ "pane_id": pane_id,
+ "source": "herdr:pi",
+ "agent": "pi",
+ "seq": 1,
+ "agent_session_path": old_session,
+ "session_start_source": "startup"
+ }
+ }),
+ ));
+ assert_ok(request(
+ &api_socket,
+ serde_json::json!({
"id": "test:agent:report",
"method": "pane.report_agent",
"params": {
@@ -1246,11 +1284,30 @@ fn live_handoff_keeps_unmanaged_agent_name_bound_to_saved_session() {
"source": "herdr:pi",
"agent": "pi",
"state": "idle",
- "seq": 1,
+ "seq": 2,
"agent_session_path": old_session
}
}),
));
+ let deadline = Instant::now() + Duration::from_secs(5);
+ loop {
+ let response = request(
+ &api_socket,
+ serde_json::json!({
+ "id": "test:agent:wait-for-process",
+ "method": "agent.get",
+ "params": {"target": pane_id}
+ }),
+ );
+ if response.get("result").is_some() {
+ break;
+ }
+ assert!(
+ Instant::now() < deadline,
+ "agent process was not detected: {response}"
+ );
+ thread::sleep(Duration::from_millis(25));
+ }
assert_ok(request(
&api_socket,
serde_json::json!({
@@ -1276,21 +1333,31 @@ fn live_handoff_keeps_unmanaged_agent_name_bound_to_saved_session() {
"pane_id": pane_id,
"source": "herdr:pi",
"agent": "pi",
- "seq": 2,
+ "seq": 3,
"agent_session_path": new_session,
"session_start_source": "new"
}
}),
));
- let old_name = request(
- &api_socket,
- serde_json::json!({
- "id": "test:agent:get-old-name",
- "method": "agent.get",
- "params": {"target": "reviewer"}
- }),
- );
- assert_eq!(old_name["error"]["code"], "agent_not_found", "{old_name}");
+ let deadline = Instant::now() + Duration::from_secs(5);
+ loop {
+ let old_name = request(
+ &api_socket,
+ serde_json::json!({
+ "id": "test:agent:get-old-name",
+ "method": "agent.get",
+ "params": {"target": "reviewer"}
+ }),
+ );
+ if old_name["error"]["code"] == "agent_not_found" {
+ break;
+ }
+ assert!(
+ Instant::now() < deadline,
+ "old session alias was not cleared: {old_name}"
+ );
+ thread::sleep(Duration::from_millis(25));
+ }
let _ = request(
&api_socket,