diff options
| author | Mark Jaquith <mark.jaquith@gusto.com> | 2026-09-11 16:36:18 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-11 22:36:18 +0200 |
| commit | fb5525fc2234a246dfdfc9e0d7ed73c824cbd8a6 (patch) | |
| tree | f989f8175e8afdd89824fd94477b1b1645af05ff /src/integration/tests.rs | |
| parent | 38d002de59331b27aee2f5838639ea27a04878f8 (diff) | |
fix: opencode v2 lifecycle reporting (#3757)
* fix: support opencode v2 lifecycle reporting
* fix: repair Japanese docs
* fix: ignore payload-less OpenCode events
* fix: harden opencode v2 integration install and reporting
Follow-ups on top of the V2 lifecycle support:
- create `cli.json` when OpenCode has no V1 TUI preferences (`tui.json` or
`kv.json`) to migrate, instead of only registering into an existing file
- settle stalled socket attempts with a plain connect timer, and resend the
latest lifecycle state after a failed delivery so the pane cannot get stuck
- resolve the OpenCode state directory from `XDG_STATE_HOME`
- remove the managed `herdr-opencode` directory on uninstall
- document failed executions reporting `blocked`, keep the integration test
environment independent of an inherited `XDG_STATE_HOME`, and simplify
`reconcileBlockers` to reassign its map
---------
Co-authored-by: Jonathan Liebig <jonathan.liebig@gmail.com>
Diffstat (limited to 'src/integration/tests.rs')
| -rw-r--r-- | src/integration/tests.rs | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/src/integration/tests.rs b/src/integration/tests.rs index b2210074..dc597d1d 100644 --- a/src/integration/tests.rs +++ b/src/integration/tests.rs @@ -134,6 +134,7 @@ fn clear_integration_path_env() { std::env::remove_var(COPILOT_HOME_ENV_VAR); std::env::remove_var(KIMI_CODE_HOME_ENV_VAR); std::env::remove_var("XDG_CONFIG_HOME"); + std::env::remove_var("XDG_STATE_HOME"); #[cfg(windows)] std::env::remove_var("APPDATA"); std::env::remove_var(QODERCLI_CONFIG_DIR_ENV_VAR); @@ -2301,12 +2302,106 @@ fn install_opencode_writes_server_and_tui_plugins() { let tui_config: Value = serde_json::from_str(&fs::read_to_string(&installed.tui_config_path).unwrap()).unwrap(); assert_eq!(tui_config["plugin"], json!([OPENCODE_TUI_PLUGIN_SPEC])); + let cli_config_path = installed + .cli_config_path + .expect("cli.json should be created when OpenCode has nothing to migrate"); + assert_eq!(cli_config_path, opencode_dir.join("cli.json")); + let cli_config: Value = + serde_json::from_str(&fs::read_to_string(&cli_config_path).unwrap()).unwrap(); + assert_eq!(cli_config["plugins"], json!([OPENCODE_V2_TUI_PLUGIN_SPEC])); std::env::remove_var("HOME"); let _ = fs::remove_dir_all(base); } #[test] +fn opencode_install_defers_v2_registration_while_migration_pending() { + let _lock = integration_env_lock(); + let base = unique_base(); + let home = base.join("home"); + let opencode_dir = home.join(".config/opencode"); + fs::create_dir_all(&opencode_dir).unwrap(); + fs::write(opencode_dir.join("tui.json"), "{}").unwrap(); + std::env::set_var("HOME", &home); + + let installed = install_opencode().unwrap(); + + assert!(installed.cli_config_path.is_none()); + assert!(!opencode_dir.join("cli.json").exists()); + assert!(opencode_dir + .join(OPENCODE_V2_TUI_PLUGIN_DIR) + .join("tui.js") + .is_file()); + + std::env::remove_var("HOME"); + let _ = fs::remove_dir_all(base); +} + +#[test] +fn opencode_v2_install_status_and_uninstall_preserve_cli_preferences() { + let _lock = integration_env_lock(); + let base = unique_base(); + let home = base.join("home"); + let dir = home.join(".config/opencode"); + fs::create_dir_all(&dir).unwrap(); + std::env::set_var("HOME", &home); + let cli = dir.join("cli.json"); + fs::write( + &cli, + r#"{"theme":{"name":"catppuccin"},"plugins":["other"]}"#, + ) + .unwrap(); + let installed = install_opencode().unwrap(); + assert_eq!(installed.cli_config_path, Some(cli.clone())); + let status = || { + integration_status_at( + crate::api::schema::IntegrationTarget::Opencode, + installed.plugin_path.clone(), + OPENCODE_INTEGRATION_VERSION, + ) + .state + }; + assert_eq!(status(), IntegrationStatusKind::Current); + let entry = dir.join(OPENCODE_V2_TUI_PLUGIN_DIR).join("tui.js"); + assert_eq!( + fs::read_to_string(&entry).unwrap(), + OPENCODE_V2_TUI_PLUGIN_ASSET + ); + fs::remove_file(&entry).unwrap(); + assert_eq!(status(), IntegrationStatusKind::Outdated); + install_opencode().unwrap(); + super::opencode_config::remove_cli_plugin(&dir, OPENCODE_V2_TUI_PLUGIN_SPEC).unwrap(); + assert_eq!(status(), IntegrationStatusKind::Outdated); + install_opencode().unwrap(); + uninstall_opencode().unwrap(); + assert!(!entry.exists()); + assert_eq!( + serde_json::from_str::<Value>(&fs::read_to_string(cli).unwrap()).unwrap(), + json!({"theme":{"name":"catppuccin"},"plugins":["other"]}) + ); + std::env::remove_var("HOME"); + let _ = fs::remove_dir_all(base); +} + +#[test] +fn opencode_invalid_cli_config_does_not_overwrite_existing_plugins() { + let _lock = integration_env_lock(); + let base = unique_base(); + let home = base.join("home"); + let dir = home.join(".config/opencode"); + fs::create_dir_all(dir.join("plugins")).unwrap(); + std::env::set_var("HOME", &home); + let plugin = dir.join("plugins").join(OPENCODE_PLUGIN_INSTALL_NAME); + fs::write(&plugin, "previous integration").unwrap(); + fs::write(dir.join("cli.json"), r#"{"plugins":{}}"#).unwrap(); + assert!(install_opencode().is_err()); + assert_eq!(fs::read_to_string(plugin).unwrap(), "previous integration"); + assert!(!dir.join("tui.jsonc").exists()); + std::env::remove_var("HOME"); + let _ = fs::remove_dir_all(base); +} + +#[test] fn opencode_status_requires_the_tui_plugin_and_config_entry() { let _lock = integration_env_lock(); let base = unique_base(); |
