diff options
Diffstat (limited to 'src/integration/tests.rs')
| -rw-r--r-- | src/integration/tests.rs | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/src/integration/tests.rs b/src/integration/tests.rs index ff616264..7abf7be2 100644 --- a/src/integration/tests.rs +++ b/src/integration/tests.rs @@ -102,6 +102,7 @@ fn clear_integration_path_env() { std::env::remove_var("XDG_CONFIG_HOME"); std::env::remove_var(QODERCLI_CONFIG_DIR_ENV_VAR); std::env::remove_var(CURSOR_CONFIG_DIR_ENV_VAR); + std::env::remove_var(ANTIGRAVITY_CLI_CONFIG_DIR_ENV_VAR); std::env::remove_var(GROK_CONFIG_DIR_ENV_VAR); std::env::remove_var(GROK_HOME_ENV_VAR); } @@ -3645,6 +3646,154 @@ fn uninstall_mastracode_errors_when_event_value_not_array() { } #[test] +fn install_antigravity_cli_writes_hook_and_updates_hooks_json() { + let _lock = integration_env_lock(); + let base = unique_base(); + let agy_dir = base.join(".gemini").join("config"); + fs::create_dir_all(&agy_dir).unwrap(); + fs::write( + agy_dir.join("hooks.json"), + r#"{"lint-checker":{"PreInvocation":[{"type":"command","command":"echo keep-me"}]}}"#, + ) + .unwrap(); + std::env::set_var(ANTIGRAVITY_CLI_CONFIG_DIR_ENV_VAR, &agy_dir); + + let installed = install_antigravity_cli().unwrap(); + + assert_eq!( + installed.hook_path, + agy_dir + .join("hooks") + .join(ANTIGRAVITY_CLI_HOOK_INSTALL_NAME) + ); + assert_eq!(installed.hooks_path, agy_dir.join("hooks.json")); + assert_eq!( + fs::read_to_string(&installed.hook_path).unwrap(), + ANTIGRAVITY_CLI_HOOK_ASSET + ); + + let hooks_file: Value = + serde_json::from_str(&fs::read_to_string(agy_dir.join("hooks.json")).unwrap()).unwrap(); + let hooks = hooks_file.as_object().unwrap(); + + // Herdr entries live under a named hook block; Antigravity CLI rejects a + // file whose top level maps event names straight to arrays. + let block = hooks + .get(ANTIGRAVITY_CLI_HOOK_BLOCK_NAME) + .and_then(Value::as_object) + .unwrap(); + + for (event, action) in ANTIGRAVITY_CLI_HOOK_EVENTS { + let entries = block.get(event).and_then(Value::as_array).unwrap(); + assert_eq!(entries.len(), 1, "{event} should hold one Herdr entry"); + let handler = &entries[0]; + + // Handlers must be a flat list; the matcher/hooks wrapper is only + // valid for tool events and invalidates the whole file here. + assert!( + handler.get("matcher").is_none() && handler.get("hooks").is_none(), + "{event} must be a flat handler, got {handler}" + ); + + assert_eq!(handler.get("type").and_then(Value::as_str), Some("command")); + assert_eq!( + handler.get("timeout").and_then(Value::as_u64), + Some(ANTIGRAVITY_CLI_HOOK_TIMEOUT_SEC) + ); + let command = handler.get("command").and_then(Value::as_str).unwrap(); + assert!(command.contains("herdr-agent-state")); + assert!(command.ends_with(action)); + } + + // The integration is session-only. Antigravity CLI cannot express blocked + // state, skips PostInvocation on interruption, and fires Stop at end of + // turn rather than process exit, so Herdr never claims lifecycle authority + // here and screen detection owns agent state. + for event in ["PreToolUse", "PostToolUse", "PostInvocation", "Stop"] { + assert!( + block.get(event).is_none(), + "{event} must not be registered; lifecycle stays with screen detection" + ); + } + + // Other named hooks are left untouched. + assert_eq!( + hooks + .get("lint-checker") + .and_then(|block| block.get("PreInvocation")) + .and_then(Value::as_array) + .and_then(|entries| entries.first()) + .and_then(|entry| entry.get("command")) + .and_then(Value::as_str), + Some("echo keep-me") + ); + + std::env::remove_var(ANTIGRAVITY_CLI_CONFIG_DIR_ENV_VAR); + let _ = fs::remove_dir_all(base); +} + +#[test] +fn install_antigravity_cli_rewrites_stale_herdr_block() { + let _lock = integration_env_lock(); + let base = unique_base(); + let agy_dir = base.join(".gemini").join("config"); + fs::create_dir_all(&agy_dir).unwrap(); + // An older Herdr install claimed lifecycle authority, wrapped events in + // matcher/hooks, and left entries Antigravity CLI now rejects. + fs::write( + agy_dir.join("hooks.json"), + r#"{"herdr":{"Stop":[{"matcher":"*","hooks":[{"type":"command","command":"stale"}]}],"PostInvocation":[{"type":"command","command":"stale idle"}],"Legacy":[]}}"#, + ) + .unwrap(); + std::env::set_var(ANTIGRAVITY_CLI_CONFIG_DIR_ENV_VAR, &agy_dir); + + install_antigravity_cli().unwrap(); + + let hooks_file: Value = + serde_json::from_str(&fs::read_to_string(agy_dir.join("hooks.json")).unwrap()).unwrap(); + let block = hooks_file + .get(ANTIGRAVITY_CLI_HOOK_BLOCK_NAME) + .and_then(Value::as_object) + .unwrap(); + + // The block is Herdr-owned and rewritten wholesale, so a stale lifecycle + // install is migrated to session-only rather than merged with. + assert_eq!( + block.keys().map(String::as_str).collect::<Vec<_>>(), + vec!["PreInvocation"], + "stale lifecycle events should be gone" + ); + let entries = block + .get("PreInvocation") + .and_then(Value::as_array) + .unwrap(); + assert_eq!(entries.len(), 1); + assert!(entries[0].get("hooks").is_none()); + assert!(entries[0] + .get("command") + .and_then(Value::as_str) + .is_some_and(|command| command.contains("herdr-agent-state"))); + + std::env::remove_var(ANTIGRAVITY_CLI_CONFIG_DIR_ENV_VAR); + let _ = fs::remove_dir_all(base); +} + +#[test] +fn install_antigravity_cli_errors_when_config_dir_missing() { + let _lock = integration_env_lock(); + let base = unique_base(); + let agy_dir = base.join(".gemini").join("config"); + std::env::set_var(ANTIGRAVITY_CLI_CONFIG_DIR_ENV_VAR, &agy_dir); + + let err = install_antigravity_cli().unwrap_err(); + assert!(err.to_string().contains("install antigravity cli first")); + assert!(!agy_dir.exists(), "install must not create the config dir"); + + std::env::remove_var(ANTIGRAVITY_CLI_CONFIG_DIR_ENV_VAR); + let _ = fs::remove_dir_all(base); +} + +#[test] fn grok_v1_integration_status_is_current() { let _lock = integration_env_lock(); let base = unique_base(); @@ -3756,6 +3905,41 @@ fn grok_status_reports_outdated_when_hook_config_missing_or_broken() { } #[test] +fn uninstall_antigravity_cli_removes_hooks_json_entries_and_hook_file() { + let _lock = integration_env_lock(); + let base = unique_base(); + let agy_dir = base.join(".gemini").join("config"); + fs::create_dir_all(&agy_dir).unwrap(); + fs::write( + agy_dir.join("hooks.json"), + r#"{"lint-checker":{"PreInvocation":[{"type":"command","command":"echo keep-me"}]}}"#, + ) + .unwrap(); + std::env::set_var(ANTIGRAVITY_CLI_CONFIG_DIR_ENV_VAR, &agy_dir); + + // Install first + let installed = install_antigravity_cli().unwrap(); + assert!(installed.hook_path.is_file()); + + // Uninstall + let result = uninstall_antigravity_cli().unwrap(); + assert!(result.removed_hook_file); + assert!(!installed.hook_path.is_file()); + assert!(result.updated_hooks); + + let hooks_file: Value = + serde_json::from_str(&fs::read_to_string(agy_dir.join("hooks.json")).unwrap()).unwrap(); + let hooks = hooks_file.as_object().unwrap(); + + // The Herdr block is gone and unrelated named hooks survive. + assert!(hooks.get(ANTIGRAVITY_CLI_HOOK_BLOCK_NAME).is_none()); + assert!(hooks.contains_key("lint-checker")); + + std::env::remove_var(ANTIGRAVITY_CLI_CONFIG_DIR_ENV_VAR); + let _ = fs::remove_dir_all(base); +} + +#[test] fn grok_dir_honors_grok_home_after_config_dir_seam() { let _lock = integration_env_lock(); let base = unique_base(); |
