using ClaudeDo.Ui.ViewModels.Islands; namespace ClaudeDo.Ui.Tests.ViewModels; public class PrepPanelViewModelTests { private sealed class Worker : StubWorkerClient { public TaskCompletionSource Gate { get; } = new(); public bool Throw { get; set; } public override Task RunDailyPrepNowAsync() => Throw ? Task.FromException(new InvalidOperationException("boom")) : Gate.Task; } [Fact] public async Task PlanDay_sets_PrepOperation_IsRunning_before_the_started_broadcast_arrives() { var worker = new Worker(); var vm = new PrepPanelViewModel(worker); var plan = vm.PlanDayCommand.ExecuteAsync(null); Assert.True(vm.PrepOperation.IsRunning); Assert.False(vm.IsPrepRunning); // the broadcast hasn't fired yet worker.Gate.SetResult(true); await plan; Assert.False(vm.PrepOperation.IsRunning); } [Fact] public async Task PlanDayCommand_CanExecute_is_locked_while_running() { var worker = new Worker(); var vm = new PrepPanelViewModel(worker); var plan = vm.PlanDayCommand.ExecuteAsync(null); Assert.False(vm.PlanDayCommand.CanExecute(null)); Assert.False(vm.IsPlanDayEnabled); worker.Gate.SetResult(true); await plan; Assert.True(vm.PlanDayCommand.CanExecute(null)); Assert.True(vm.IsPlanDayEnabled); } [Fact] public async Task PlanDay_ends_the_operation_when_the_worker_call_throws() { var worker = new Worker { Throw = true }; var vm = new PrepPanelViewModel(worker); await vm.PlanDayCommand.ExecuteAsync(null); Assert.False(vm.PrepOperation.IsRunning); Assert.True(vm.PlanDayCommand.CanExecute(null)); } }