Also stub new IWorkerClient planning members in FakeWorkerClient to restore build. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
151 lines
6.7 KiB
C#
151 lines
6.7 KiB
C#
using ClaudeDo.Data;
|
|
using ClaudeDo.Data.Models;
|
|
using ClaudeDo.Ui.Services;
|
|
using ClaudeDo.Ui.ViewModels.Islands;
|
|
using CommunityToolkit.Mvvm.Input;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Xunit;
|
|
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
|
|
|
namespace ClaudeDo.Worker.Tests.UiVm;
|
|
|
|
// ── Fake worker client ────────────────────────────────────────────────────────
|
|
|
|
sealed class FakeWorkerClient : IWorkerClient
|
|
{
|
|
public int StartPlanningCalls { get; private set; }
|
|
public int ResumePlanningCalls { get; private set; }
|
|
public int DiscardPlanningCalls { get; private set; }
|
|
public int FinalizePlanningCalls { get; private set; }
|
|
public int WakeQueueCalls { get; private set; }
|
|
|
|
public event Action<string>? TaskUpdatedEvent;
|
|
public event Action<string>? WorktreeUpdatedEvent;
|
|
public event Action<string, string>? TaskMessageEvent;
|
|
public void RaiseTaskUpdated(string taskId) => TaskUpdatedEvent?.Invoke(taskId);
|
|
public void RaiseWorktreeUpdated(string taskId) => WorktreeUpdatedEvent?.Invoke(taskId);
|
|
public void RaiseTaskMessage(string taskId, string line) => TaskMessageEvent?.Invoke(taskId, line);
|
|
|
|
public Task WakeQueueAsync() { WakeQueueCalls++; return Task.CompletedTask; }
|
|
public Task StartPlanningSessionAsync(string taskId, CancellationToken ct = default) { StartPlanningCalls++; return Task.CompletedTask; }
|
|
public Task ResumePlanningSessionAsync(string taskId, CancellationToken ct = default) { ResumePlanningCalls++; return Task.CompletedTask; }
|
|
public Task DiscardPlanningSessionAsync(string taskId, CancellationToken ct = default) { DiscardPlanningCalls++; return Task.CompletedTask; }
|
|
public Task FinalizePlanningSessionAsync(string taskId, bool queueAgentTasks = true, CancellationToken ct = default) { FinalizePlanningCalls++; return Task.CompletedTask; }
|
|
public Task<int> GetPendingDraftCountAsync(string taskId, CancellationToken ct = default) => Task.FromResult(0);
|
|
|
|
public event Action<string, string>? PlanningMergeStartedEvent;
|
|
public event Action<string, string>? PlanningSubtaskMergedEvent;
|
|
public event Action<string, string, IReadOnlyList<string>>? PlanningMergeConflictEvent;
|
|
public event Action<string>? PlanningMergeAbortedEvent;
|
|
public event Action<string>? PlanningCompletedEvent;
|
|
|
|
public Task<MergeTargetsDto?> GetMergeTargetsAsync(string taskId) => Task.FromResult<MergeTargetsDto?>(null);
|
|
public Task<IReadOnlyList<SubtaskDiffDto>> GetPlanningAggregateAsync(string planningTaskId) => Task.FromResult<IReadOnlyList<SubtaskDiffDto>>(Array.Empty<SubtaskDiffDto>());
|
|
public Task<CombinedDiffResultDto?> BuildPlanningIntegrationBranchAsync(string planningTaskId, string targetBranch) => Task.FromResult<CombinedDiffResultDto?>(null);
|
|
public Task MergeAllPlanningAsync(string planningTaskId, string targetBranch) => Task.CompletedTask;
|
|
public Task ContinuePlanningMergeAsync(string planningTaskId) => Task.CompletedTask;
|
|
public Task AbortPlanningMergeAsync(string planningTaskId) => Task.CompletedTask;
|
|
}
|
|
|
|
// ── Helper to build VM with pre-seeded Items ──────────────────────────────────
|
|
|
|
file static class VmFactory
|
|
{
|
|
// Minimal SQLite :memory: factory — never actually called in these tests
|
|
// (we seed Items directly), but required by the VM constructor.
|
|
private static IDbContextFactory<ClaudeDoDbContext> NullDbFactory()
|
|
{
|
|
var opts = new DbContextOptionsBuilder<ClaudeDoDbContext>()
|
|
.UseSqlite("DataSource=:memory:")
|
|
.Options;
|
|
return new NullDbContextFactory(opts);
|
|
}
|
|
|
|
private sealed class NullDbContextFactory(DbContextOptions<ClaudeDoDbContext> opts)
|
|
: IDbContextFactory<ClaudeDoDbContext>
|
|
{
|
|
public ClaudeDoDbContext CreateDbContext() => new(opts);
|
|
}
|
|
|
|
public static (TasksIslandViewModel vm, FakeWorkerClient worker) Create(
|
|
IEnumerable<TaskRowViewModel> rows)
|
|
{
|
|
var worker = new FakeWorkerClient();
|
|
var vm = new TasksIslandViewModel(NullDbFactory(), worker);
|
|
foreach (var r in rows)
|
|
vm.Items.Add(r);
|
|
vm.Regroup();
|
|
return (vm, worker);
|
|
}
|
|
}
|
|
|
|
// ── Tests ─────────────────────────────────────────────────────────────────────
|
|
|
|
public class TasksIslandViewModelPlanningTests
|
|
{
|
|
private static TaskRowViewModel MakeRow(string id, TaskStatus status, string? parentId = null, int sortOrder = 0)
|
|
=> new TaskRowViewModel { Id = id, Status = status, ParentTaskId = parentId };
|
|
|
|
[Fact]
|
|
public void ToggleExpand_CollapsesChildrenOfPlanningParent()
|
|
{
|
|
var parent = MakeRow("p1", TaskStatus.Planning);
|
|
var child1 = MakeRow("c1", TaskStatus.Draft, "p1");
|
|
var child2 = MakeRow("c2", TaskStatus.Draft, "p1");
|
|
|
|
var (vm, _) = VmFactory.Create([parent, child1, child2]);
|
|
|
|
// Initially expanded — children visible in OpenItems
|
|
Assert.Contains(child1, vm.OpenItems);
|
|
Assert.Contains(child2, vm.OpenItems);
|
|
|
|
// Collapse the parent
|
|
vm.ToggleExpandCommand.Execute(parent);
|
|
|
|
// Children should no longer appear
|
|
Assert.DoesNotContain(child1, vm.OpenItems);
|
|
Assert.DoesNotContain(child2, vm.OpenItems);
|
|
// Parent still present
|
|
Assert.Contains(parent, vm.OpenItems);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task OpenPlanningSession_IgnoresNonManualRow()
|
|
{
|
|
var row = MakeRow("t1", TaskStatus.Queued);
|
|
var (vm, worker) = VmFactory.Create([row]);
|
|
|
|
await ((IAsyncRelayCommand<TaskRowViewModel?>)vm.OpenPlanningSessionCommand).ExecuteAsync(row);
|
|
|
|
Assert.Equal(0, worker.StartPlanningCalls);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task OpenPlanningSession_CallsWorkerForManualRow()
|
|
{
|
|
var row = MakeRow("t1", TaskStatus.Manual);
|
|
var (vm, worker) = VmFactory.Create([row]);
|
|
|
|
await ((IAsyncRelayCommand<TaskRowViewModel?>)vm.OpenPlanningSessionCommand).ExecuteAsync(row);
|
|
|
|
Assert.Equal(1, worker.StartPlanningCalls);
|
|
}
|
|
|
|
[Fact]
|
|
public void ToggleExpand_ExpandsCollapsedParentAgain()
|
|
{
|
|
var parent = MakeRow("p1", TaskStatus.Planned);
|
|
var child = MakeRow("c1", TaskStatus.Draft, "p1");
|
|
|
|
var (vm, _) = VmFactory.Create([parent, child]);
|
|
|
|
// Collapse
|
|
vm.ToggleExpandCommand.Execute(parent);
|
|
Assert.DoesNotContain(child, vm.OpenItems);
|
|
|
|
// Re-expand
|
|
vm.ToggleExpandCommand.Execute(parent);
|
|
Assert.Contains(child, vm.OpenItems);
|
|
}
|
|
}
|