use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex, MutexGuard, OnceLock, RwLock};
use cc_switch_lib::{
update_settings, AppSettings, AppState, Database, MultiAppConfig, ProviderService, ProxyService,
};
pub fn ensure_test_home() -> &'static Path {
static HOME: OnceLock<PathBuf> = OnceLock::new();
let home = HOME.get_or_init(|| {
let base = std::env::temp_dir().join(format!("cc-switch-test-home-{}", std::process::id()));
if base.exists() {
let _ = std::fs::remove_dir_all(&base);
}
std::fs::create_dir_all(&base).expect("create test home");
base
});
std::env::set_var("HOME", home);
#[cfg(windows)]
std::env::set_var("USERPROFILE", home);
home.as_path()
}
pub fn reset_test_fs() {
let home = ensure_test_home();
for sub in [
".claude",
".claude-env-home",
".codex",
".codex-agent-home",
".agents",
".hermes",
".hermes-env-home",
".cc-switch",
".cc-switch-tui",
".gemini",
".openclaw",
".config",
] {
let path = home.join(sub);
if path.exists() {
if let Err(err) = std::fs::remove_dir_all(&path) {
eprintln!("failed to clean {}: {}", path.display(), err);
}
}
}
let claude_json = home.join(".claude.json");
if claude_json.exists() {
let _ = std::fs::remove_file(&claude_json);
}
let _ = update_settings(AppSettings::default());
}
pub fn test_mutex() -> &'static Mutex<()> {
static MUTEX: OnceLock<Mutex<()>> = OnceLock::new();
MUTEX.get_or_init(|| Mutex::new(()))
}
pub fn lock_test_mutex() -> MutexGuard<'static, ()> {
test_mutex()
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
pub fn state_from_config(config: MultiAppConfig) -> AppState {
let _ = ensure_test_home();
let db = Arc::new(Database::init().expect("create database"));
db.migrate_from_json(&config)
.expect("seed database from config");
let mut config = config;
ProviderService::migrate_common_config_upstream_semantics_if_needed(&db, &mut config)
.expect("migrate common config semantics for test state");
AppState {
db: db.clone(),
config: RwLock::new(config),
proxy_service: ProxyService::new(db),
}
}
#[allow(dead_code)]
pub struct CurrentDirGuard {
previous: PathBuf,
}
impl CurrentDirGuard {
#[allow(dead_code)]
pub fn change_to(path: &Path) -> Self {
let previous = std::env::current_dir().expect("read current dir");
std::fs::create_dir_all(path).expect("create target cwd");
std::env::set_current_dir(path).expect("switch current dir");
Self { previous }
}
}
impl Drop for CurrentDirGuard {
fn drop(&mut self) {
std::env::set_current_dir(&self.previous).expect("restore current dir");
}
}