using Paca.App.ViewModels; using Paca.Core.Config; using Xunit; namespace Paca.Tests.Infrastructure; public sealed class SettingsViewModelTests { [Fact] public void LoadFromConfig_MapsAllPropertiesAccurately() { var config = new AppConfig { Theme = "light", AccentColor = "#2563EB", Concurrency = 16, MaxConcurrentDownloads = 5, OutputFolder = @"C:\Downloads\Videos", ToastDurationMs = 4500, ReduceMotion = true }; var vm = new SettingsViewModel(config); Assert.Equal("light", vm.Theme); Assert.Equal("#2563EB", vm.AccentColor); Assert.Equal(16, vm.Concurrency); Assert.Equal(5, vm.MaxConcurrentDownloads); Assert.Equal(@"C:\Downloads\Videos", vm.OutputFolder); Assert.Equal(4500, vm.ToastDurationMs); Assert.True(vm.ReduceMotion); Assert.False(vm.IsDirty); Assert.Equal(0, vm.DirtyCount); } [Fact] public void PropertyChanged_TracksDirtyStateAccurately() { var config = new AppConfig(); var vm = new SettingsViewModel(config); Assert.False(vm.IsDirty); vm.Concurrency = 32; Assert.True(vm.IsDirty); Assert.True(vm.DirtyCount >= 1); // 원본 값으로 복구하면 IsDirty 해제 vm.Concurrency = config.Concurrency; Assert.False(vm.IsDirty); Assert.Equal(0, vm.DirtyCount); } [Fact] public void Validate_CatchesInvalidInputs() { var config = new AppConfig(); var vm = new SettingsViewModel(config); Assert.True(vm.Validate()); Assert.False(vm.HasErrors); // 1. 유효하지 않은 세그먼트 동시 수 vm.Concurrency = 0; Assert.False(vm.Validate()); Assert.True(vm.HasErrors); Assert.Contains("세그먼트", vm.ValidationSummary); vm.Concurrency = 8; Assert.True(vm.Validate()); // 2. 유효하지 않은 토스트 지속시간 vm.ToastDurationMs = 100; Assert.False(vm.Validate()); Assert.True(vm.HasErrors); vm.ToastDurationMs = 3000; Assert.True(vm.Validate()); // 3. 유효하지 않은 강조색 포맷 vm.AccentColor = "InvalidHex"; Assert.False(vm.Validate()); Assert.True(vm.HasErrors); vm.AccentColor = "#6366F1"; Assert.True(vm.Validate()); // 4. 유효하지 않은 예약 시간 포맷 vm.DownloadScheduleWindow = "25:99-99:00"; Assert.False(vm.Validate()); Assert.True(vm.HasErrors); vm.DownloadScheduleWindow = "02:00-08:00"; Assert.True(vm.Validate()); } [Fact] public void CommitToConfig_UpdatesTargetConfigCompletely() { var sourceConfig = new AppConfig(); var vm = new SettingsViewModel(sourceConfig) { Theme = "highcontrast", AccentColor = "#34D399", Concurrency = 12, MaxConcurrentDownloads = 6, OutputFolder = @"D:\Media\Saved", ReduceMotion = true, AutoStart = true }; var target = new AppConfig(); vm.CommitToConfig(target); Assert.Equal("highcontrast", target.Theme); Assert.Equal("#34D399", target.AccentColor); Assert.Equal(12, target.Concurrency); Assert.Equal(6, target.MaxConcurrentDownloads); Assert.Equal(@"D:\Media\Saved", target.OutputFolder); Assert.True(target.ReduceMotion); Assert.True(target.AutoStart); } [Fact] public void ResetToDefaults_RestoresStandardDefaults() { var config = new AppConfig { Theme = "light", Concurrency = 32, MaxConcurrentDownloads = 10 }; var vm = new SettingsViewModel(config); vm.ResetToDefaults(); Assert.Equal("dark", vm.Theme); Assert.Equal(8, vm.Concurrency); Assert.Equal(3, vm.MaxConcurrentDownloads); } }