aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-04-09 03:31:37 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-04-09 03:31:37 +0300
commita3ecb4e3b4151e840ad237e073d8282e1181c34a (patch)
tree12c6924cc65c2b9c5da45df2e3a2fde188f07681 /tests
parent05a6d89e33e722376688c679ab0d7322014814a1 (diff)
feat(api): expose done as agent status
Diffstat (limited to 'tests')
-rw-r--r--tests/api_ping.rs142
-rw-r--r--tests/cli_wrapper.rs119
2 files changed, 247 insertions, 14 deletions
diff --git a/tests/api_ping.rs b/tests/api_ping.rs
index a7e61fa6..d5fa190c 100644
--- a/tests/api_ping.rs
+++ b/tests/api_ping.rs
@@ -866,7 +866,7 @@ fn pane_report_agent_updates_effective_state() {
),
);
assert_eq!(pane["result"]["pane"]["agent"], "pi");
- assert_eq!(pane["result"]["pane"]["agent_state"], "working");
+ assert_eq!(pane["result"]["pane"]["agent_status"], "working");
cleanup_spawned_herdr(child, base);
}
@@ -989,7 +989,7 @@ fn pane_release_agent_suppresses_reacquire_during_graceful_exit() {
pane["result"]["pane"]["agent"].is_null(),
"pane reacquired pi during graceful release: {pane}"
);
- assert_eq!(pane["result"]["pane"]["agent_state"], "unknown");
+ assert_eq!(pane["result"]["pane"]["agent_status"], "unknown");
thread::sleep(Duration::from_millis(50));
}
@@ -1005,7 +1005,7 @@ fn pane_release_agent_suppresses_reacquire_during_graceful_exit() {
),
);
if pane["result"]["pane"]["agent"].is_null()
- && pane["result"]["pane"]["agent_state"] == "unknown"
+ && pane["result"]["pane"]["agent_status"] == "unknown"
{
break;
}
@@ -1105,7 +1105,7 @@ fn pane_clear_agent_authority_restores_fallback_state() {
pane_id
),
);
- let fallback_state = fallback_before_hook["result"]["pane"]["agent_state"]
+ let fallback_status = fallback_before_hook["result"]["pane"]["agent_status"]
.as_str()
.unwrap()
.to_string();
@@ -1136,13 +1136,13 @@ fn pane_clear_agent_authority_restores_fallback_state() {
),
);
assert_eq!(pane["result"]["pane"]["agent"], "pi");
- assert_eq!(pane["result"]["pane"]["agent_state"], fallback_state);
+ assert_eq!(pane["result"]["pane"]["agent_status"], fallback_status);
cleanup_spawned_herdr(child, base);
}
#[test]
-fn events_subscribe_streams_output_and_agent_state_events() {
+fn events_subscribe_streams_output_and_agent_status_events() {
let _lock = test_lock();
let base = unique_test_dir();
let config_home = base.join("config");
@@ -1196,7 +1196,7 @@ fn events_subscribe_streams_output_and_agent_state_events() {
let mut reader = open_subscription(
&socket_path,
&format!(
- r#"{{"id":"sub_1","method":"events.subscribe","params":{{"subscriptions":[{{"type":"pane.output_matched","pane_id":"{}","source":"recent","lines":40,"match":{{"type":"substring","value":"hello from socket"}}}},{{"type":"pane.agent_state_changed","pane_id":"{}","state":"idle"}}]}}}}"#,
+ r#"{{"id":"sub_1","method":"events.subscribe","params":{{"subscriptions":[{{"type":"pane.output_matched","pane_id":"{}","source":"recent","lines":40,"match":{{"type":"substring","value":"hello from socket"}}}},{{"type":"pane.agent_status_changed","pane_id":"{}","agent_status":"idle"}}]}}}}"#,
pane_id, pane_id,
),
);
@@ -1252,10 +1252,134 @@ fn events_subscribe_streams_output_and_agent_state_events() {
assert_eq!(send_enter["result"]["type"], "ok");
let agent_idle = reader.read_json_line(Duration::from_secs(8));
- assert_eq!(agent_idle["event"], "pane.agent_state_changed");
+ assert_eq!(agent_idle["event"], "pane.agent_status_changed");
assert_eq!(agent_idle["data"]["pane_id"], pane_id);
- assert_eq!(agent_idle["data"]["state"], "idle");
+ assert_eq!(agent_idle["data"]["agent_status"], "idle");
assert_eq!(agent_idle["data"]["agent"], "pi");
cleanup_spawned_herdr(child, base);
}
+
+#[test]
+fn pane_info_and_subscriptions_expose_done_agent_status() {
+ let _lock = test_lock();
+ let base = unique_test_dir();
+ let config_home = base.join("config");
+ let runtime_dir = base.join("runtime");
+ let socket_path = runtime_dir.join("herdr.sock");
+ let bin_dir = base.join("bin");
+
+ fs::create_dir_all(&bin_dir).unwrap();
+ let fake_pi = bin_dir.join("pi");
+ fs::write(
+ &fake_pi,
+ "#!/bin/sh\nprintf 'Working...\\n'\nsleep 1\nprintf '\\033[2J\\033[Hdone\\n'\n",
+ )
+ .unwrap();
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::PermissionsExt;
+ let mut perms = fs::metadata(&fake_pi).unwrap().permissions();
+ perms.set_mode(0o755);
+ fs::set_permissions(&fake_pi, perms).unwrap();
+ }
+
+ let inherited_path = std::env::var("PATH").unwrap_or_default();
+ let path_override = format!("{}:{}", bin_dir.display(), inherited_path);
+ let child = spawn_herdr_with_path(
+ &config_home,
+ &runtime_dir,
+ &socket_path,
+ Some(Path::new(&path_override)),
+ );
+ wait_for_socket(&socket_path, Duration::from_secs(5));
+
+ let created = send_request(
+ &socket_path,
+ &format!(
+ r#"{{"id":"req_status_1","method":"workspace.create","params":{{"cwd":"{}","focus":true}}}}"#,
+ base.display()
+ ),
+ );
+ let workspace_id = created["result"]["workspace"]["workspace_id"]
+ .as_str()
+ .unwrap()
+ .to_string();
+ let background_pane_id = format!("{}-1", workspace_id);
+
+ let tab_created = send_request(
+ &socket_path,
+ &format!(
+ r#"{{"id":"req_status_2","method":"tab.create","params":{{"workspace_id":"{}","focus":true}}}}"#,
+ workspace_id
+ ),
+ );
+ assert_eq!(tab_created["result"]["type"], "tab_info");
+
+ let mut reader = open_subscription(
+ &socket_path,
+ &format!(
+ r#"{{"id":"sub_status","method":"events.subscribe","params":{{"subscriptions":[{{"type":"pane.agent_status_changed","pane_id":"{}","agent_status":"done"}}]}}}}"#,
+ background_pane_id,
+ ),
+ );
+ let ack = reader.read_json_line(Duration::from_secs(2));
+ assert_eq!(ack["id"], "sub_status");
+ assert_eq!(ack["result"]["type"], "subscription_started");
+
+ let send_pi = send_request(
+ &socket_path,
+ &format!(
+ r#"{{"id":"req_status_3","method":"pane.send_text","params":{{"pane_id":"{}","text":"pi"}}}}"#,
+ background_pane_id
+ ),
+ );
+ assert_eq!(send_pi["result"]["type"], "ok");
+ let send_enter = send_request(
+ &socket_path,
+ &format!(
+ r#"{{"id":"req_status_4","method":"pane.send_keys","params":{{"pane_id":"{}","keys":["Enter"]}}}}"#,
+ background_pane_id
+ ),
+ );
+ assert_eq!(send_enter["result"]["type"], "ok");
+
+ let status_event = reader.read_json_line(Duration::from_secs(8));
+ assert_eq!(status_event["event"], "pane.agent_status_changed");
+ assert_eq!(status_event["data"]["pane_id"], background_pane_id);
+ assert_eq!(status_event["data"]["agent_status"], "done");
+ assert_eq!(status_event["data"]["agent"], "pi");
+
+ let pane = send_request(
+ &socket_path,
+ &format!(
+ r#"{{"id":"req_status_5","method":"pane.get","params":{{"pane_id":"{}"}}}}"#,
+ background_pane_id
+ ),
+ );
+ assert_eq!(pane["result"]["pane"]["agent_status"], "done");
+
+ let focused_tab_id = created["result"]["workspace"]["active_tab_id"]
+ .as_str()
+ .unwrap()
+ .to_string();
+ let tab_focus = send_request(
+ &socket_path,
+ &format!(
+ r#"{{"id":"req_status_6","method":"tab.focus","params":{{"tab_id":"{}"}}}}"#,
+ focused_tab_id
+ ),
+ );
+ assert_eq!(tab_focus["result"]["type"], "tab_info");
+
+ let pane_after_focus = send_request(
+ &socket_path,
+ &format!(
+ r#"{{"id":"req_status_7","method":"pane.get","params":{{"pane_id":"{}"}}}}"#,
+ background_pane_id
+ ),
+ );
+ assert_eq!(pane_after_focus["result"]["pane"]["agent_status"], "idle");
+
+ cleanup_spawned_herdr(child, base);
+}
diff --git a/tests/cli_wrapper.rs b/tests/cli_wrapper.rs
index 3e9b9f31..1395008b 100644
--- a/tests/cli_wrapper.rs
+++ b/tests/cli_wrapper.rs
@@ -776,7 +776,7 @@ fn pane_shell_gets_herdr_socket_and_pane_env() {
}
#[test]
-fn wait_agent_state_exits_when_state_matches() {
+fn wait_agent_status_exits_when_idle_status_matches() {
let base = unique_test_dir();
let config_home = base.join("config");
let runtime_dir = base.join("runtime");
@@ -851,9 +851,9 @@ fn wait_agent_state_exits_when_state_matches() {
&socket_path,
&[
"wait",
- "agent-state",
+ "agent-status",
"1-1",
- "--state",
+ "--status",
"idle",
"--timeout",
"5000",
@@ -865,8 +865,117 @@ fn wait_agent_state_exits_when_state_matches() {
String::from_utf8_lossy(&waited.stderr)
);
let waited_json: serde_json::Value = serde_json::from_slice(&waited.stdout).unwrap();
- assert_eq!(waited_json["event"], "pane.agent_state_changed");
- assert_eq!(waited_json["data"]["state"], "idle");
+ assert_eq!(waited_json["event"], "pane.agent_status_changed");
+ assert_eq!(waited_json["data"]["agent_status"], "idle");
+ assert_eq!(waited_json["data"]["agent"], "pi");
+
+ cleanup_spawned_herdr(herdr, base);
+}
+
+#[test]
+fn wait_agent_status_exits_when_done_status_matches() {
+ let base = unique_test_dir();
+ let config_home = base.join("config");
+ let runtime_dir = base.join("runtime");
+ let socket_path = runtime_dir.join("herdr.sock");
+ let bin_dir = base.join("bin");
+
+ fs::create_dir_all(&bin_dir).unwrap();
+ let fake_pi = bin_dir.join("pi");
+ fs::write(
+ &fake_pi,
+ "#!/bin/sh\nprintf 'Working...\\n'\nsleep 1\nprintf '\\033[2J\\033[Hdone\\n'\n",
+ )
+ .unwrap();
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::PermissionsExt;
+ let mut perms = fs::metadata(&fake_pi).unwrap().permissions();
+ perms.set_mode(0o755);
+ fs::set_permissions(&fake_pi, perms).unwrap();
+ }
+
+ let pair = native_pty_system()
+ .openpty(PtySize {
+ rows: 24,
+ cols: 80,
+ pixel_width: 0,
+ pixel_height: 0,
+ })
+ .unwrap();
+ fs::create_dir_all(config_home.join("herdr")).unwrap();
+ fs::create_dir_all(&runtime_dir).unwrap();
+ fs::write(
+ config_home.join("herdr/config.toml"),
+ "onboarding = false\n",
+ )
+ .unwrap();
+ let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
+ cmd.arg("--no-session");
+ cmd.env("XDG_CONFIG_HOME", &config_home);
+ cmd.env("XDG_RUNTIME_DIR", &runtime_dir);
+ cmd.env("HERDR_SOCKET_PATH", &socket_path);
+ cmd.env_remove("HERDR_ENV");
+ cmd.env(
+ "PATH",
+ format!(
+ "{}:{}",
+ bin_dir.display(),
+ std::env::var("PATH").unwrap_or_default()
+ ),
+ );
+ let child = pair.slave.spawn_command(cmd).unwrap();
+ let herdr = SpawnedHerdr {
+ _master: pair.master,
+ child,
+ };
+
+ wait_for_socket(&socket_path, Duration::from_secs(5));
+
+ let created = send_request(
+ &socket_path,
+ &format!(
+ r#"{{"id":"req_cli_status_1","method":"workspace.create","params":{{"cwd":"{}","focus":true}}}}"#,
+ base.display()
+ ),
+ );
+ let workspace_id = created["result"]["workspace"]["workspace_id"]
+ .as_str()
+ .unwrap()
+ .to_string();
+
+ let tab_created = send_request(
+ &socket_path,
+ &format!(
+ r#"{{"id":"req_cli_status_2","method":"tab.create","params":{{"workspace_id":"{}","focus":true}}}}"#,
+ workspace_id
+ ),
+ );
+ assert_eq!(tab_created["result"]["type"], "tab_info");
+
+ let start_pi = run_cli(&socket_path, &["pane", "run", "1-1", "pi"]);
+ assert!(start_pi.status.success());
+
+ let waited = run_cli(
+ &socket_path,
+ &[
+ "wait",
+ "agent-status",
+ "1-1",
+ "--status",
+ "done",
+ "--timeout",
+ "5000",
+ ],
+ );
+ assert!(
+ waited.status.success(),
+ "stderr: {}",
+ String::from_utf8_lossy(&waited.stderr)
+ );
+ let waited_json: serde_json::Value = serde_json::from_slice(&waited.stdout).unwrap();
+ assert_eq!(waited_json["event"], "pane.agent_status_changed");
+ assert_eq!(waited_json["data"]["agent_status"], "done");
assert_eq!(waited_json["data"]["agent"], "pi");
cleanup_spawned_herdr(herdr, base);