using System.IO; using ClaudeDo.Localization; using ClaudeDo.Ui.Localization; using ClaudeDo.Ui.Services; using ClaudeDo.Ui.ViewModels.Modals; namespace ClaudeDo.Ui.Tests.ViewModels; public class WorktreesOverviewModalErrorTests { public WorktreesOverviewModalErrorTests() { var dir = AppContext.BaseDirectory; while (dir is not null && !Directory.Exists(Path.Combine(dir, "src", "ClaudeDo.Localization", "locales"))) dir = Path.GetDirectoryName(dir); Loc.Current = new Localizer( LocaleStore.Load(Path.Combine(dir!, "src", "ClaudeDo.Localization", "locales")), "en"); } private sealed class ThrowingWorker : StubWorkerClient { public string ExceptionMessage { get; init; } = "worktree is locked by another process"; public override Task CleanupFinishedWorktreesAsync(string? listId = null) => throw new Exception(ExceptionMessage); public override Task ForceRemoveWorktreeAsync(string taskId) => throw new Exception(ExceptionMessage); } private static WorktreesOverviewModalViewModel NewVm(ThrowingWorker worker) => new(worker, () => null!, new MergeCoordinator()); [Fact] public async Task CleanupFinished_WhenWorkerThrows_ShowsExceptionMessage() { var worker = new ThrowingWorker(); var vm = NewVm(worker); await vm.CleanupFinishedCommand.ExecuteAsync(null); Assert.NotNull(vm.StatusMessage); Assert.Contains(worker.ExceptionMessage, vm.StatusMessage); Assert.False(vm.IsBusy); } [Fact] public async Task ForceRemove_WhenWorkerThrows_ShowsExceptionMessage_AndKeepsRow() { var worker = new ThrowingWorker(); var vm = NewVm(worker); var row = new WorktreeOverviewRowViewModel { TaskId = "task-1", TaskTitle = "Task 1", TaskStatus = ClaudeDo.Data.Models.TaskStatus.Idle, State = ClaudeDo.Data.Models.WorktreeState.Active, }; vm.AddRowForTest(row); await vm.ForceRemoveCommand.ExecuteAsync(row); Assert.NotNull(vm.StatusMessage); Assert.Contains(worker.ExceptionMessage, vm.StatusMessage); Assert.Contains(row, vm.Rows); } }