diff options
| author | Lane <150401101+LaneBirmingham@users.noreply.github.com> | 2026-06-02 08:32:05 +1000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-06-02 01:32:05 +0300 |
| commit | f7d315d494b6a3d583574012526d15c1ddc82bfe (patch) | |
| tree | 327585c6cb27d478bbb1e4b0da782d9bbfd00901 /tests/cli_wrapper.rs | |
| parent | 99e097c7d3cf76ae2d53bac5e7e2b3fd88b83591 (diff) | |
feat: add github copilot cli integration (#386)
* feat: add github copilot cli integration
refs #232
* docs: restore 0.6.5 changelog and document copilot hook flow
* test: cover copilot camelcase hook payloads
---------
Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>
Diffstat (limited to 'tests/cli_wrapper.rs')
| -rw-r--r-- | tests/cli_wrapper.rs | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/tests/cli_wrapper.rs b/tests/cli_wrapper.rs index 0c81e7e4..511674ab 100644 --- a/tests/cli_wrapper.rs +++ b/tests/cli_wrapper.rs @@ -500,6 +500,14 @@ fn run_codex_hook(action: &str, hook_input: &str) -> Option<serde_json::Value> { ) } +fn run_copilot_hook(hook_input: &str) -> Option<serde_json::Value> { + run_shell_hook( + "src/integration/assets/copilot/herdr-agent-state.sh", + &[], + hook_input, + ) +} + fn run_shell_hook(asset_path: &str, args: &[&str], hook_input: &str) -> Option<serde_json::Value> { let base = unique_test_dir(); fs::create_dir_all(&base).unwrap(); @@ -623,6 +631,169 @@ fn codex_hook_reports_session_id_from_stdin() { } #[test] +fn copilot_hook_maps_turn_lifecycle() { + let session_start_idle = run_copilot_hook( + r#"{"hook_event_name":"SessionStart","session_id":"copilot-session","source":"resume"}"#, + ) + .expect("copilot session start without prompt should report idle"); + assert_eq!(session_start_idle["method"], "pane.report_agent"); + assert_eq!(session_start_idle["params"]["state"], "idle"); + assert_eq!( + session_start_idle["params"]["agent_session_id"], + "copilot-session" + ); + + let session_start_working = run_copilot_hook( + r#"{"hook_event_name":"SessionStart","sessionId":"copilot-session","initialPrompt":"run tests"}"#, + ) + .expect("copilot session start with prompt should report working"); + assert_eq!(session_start_working["method"], "pane.report_agent"); + assert_eq!(session_start_working["params"]["state"], "working"); + assert_eq!( + session_start_working["params"]["agent_session_id"], + "copilot-session" + ); + + let working = run_copilot_hook( + r#"{"hook_event_name":"UserPromptSubmit","session_id":"copilot-session","prompt":"run tests"}"#, + ) + .expect("copilot prompt should report working"); + assert_eq!(working["method"], "pane.report_agent"); + assert_eq!(working["params"]["agent"], "copilot"); + assert_eq!(working["params"]["state"], "working"); + assert_eq!(working["params"]["agent_session_id"], "copilot-session"); + + let idle = run_copilot_hook( + r#"{"hook_event_name":"agentStop","session_id":"copilot-session","stop_reason":"end_turn"}"#, + ) + .expect("copilot stop should report idle"); + assert_eq!(idle["method"], "pane.report_agent"); + assert_eq!(idle["params"]["state"], "idle"); +} + +#[test] +fn copilot_hook_maps_camel_case_payloads() { + let session_start = run_copilot_hook( + r#"{"sessionId":"copilot-camel-session","source":"new","initialPrompt":"run tests"}"#, + ) + .expect("copilot camelCase session start should report working"); + assert_eq!(session_start["method"], "pane.report_agent"); + assert_eq!(session_start["params"]["state"], "working"); + assert_eq!( + session_start["params"]["agent_session_id"], + "copilot-camel-session" + ); + + let prompt = + run_copilot_hook(r#"{"sessionId":"copilot-camel-session","prompt":"continue the task"}"#) + .expect("copilot camelCase prompt should report working"); + assert_eq!(prompt["method"], "pane.report_agent"); + assert_eq!(prompt["params"]["state"], "working"); + + let blocker = run_copilot_hook( + r#"{"sessionId":"copilot-camel-session","toolName":"ask_user","toolArgs":{}}"#, + ) + .expect("copilot camelCase ask_user should report blocked"); + assert_eq!(blocker["method"], "pane.report_agent"); + assert_eq!(blocker["params"]["state"], "blocked"); + + let answered = run_copilot_hook( + r#"{"sessionId":"copilot-camel-session","toolName":"ask_user","toolArgs":{},"toolResult":{"resultType":"success","textResultForLlm":"ok"}}"#, + ) + .expect("copilot camelCase postToolUse should report working"); + assert_eq!(answered["method"], "pane.report_agent"); + assert_eq!(answered["params"]["state"], "working"); + + let idle = run_copilot_hook( + r#"{"sessionId":"copilot-camel-session","stopReason":"end_turn","transcriptPath":"/tmp/transcript.jsonl"}"#, + ) + .expect("copilot camelCase agentStop should report idle"); + assert_eq!(idle["method"], "pane.report_agent"); + assert_eq!(idle["params"]["state"], "idle"); + + let user_exit = + run_copilot_hook(r#"{"sessionId":"copilot-camel-session","reason":"user_exit"}"#) + .expect("copilot camelCase user exit should release"); + assert_eq!(user_exit["method"], "pane.release_agent"); + assert_eq!(user_exit["params"]["agent"], "copilot"); +} + +#[test] +fn copilot_hook_maps_user_prompts_and_notifications() { + let ask_user = run_copilot_hook( + r#"{"hook_event_name":"PreToolUse","session_id":"copilot-session","tool_name":"ask_user"}"#, + ) + .expect("copilot ask_user should report blocked"); + assert_eq!(ask_user["method"], "pane.report_agent"); + assert_eq!(ask_user["params"]["state"], "blocked"); + + let stale_report_intent = run_copilot_hook( + r#"{"hook_event_name":"PostToolUse","session_id":"copilot-session","tool_name":"report_intent","tool_result":{"text_result_for_llm":"ok"}}"#, + ); + assert!( + stale_report_intent.is_none(), + "postToolUse report_intent must not clear a pending ask_user prompt" + ); + + let ask_user_answered = run_copilot_hook( + r#"{"hook_event_name":"PostToolUse","session_id":"copilot-session","tool_name":"ask_user","tool_result":{"text_result_for_llm":"Blue"}}"#, + ) + .expect("copilot answered ask_user should report working"); + assert_eq!(ask_user_answered["method"], "pane.report_agent"); + assert_eq!(ask_user_answered["params"]["state"], "working"); + + let exit_plan_mode = run_copilot_hook( + r#"{"hook_event_name":"PreToolUse","session_id":"copilot-session","tool_name":"exit_plan_mode"}"#, + ) + .expect("copilot exit_plan_mode should report blocked"); + assert_eq!(exit_plan_mode["method"], "pane.report_agent"); + assert_eq!(exit_plan_mode["params"]["state"], "blocked"); + + let exit_plan_answered = run_copilot_hook( + r#"{"hook_event_name":"PostToolUse","session_id":"copilot-session","tool_name":"exit_plan_mode","tool_result":{"text_result_for_llm":"Approved"}}"#, + ) + .expect("copilot answered exit_plan_mode should report working"); + assert_eq!(exit_plan_answered["method"], "pane.report_agent"); + assert_eq!(exit_plan_answered["params"]["state"], "working"); + + let notification = run_copilot_hook( + r#"{"hook_event_name":"notification","session_id":"copilot-session","notification_type":"permission_prompt"}"#, + ) + .expect("copilot permission notification should report blocked"); + assert_eq!(notification["method"], "pane.report_agent"); + assert_eq!(notification["params"]["state"], "blocked"); + + let permission_approved_tool = run_copilot_hook( + r#"{"hook_event_name":"PostToolUse","session_id":"copilot-session","tool_name":"bash","tool_result":{"text_result_for_llm":"ok"}}"#, + ) + .expect("copilot completed tool should report working after permission prompt"); + assert_eq!(permission_approved_tool["method"], "pane.report_agent"); + assert_eq!(permission_approved_tool["params"]["state"], "working"); + + let agent_idle = run_copilot_hook( + r#"{"hook_event_name":"notification","session_id":"copilot-session","notification_type":"agent_idle"}"#, + ) + .expect("copilot agent_idle notification should report idle"); + assert_eq!(agent_idle["method"], "pane.report_agent"); + assert_eq!(agent_idle["params"]["state"], "idle"); +} + +#[test] +fn copilot_hook_releases_on_user_exit_only() { + let complete = run_copilot_hook( + r#"{"hook_event_name":"SessionEnd","session_id":"copilot-session","reason":"complete"}"#, + ); + assert!(complete.is_none()); + + let user_exit = run_copilot_hook( + r#"{"hook_event_name":"SessionEnd","session_id":"copilot-session","reason":"user_exit"}"#, + ) + .expect("copilot user exit should release"); + assert_eq!(user_exit["method"], "pane.release_agent"); + assert_eq!(user_exit["params"]["agent"], "copilot"); +} + +#[test] fn pane_run_sends_one_send_input_request_with_enter_key() { let base = unique_test_dir(); fs::create_dir_all(&base).unwrap(); |
