fix(ui): surface worker connect/restart failures instead of no-opting
WorkerConnectionModalViewModel's StartWorker/RerunInstaller swallowed missing-exe and Process.Start failures behind an empty catch and closed the dialog anyway, looking like success. Now both show an inline error and keep the dialog open; only a real launch closes it. IslandsShellViewModel.RestartWorkerAsync never checked whether the relaunched worker actually stayed up, and its status property was never bound in any view. RestartWorkerService now fails if the new process exits within a short survival window, and the outcome routes through the footer error/success strip instead of the dead RestartWorkerStatus. InstallArtifactLocator.Find() is now virtual and its two subclasses unsealed so tests can fake exe resolution without touching the real filesystem/registry.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
using ClaudeDo.Ui.Services;
|
||||
using ClaudeDo.Ui.ViewModels.Modals;
|
||||
|
||||
namespace ClaudeDo.Ui.Tests.ViewModels;
|
||||
|
||||
public sealed class WorkerConnectionModalViewModelTests
|
||||
{
|
||||
private sealed class FakeWorkerLocator(string? path) : WorkerLocator
|
||||
{
|
||||
public override string? Find() => path;
|
||||
}
|
||||
|
||||
private sealed class FakeInstallerLocator(string? path) : InstallerLocator
|
||||
{
|
||||
public override string? Find() => path;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartWorker_ExeNotFound_ShowsErrorAndKeepsDialogOpen()
|
||||
{
|
||||
var vm = new WorkerConnectionModalViewModel(new FakeWorkerLocator(null), new FakeInstallerLocator(null));
|
||||
var closed = false;
|
||||
vm.CloseAction = () => closed = true;
|
||||
|
||||
vm.StartWorkerCommand.Execute(null);
|
||||
|
||||
Assert.False(closed);
|
||||
Assert.False(string.IsNullOrEmpty(vm.ErrorText));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartWorker_ProcessStartThrows_ShowsErrorAndKeepsDialogOpen()
|
||||
{
|
||||
var vm = new WorkerConnectionModalViewModel(new FakeWorkerLocator(@"C:\fake\ClaudeDo.Worker.exe"), new FakeInstallerLocator(null))
|
||||
{
|
||||
ProcessStarter = _ => throw new InvalidOperationException("blocked by AV")
|
||||
};
|
||||
var closed = false;
|
||||
vm.CloseAction = () => closed = true;
|
||||
|
||||
vm.StartWorkerCommand.Execute(null);
|
||||
|
||||
Assert.False(closed);
|
||||
Assert.Contains("blocked by AV", vm.ErrorText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartWorker_Success_ClearsErrorAndClosesDialog()
|
||||
{
|
||||
var vm = new WorkerConnectionModalViewModel(new FakeWorkerLocator(@"C:\fake\ClaudeDo.Worker.exe"), new FakeInstallerLocator(null))
|
||||
{
|
||||
ProcessStarter = _ => { }
|
||||
};
|
||||
var closed = false;
|
||||
vm.CloseAction = () => closed = true;
|
||||
|
||||
vm.StartWorkerCommand.Execute(null);
|
||||
|
||||
Assert.True(closed);
|
||||
Assert.Null(vm.ErrorText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RerunInstaller_NotFound_ShowsErrorAndDoesNotThrow()
|
||||
{
|
||||
var vm = new WorkerConnectionModalViewModel(new FakeWorkerLocator(null), new FakeInstallerLocator(null));
|
||||
|
||||
vm.RerunInstallerCommand.Execute(null);
|
||||
|
||||
Assert.False(string.IsNullOrEmpty(vm.ErrorText));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RerunInstaller_ProcessStartThrows_ShowsError()
|
||||
{
|
||||
var vm = new WorkerConnectionModalViewModel(new FakeWorkerLocator(null), new FakeInstallerLocator(@"C:\fake\ClaudeDo.Installer.exe"))
|
||||
{
|
||||
ProcessStarter = _ => throw new InvalidOperationException("blocked by AV")
|
||||
};
|
||||
|
||||
vm.RerunInstallerCommand.Execute(null);
|
||||
|
||||
Assert.Contains("blocked by AV", vm.ErrorText);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user