Files
ClaudeDo/tests/ClaudeDo.Ui.Tests/ViewModels/ConflictResolutionViewModelTests.cs
mika kuns 59d72635da test(ui): rebase IWorkerClient fakes onto shared StubWorkerClient base
Add a StubWorkerClient base implementing the full IWorkerClient surface so
the planning/conflict/diff test fakes only override the members they exercise.
Eliminates the constructor-drift duplication across the three fakes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 09:39:12 +02:00

105 lines
3.5 KiB
C#

using ClaudeDo.Data.Models;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels.Planning;
namespace ClaudeDo.Ui.Tests.ViewModels;
public class ConflictResolutionViewModelTests
{
// ------------------------------------------------------------------ fake
private sealed class FakeWorker : StubWorkerClient
{
public string? ContinueCalledWith { get; private set; }
public string? AbortCalledWith { get; private set; }
public Exception? ContinueThrows { get; set; }
public Exception? AbortThrows { get; set; }
public override Task ContinuePlanningMergeAsync(string planningTaskId)
{
ContinueCalledWith = planningTaskId;
if (ContinueThrows is not null) throw ContinueThrows;
return Task.CompletedTask;
}
public override Task AbortPlanningMergeAsync(string planningTaskId)
{
AbortCalledWith = planningTaskId;
if (AbortThrows is not null) throw AbortThrows;
return Task.CompletedTask;
}
}
private static ConflictResolutionViewModel BuildVm(FakeWorker worker, string planningTaskId = "plan-1") =>
new ConflictResolutionViewModel(
worker,
planningTaskId,
subtaskTitle: "My subtask",
targetBranch: "main",
conflictedFiles: new[] { "src/Foo.cs", "src/Bar.cs" },
worktreePath: "C:/worktrees/plan-1");
// ------------------------------------------------------------------ tests
[Fact]
public async Task ContinueAsync_CallsHub_AndClosesOnSuccess()
{
var worker = new FakeWorker();
var vm = BuildVm(worker, "plan-42");
bool closeCalled = false;
vm.CloseRequested = () => closeCalled = true;
await vm.ContinueCommand.ExecuteAsync(null);
Assert.Equal("plan-42", worker.ContinueCalledWith);
Assert.True(closeCalled);
Assert.Null(vm.ActionError);
}
[Fact]
public async Task ContinueAsync_HubThrows_ShowsActionErrorAndStaysOpen()
{
var worker = new FakeWorker { ContinueThrows = new InvalidOperationException("hub down") };
var vm = BuildVm(worker);
bool closeCalled = false;
vm.CloseRequested = () => closeCalled = true;
await vm.ContinueCommand.ExecuteAsync(null);
Assert.False(closeCalled);
Assert.Equal("hub down", vm.ActionError);
}
[Fact]
public async Task AbortAsync_CallsHub_AndClosesOnSuccess()
{
var worker = new FakeWorker();
var vm = BuildVm(worker, "plan-99");
bool closeCalled = false;
vm.CloseRequested = () => closeCalled = true;
await vm.AbortCommand.ExecuteAsync(null);
Assert.Equal("plan-99", worker.AbortCalledWith);
Assert.True(closeCalled);
Assert.Null(vm.ActionError);
}
[Fact]
public async Task AbortAsync_HubThrows_ShowsActionError()
{
var worker = new FakeWorker { AbortThrows = new InvalidOperationException("abort failed") };
var vm = BuildVm(worker);
bool closeCalled = false;
vm.CloseRequested = () => closeCalled = true;
await vm.AbortCommand.ExecuteAsync(null);
Assert.False(closeCalled);
Assert.Equal("abort failed", vm.ActionError);
}
// OpenInVsCode is not unit-tested here because abstracting Process.Start
// would require an indirection layer that isn't part of the approved design.
// The error path is covered by the VsCodeError property being set on catch.
}