namespace Zcbot.WindowsNode.Tests; public sealed class DataRootPageViewModelTests { [Fact] public async Task ChangeMigratesConfirmedTargetAndReportsCompletion() { var service = new FakeDataRootManagementService(); var viewModel = new DataRootPageViewModel(service); NodeDataMigrationResult? completed = null; viewModel.ChooseTargetRequested += _ => @"D:\zcbot-data"; viewModel.ConfirmMigrationRequested += _ => true; viewModel.MigrationRequested += target => Task.FromResult( new NodeDataMigrationResult(@"C:\node-data", target, 3, 4096)); viewModel.MigrationCompleted += result => completed = result; viewModel.ChangeCommand.Execute(null); await WaitUntilAsync(() => completed is not null); Assert.Equal(@"D:\zcbot-data", completed!.TargetDirectory); Assert.Contains("3 个文件", viewModel.Status); Assert.True(viewModel.IsMigrating); } [Fact] public async Task ChangeRefusesPendingJobsBeforeChoosingATarget() { var service = new FakeDataRootManagementService { PendingJobs = true }; var viewModel = new DataRootPageViewModel(service); var chooseCount = 0; viewModel.ChooseTargetRequested += _ => { chooseCount++; return @"D:\zcbot-data"; }; viewModel.MigrationRequested += _ => throw new InvalidOperationException(); viewModel.ChangeCommand.Execute(null); await WaitUntilAsync(() => viewModel.Status.Contains("未完成任务")); Assert.Equal(0, chooseCount); Assert.False(viewModel.IsMigrating); } [Fact] public void EnvironmentManagedRootDisablesMigration() { var service = new FakeDataRootManagementService { Selection = new NodeDataRootSelection( @"D:\managed", IsEnvironmentManaged: true, "environment"), }; var viewModel = new DataRootPageViewModel(service); Assert.False(viewModel.ChangeCommand.CanExecute(null)); Assert.Contains(NodeDataRootSettings.EnvironmentVariableName, viewModel.Status); } private static async Task WaitUntilAsync(Func condition) { var deadline = DateTime.UtcNow.AddSeconds(2); while (!condition() && DateTime.UtcNow < deadline) { await Task.Delay(10); } Assert.True(condition()); } private sealed class FakeDataRootManagementService : IDataRootManagementService { internal NodeDataRootSelection Selection { get; init; } = new( @"C:\node-data", IsEnvironmentManaged: false, "default"); internal bool PendingJobs { get; init; } public NodeDataRootSelection ReadSelection() => Selection; public bool HasPendingJobs => PendingJobs; public string Normalize(string path) => Path.TrimEndingDirectorySeparator(path); } }