use std::path::PathBuf; use std::sync::Arc; use domain::{ DefaultAgentTerminalInterface, FileSystem, IdeaUserSettings, ProjectPath, ProjectSettings, ProjectSettingsStore, ThemeColor, UserSettingsStore, }; use infrastructure::{FsProjectSettingsStore, FsUserSettingsStore, LocalFileSystem}; use uuid::Uuid; struct TempDir(PathBuf); impl TempDir { fn new() -> Self { let path = std::env::temp_dir().join(format!("idea-settings-{}", Uuid::new_v4())); std::fs::create_dir_all(&path).unwrap(); Self(path) } fn root(&self) -> ProjectPath { ProjectPath::new(self.0.to_string_lossy().into_owned()).unwrap() } } impl Drop for TempDir { fn drop(&mut self) { let _ = std::fs::remove_dir_all(&self.0); } } fn fs() -> Arc { Arc::new(LocalFileSystem::new()) } #[tokio::test] async fn global_user_preference_persists_outside_project() { let tmp = TempDir::new(); let store = FsUserSettingsStore::new(fs(), tmp.0.to_string_lossy()); assert_eq!(store.load().await.unwrap(), IdeaUserSettings::default()); let settings = IdeaUserSettings { default_agent_terminal_interface: Some(DefaultAgentTerminalInterface::IdeaCli), }; store.save(&settings).await.unwrap(); assert_eq!( FsUserSettingsStore::new(fs(), tmp.0.to_string_lossy()) .load() .await .unwrap(), settings ); assert!(tmp.0.join("settings.json").is_file()); assert!(!tmp.0.join(".ideai").exists()); } #[tokio::test] async fn project_settings_missing_then_valid_round_trip() { let tmp = TempDir::new(); let store = FsProjectSettingsStore::new(fs()); assert_eq!( store.load(&tmp.root()).await.unwrap(), ProjectSettings::default() ); assert!(!tmp.0.join(".ideai/project-settings.json").exists()); let settings = ProjectSettings { theme_color: Some(ThemeColor::new("#12AbEF").unwrap()), }; store.save(&tmp.root(), &settings).await.unwrap(); assert_eq!(store.load(&tmp.root()).await.unwrap(), settings); assert_eq!( std::fs::read_to_string(tmp.0.join(".ideai/project-settings.json")).unwrap(), "{\n \"themeColor\": \"#12AbEF\"\n}\n" ); assert!(!tmp.0.join(".ideai/CONTEXT.md").exists()); } #[tokio::test] async fn project_settings_are_isolated_and_missing_defaults_are_not_persisted() { let first = TempDir::new(); let second = TempDir::new(); let store = FsProjectSettingsStore::new(fs()); store .save( &first.root(), &ProjectSettings { theme_color: Some(ThemeColor::new("#AA3366").unwrap()), }, ) .await .unwrap(); assert_eq!( store.load(&second.root()).await.unwrap(), ProjectSettings::default() ); assert!(!second.0.join(".ideai/project-settings.json").exists()); assert_eq!( store .load(&first.root()) .await .unwrap() .theme_color .as_ref() .map(ThemeColor::as_str), Some("#AA3366") ); } #[test] fn theme_color_validation_is_strict() { assert!(ThemeColor::new("#112233").is_ok()); for invalid in ["112233", "#123", "#GG0000", "#11223344", "# 12233"] { assert!(ThemeColor::new(invalid).is_err(), "accepted {invalid}"); } }