diff --git a/CHANGELOG.md b/CHANGELOG.md index 70d90e1..88ed2ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [1.8.3] - 2026-09-06 + +### Fixed +- Fixed two test-suite race conditions that made CI intermittently red. The `CSHIP_ACCOUNT` error-hint tests mutated the same process-global env var from parallel worker threads and were merged into one sequential test; `test_render_passthrough_returns_none_for_nonexistent_module` resolved `starship` from `PATH` without holding `PATH_MUTEX`, so sibling tests pointing `PATH` at mock `starship` scripts could make it observe real output ([#206](https://github.com/stephenleo/cship/pull/206)) + ## [1.8.2] - 2026-09-06 ### Added diff --git a/Cargo.lock b/Cargo.lock index 77b02b2..74155c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,7 +257,7 @@ dependencies = [ [[package]] name = "cship" -version = "1.8.2" +version = "1.8.3" dependencies = [ "anyhow", "assert_cmd", diff --git a/Cargo.toml b/Cargo.toml index d16f983..bd7ca5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cship" -version = "1.8.2" +version = "1.8.3" edition = "2024" description = "A beautiful, fully customizable statusline for Claude Code — Starship-style TOML config, themeable colours, Nerd Font glyphs, and tunable cost/context/usage thresholds" license = "Apache-2.0" diff --git a/src/explain.rs b/src/explain.rs index 41f7647..dbe5e17 100644 --- a/src/explain.rs +++ b/src/explain.rs @@ -784,22 +784,21 @@ mod tests { let _ = asserted_at_least_once; } + /// Both `CSHIP_ACCOUNT` cases live in one test: `set_var` is process-global, + /// so two tests mutating it run concurrently on cargo's worker threads and + /// clobber each other (this raced on Windows CI). One test = sequential. + /// ponytail: unsafe because `std::env::set_var` is process-global; scoped + /// tightly around the two `error_hint_for` calls that need it. #[test] - fn test_error_hint_account_malformed_env_names_env_as_cause() { - // No other test in this file touches CSHIP_ACCOUNT, so set/unset here - // doesn't race with a sibling test's own use of the var. - // ponytail: unsafe because std::env::set_var is process-global; scoped - // tightly around the single assertion this test needs. - unsafe { - std::env::set_var("CSHIP_ACCOUNT", "{not json"); - } + fn test_error_hint_account_env_var_cases() { let ctx = crate::context::Context::default(); let cfg = crate::config::CshipConfig::default(); - let (error, remediation) = error_hint_for("account", &ctx, &cfg); + + // Set-but-unparseable CSHIP_ACCOUNT is named as the root cause. unsafe { - std::env::remove_var("CSHIP_ACCOUNT"); + std::env::set_var("CSHIP_ACCOUNT", "{not json"); } - + let (error, remediation) = error_hint_for("account", &ctx, &cfg); assert!( error.contains("CSHIP_ACCOUNT"), "expected CSHIP_ACCOUNT named as the cause, got: {error}" @@ -808,22 +807,16 @@ mod tests { remediation.contains("CSHIP_ACCOUNT"), "expected remediation to mention CSHIP_ACCOUNT, got: {remediation}" ); - } - #[test] - fn test_error_hint_account_empty_env_falls_back_to_credential_probe() { // Empty CSHIP_ACCOUNT must not trigger the malformed-env hint; it // falls through to the normal credential probe. unsafe { std::env::set_var("CSHIP_ACCOUNT", " "); } - let ctx = crate::context::Context::default(); - let cfg = crate::config::CshipConfig::default(); let (error, _) = error_hint_for("account", &ctx, &cfg); unsafe { std::env::remove_var("CSHIP_ACCOUNT"); } - assert!( !error.contains("CSHIP_ACCOUNT"), "empty CSHIP_ACCOUNT should not be reported as malformed, got: {error}" diff --git a/src/passthrough.rs b/src/passthrough.rs index 8e2b58a..4f644ce 100644 --- a/src/passthrough.rs +++ b/src/passthrough.rs @@ -381,7 +381,10 @@ mod tests { #[test] fn test_render_passthrough_returns_none_for_nonexistent_module() { - // starship exits non-zero for unknown module names → None + // starship exits non-zero for unknown module names → None. + // Resolves `starship` from PATH, so it must hold the mutex: sibling tests + // point PATH at mock starship scripts that print output. + let _guard = PATH_MUTEX.lock().unwrap(); let result = render_passthrough("__cship_nonexistent_xyz__", &Context::default()); assert!(result.is_none()); }