gitbaby is an asynchronous Rust library wrapping git operations, built on gix + tokio + async-trait. Module layout: - archive, blame, blob, branch, cleanup, commit, compare, config, conflict, diff, merge, refs, remote, setup, share, submodule, tags, tree (feature modules) - command: command scheduler (cmd/env/pipe/context/error) - repo: RepositoryFacade trait (consumers implement, exposing a gix::Repository) Features: - async API on tokio 1.53 - order-preserving Env (Vec<(String, String)>) - hand-written BabyError / CmdError, not using thiserror derive - integration tests covering cmd_run / context / env / pipe (env keys prefixed for isolation) CI: none, standard cargo build / test / clippy / fmt only
138 lines
3.5 KiB
Rust
138 lines
3.5 KiB
Rust
use gitbaby::command::env::Env;
|
|
use std::sync::{Mutex, MutexGuard, PoisonError};
|
|
|
|
static ENV_LOCK: Mutex<()> = Mutex::new(());
|
|
|
|
fn lock_or_recover() -> MutexGuard<'static, ()> {
|
|
match ENV_LOCK.lock() {
|
|
Ok(g) => g,
|
|
Err(PoisonError { .. }) => panic!("env lock poisoned"),
|
|
}
|
|
}
|
|
|
|
fn unique_key(suffix: &str) -> String {
|
|
format!("GITBABY_TEST_ENV_{suffix}")
|
|
}
|
|
|
|
unsafe fn set_var(key: &str, val: &str) {
|
|
unsafe { std::env::set_var(key, val) }
|
|
}
|
|
|
|
unsafe fn remove_var(key: &str) {
|
|
unsafe { std::env::remove_var(key) }
|
|
}
|
|
|
|
#[test]
|
|
fn from_current_env_includes_system_var() {
|
|
let _guard = lock_or_recover();
|
|
let key = unique_key("FROM_CUR");
|
|
unsafe { set_var(&key, "from-system") };
|
|
|
|
let env = Env::from_current_env();
|
|
assert_eq!(env.get(&key), Some("from-system"));
|
|
|
|
unsafe { remove_var(&key) };
|
|
}
|
|
|
|
#[test]
|
|
fn from_current_env_contains_path_like_var() {
|
|
let env = Env::from_current_env();
|
|
assert!(env.get("PATH").is_some() || env.get("Path").is_some() || !env.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn set_overrides_system_var_after_from_current_env() {
|
|
let _guard = lock_or_recover();
|
|
let key = unique_key("OVERRIDE");
|
|
unsafe { set_var(&key, "system-value") };
|
|
|
|
let mut env = Env::from_current_env();
|
|
env.set(&key, "user-value");
|
|
assert_eq!(env.get(&key), Some("user-value"));
|
|
|
|
unsafe { remove_var(&key) };
|
|
}
|
|
|
|
#[test]
|
|
fn with_current_env_overrides_user_added_var() {
|
|
let _guard = lock_or_recover();
|
|
let key = unique_key("WC_OVERRIDE");
|
|
unsafe { set_var(&key, "system-wins") };
|
|
|
|
let env = Env::new().with(&key, "user-loses").with_current_env();
|
|
assert_eq!(env.get(&key), Some("system-wins"));
|
|
|
|
unsafe { remove_var(&key) };
|
|
}
|
|
|
|
#[test]
|
|
fn overlay_current_env_preserves_user_added_var() {
|
|
let _guard = lock_or_recover();
|
|
let key = unique_key("OVERLAY");
|
|
unsafe { set_var(&key, "system-value") };
|
|
|
|
let env = Env::new().with(&key, "user-keeps").overlay_current_env();
|
|
assert_eq!(env.get(&key), Some("user-keeps"));
|
|
|
|
unsafe { remove_var(&key) };
|
|
}
|
|
|
|
#[test]
|
|
fn overlay_current_env_fills_missing_keys() {
|
|
let _guard = lock_or_recover();
|
|
let mut iter = std::env::vars_os();
|
|
let known = match iter.next() {
|
|
Some(v) => v,
|
|
None => panic!("process should have at least one env var"),
|
|
};
|
|
let known_key = known.0.to_string_lossy().into_owned();
|
|
|
|
let env = Env::new().overlay_current_env();
|
|
assert!(env.get(&known_key).is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn unset_removes_system_var_when_loaded() {
|
|
let _guard = lock_or_recover();
|
|
let key = unique_key("UNSET");
|
|
unsafe { set_var(&key, "temp") };
|
|
|
|
let mut env = Env::from_current_env();
|
|
assert_eq!(env.get(&key), Some("temp"));
|
|
|
|
env.unset(&key);
|
|
assert_eq!(env.get(&key), None);
|
|
|
|
unsafe { remove_var(&key) };
|
|
}
|
|
|
|
#[test]
|
|
fn from_current_env_is_cloneable() {
|
|
let _guard = lock_or_recover();
|
|
let key = unique_key("CLONE");
|
|
unsafe { set_var(&key, "v") };
|
|
|
|
let env = Env::from_current_env();
|
|
let copy = env.clone();
|
|
assert_eq!(env.get(&key), Some("v"));
|
|
assert_eq!(copy.get(&key), Some("v"));
|
|
|
|
unsafe { remove_var(&key) };
|
|
}
|
|
|
|
#[test]
|
|
fn set_after_from_current_env_does_not_grow_pairs_when_key_known() {
|
|
let _guard = lock_or_recover();
|
|
let key = unique_key("NOGROW");
|
|
unsafe { set_var(&key, "system") };
|
|
|
|
let mut env = Env::from_current_env();
|
|
let before = env.len();
|
|
env.set(&key, "user");
|
|
let after = env.len();
|
|
assert_eq!(before, after, "set 应替换而非新增");
|
|
assert_eq!(env.get(&key), Some("user"));
|
|
|
|
unsafe { remove_var(&key) };
|
|
}
|