use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use abstractcode::config::{legacy_prefs_path, prefs_path, Prefs};
static ENV_LOCK: Mutex<()> = Mutex::new(());
struct EnvGuard(Vec<(&'static str, Option<String>)>);
impl EnvGuard {
fn set(vars: &[(&'static str, Option<&str>)]) -> EnvGuard {
let saved = vars
.iter()
.map(|(k, _)| (*k, std::env::var(k).ok()))
.collect();
for (k, v) in vars {
match v {
Some(val) => std::env::set_var(k, val),
None => std::env::remove_var(k),
}
}
EnvGuard(saved)
}
}
impl Drop for EnvGuard {
fn drop(&mut self) {
for (k, v) in &self.0 {
match v {
Some(val) => std::env::set_var(k, val),
None => std::env::remove_var(k),
}
}
}
}
fn scratch(tag: &str) -> PathBuf {
let dir = std::env::temp_dir().join(format!("acode-prefsmig-{tag}-{}", std::process::id()));
let _ = fs::remove_dir_all(&dir);
fs::create_dir_all(dir.join(".abstractcode-tui")).expect("mkdir legacy");
fs::create_dir_all(dir.join(".abstractcode")).expect("mkdir current");
dir
}
fn write_legacy(home: &Path, theme: &str) {
fs::write(
home.join(".abstractcode-tui").join("prefs.json"),
format!(r#"{{"theme":"{theme}","tool_approval":{{"accepted_tier":"all"}}}}"#),
)
.expect("write legacy");
}
#[test]
fn an_explicit_prefs_override_never_falls_back_to_the_legacy_file() {
let _lock = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let home = scratch("override");
write_legacy(&home, "abstract-aurora");
let fresh = home.join("fresh").join("prefs.json");
let _guard = EnvGuard::set(&[
("HOME", Some(home.to_str().unwrap())),
("ABSTRACTCODE_PREFS_FILE", Some(fresh.to_str().unwrap())),
("ABSTRACTCODE_TUI_PREFS_FILE", None),
]);
assert_eq!(legacy_prefs_path(), None);
assert_eq!(prefs_path(), fresh);
assert!(Prefs::load().theme.is_none());
let _ = fs::remove_dir_all(home);
}
#[test]
fn the_legacy_env_override_is_also_honored_exactly() {
let _lock = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let home = scratch("legacy-override");
write_legacy(&home, "abstract-aurora");
let fresh = home.join("fresh2").join("prefs.json");
let _guard = EnvGuard::set(&[
("HOME", Some(home.to_str().unwrap())),
("ABSTRACTCODE_PREFS_FILE", None),
("ABSTRACTCODE_TUI_PREFS_FILE", Some(fresh.to_str().unwrap())),
]);
assert_eq!(legacy_prefs_path(), None);
assert_eq!(prefs_path(), fresh);
let _ = fs::remove_dir_all(home);
}
#[test]
fn prefs_from_before_the_rename_are_read_once_and_saved_forward() {
let _lock = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let home = scratch("migrate");
write_legacy(&home, "abstract-aurora");
let current = home.join(".abstractcode").join("prefs.json");
let _guard = EnvGuard::set(&[
("HOME", Some(home.to_str().unwrap())),
("ABSTRACTCODE_PREFS_FILE", None),
("ABSTRACTCODE_TUI_PREFS_FILE", None),
]);
assert_eq!(
legacy_prefs_path(),
Some(home.join(".abstractcode-tui").join("prefs.json"))
);
let prefs = Prefs::load();
assert_eq!(prefs.theme.as_deref(), Some("abstract-aurora"));
assert_eq!(prefs.path.as_ref(), Some(¤t));
prefs.save().expect("save");
assert!(current.exists(), "the first save migrates the file forward");
assert_eq!(legacy_prefs_path(), None);
assert_eq!(Prefs::load().theme.as_deref(), Some("abstract-aurora"));
let _ = fs::remove_dir_all(home);
}
#[test]
fn the_current_prefs_file_wins_when_both_exist() {
let _lock = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let home = scratch("both");
write_legacy(&home, "abstract-aurora");
fs::write(
home.join(".abstractcode").join("prefs.json"),
r#"{"theme":"nord"}"#,
)
.expect("write current");
let _guard = EnvGuard::set(&[
("HOME", Some(home.to_str().unwrap())),
("ABSTRACTCODE_PREFS_FILE", None),
("ABSTRACTCODE_TUI_PREFS_FILE", None),
]);
assert_eq!(legacy_prefs_path(), None);
assert_eq!(Prefs::load().theme.as_deref(), Some("nord"));
let _ = fs::remove_dir_all(home);
}