feat(ui): worker-menu Shutdown ends worker + app after confirm
Adds a Shutdown entry after the Worker menu's Restart/Update Claude CLI items. Confirms via IDialogService.ConfirmAsync, then kills this installation's ClaudeDo.Worker process(es) (shared kill loop, extracted from RestartWorkerService) and force-closes the window through the same _forceClose path MainWindow already uses for the Mission-Control close confirm, so no second confirmation dialog shows.
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using ClaudeDo.Ui.Services;
|
||||
using ClaudeDo.Ui.ViewModels;
|
||||
using ClaudeDo.Ui.ViewModels.Conflicts;
|
||||
using ClaudeDo.Ui.ViewModels.Modals;
|
||||
|
||||
namespace ClaudeDo.Ui.Tests;
|
||||
|
||||
// Covers the worker-menu "Shutdown" command: a confirm dialog gates the actual kill-worker +
|
||||
// close-app action. ShutdownOperation is a test seam standing in for the real body, so these
|
||||
// tests only assert the confirm gate, never a real process kill.
|
||||
public class IslandsShellViewModelShutdownTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Declined_DoesNotInvokeShutdownOperation()
|
||||
{
|
||||
var invoked = false;
|
||||
var vm = new IslandsShellViewModel
|
||||
{
|
||||
Dialogs = new FakeDialogService(confirmResult: false),
|
||||
ShutdownOperation = () => invoked = true,
|
||||
};
|
||||
|
||||
await vm.ShutdownCommand.ExecuteAsync(null);
|
||||
|
||||
Assert.False(invoked);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Confirmed_InvokesShutdownOperation()
|
||||
{
|
||||
var invoked = false;
|
||||
var vm = new IslandsShellViewModel
|
||||
{
|
||||
Dialogs = new FakeDialogService(confirmResult: true),
|
||||
ShutdownOperation = () => invoked = true,
|
||||
};
|
||||
|
||||
await vm.ShutdownCommand.ExecuteAsync(null);
|
||||
|
||||
Assert.True(invoked);
|
||||
}
|
||||
|
||||
private sealed class FakeDialogService(bool confirmResult) : IDialogService
|
||||
{
|
||||
public Task<bool> ConfirmAsync(string message) => Task.FromResult(confirmResult);
|
||||
|
||||
public Task ShowAboutAsync(AboutModalViewModel vm) => throw new NotImplementedException();
|
||||
public Task ShowWeeklyReportAsync(WeeklyReportModalViewModel vm) => throw new NotImplementedException();
|
||||
public Task ShowUsageMonitorAsync(UsageMonitorModalViewModel vm) => throw new NotImplementedException();
|
||||
public Task ShowSettingsAsync(SettingsModalViewModel vm) => throw new NotImplementedException();
|
||||
public Task ShowListSettingsAsync(ListSettingsModalViewModel vm) => throw new NotImplementedException();
|
||||
public Task ShowRepoImportAsync(RepoImportModalViewModel vm) => throw new NotImplementedException();
|
||||
public Task ShowWorktreesOverviewAsync(WorktreesOverviewModalViewModel vm) => throw new NotImplementedException();
|
||||
public Task<IReadOnlyList<string>?> ShowMergeHelperSelectionAsync(MergeHelperSelectionModalViewModel vm) => throw new NotImplementedException();
|
||||
public Task ShowWorkerConnectionAsync(WorkerConnectionModalViewModel vm) => throw new NotImplementedException();
|
||||
public Task ShowConflictResolverAsync(ConflictResolverViewModel vm) => throw new NotImplementedException();
|
||||
public Task ShowLogVisualizerAsync(LogVisualizerViewModel vm) => throw new NotImplementedException();
|
||||
public Task ShowErrorAsync(string message) => Task.CompletedTask;
|
||||
public void ShowMissionControl(MissionControlViewModel vm) => throw new NotImplementedException();
|
||||
public void ShowCheatSheets(CheatSheetsViewModel vm) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user