Merge claudedo/02777289600f499ab274e844eb070ff4

This commit is contained in:
mika kuns
2026-08-05 09:24:21 +02:00
19 changed files with 20 additions and 608 deletions
@@ -3,8 +3,6 @@ using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels;
using ClaudeDo.Ui.ViewModels.Islands;
using ClaudeDo.Ui.ViewModels.MissionControl;
using Microsoft.EntityFrameworkCore;
using Xunit;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
@@ -60,7 +58,6 @@ public class MissionControlViewModelTests : IDisposable
worker.RaiseTaskStarted("slot-1", "t1", DateTime.UtcNow);
worker.RaiseTaskStarted("slot-2", "t2", DateTime.UtcNow);
Assert.Empty(vm.Monitors);
Assert.Empty(vm.Panes);
}
@@ -70,7 +67,6 @@ public class MissionControlViewModelTests : IDisposable
var worker = new SeededFakeWorker();
using var vm = BuildVm(worker);
Assert.Empty(vm.Monitors);
Assert.Empty(vm.Panes);
}
@@ -171,161 +167,6 @@ public class MissionControlViewModelTests : IDisposable
Assert.Equal("r1", opened);
}
// ── monitor machinery tests (EnsureMonitor still works when called directly) ─
[Fact]
public void EnsureMonitor_TwoCalls_CreateTwoMonitors_ColumnCountTwo()
{
var worker = new FakeWorker();
using var vm = BuildVm(worker);
vm.EnsureMonitor("t1");
vm.EnsureMonitor("t2");
Assert.Equal(2, vm.Monitors.Count);
Assert.Equal(2, vm.ColumnCount);
}
[Fact]
public void EnsureMonitor_DuplicateCall_DoesNotAddSecondMonitor()
{
var worker = new FakeWorker();
using var vm = BuildVm(worker);
vm.EnsureMonitor("t1");
vm.EnsureMonitor("t1");
Assert.Equal(1, vm.Monitors.Count);
}
[Fact]
public void Finish_KeepsPane_AndFlipsState()
{
var worker = new FakeWorker();
using var vm = BuildVm(worker);
vm.EnsureMonitor("t1");
worker.RaiseTaskFinished("slot-1", "t1", "done", DateTime.UtcNow);
Assert.Equal(1, vm.Monitors.Count);
Assert.True(vm.Monitors[0].IsDone);
}
[Fact]
public void ClearFinished_RemovesTerminalMonitors()
{
var worker = new FakeWorker();
using var vm = BuildVm(worker);
vm.EnsureMonitor("t1");
vm.EnsureMonitor("t2");
worker.RaiseTaskFinished("slot-1", "t1", "done", DateTime.UtcNow);
vm.ClearFinishedCommand.Execute(null);
Assert.Equal(1, vm.Monitors.Count);
Assert.Equal("t2", vm.Monitors[0].SubscribedTaskId);
Assert.Equal(1, vm.ColumnCount);
}
[Fact]
public void OpenInApp_PropagatesToMonitors_AndCommandInvokesHook()
{
var worker = new FakeWorker();
using var vm = BuildVm(worker);
string? revealed = null;
vm.OpenInApp = id => revealed = id;
vm.EnsureMonitor("t1");
vm.Monitors[0].OpenInAppCommand.Execute(null);
Assert.Equal("t1", revealed);
}
[Fact]
public void Detach_RemovesFromGrid_ThenReDockRestores()
{
var worker = new FakeWorker();
using var vm = BuildVm(worker);
TaskMonitorViewModel? detached = null;
Action? reDock = null;
vm.ShowDetached = (m, rd) => { detached = m; reDock = rd; };
vm.EnsureMonitor("t1");
var monitor = vm.Monitors[0];
monitor.DetachCommand.Execute(null);
Assert.Empty(vm.Monitors);
Assert.Same(monitor, detached);
reDock!.Invoke();
Assert.Single(vm.Monitors);
Assert.Same(monitor, vm.Monitors[0]);
}
[Fact]
public void ClearFinished_AlsoRemoves_WaitingForReview()
{
var worker = new FakeWorker();
using var vm = BuildVm(worker);
vm.EnsureMonitor("t1");
worker.RaiseTaskFinished("slot-1", "t1", "waiting_for_review", DateTime.UtcNow);
Assert.True(vm.Monitors[0].IsWaitingForReview);
vm.ClearFinishedCommand.Execute(null);
Assert.Empty(vm.Monitors);
}
[Fact]
public void Detach_SetsIsDetached_AndReDockClearsIt()
{
var worker = new FakeWorker();
using var vm = BuildVm(worker);
Action? reDock = null;
vm.ShowDetached = (m, rd) => reDock = rd;
vm.EnsureMonitor("t1");
var monitor = vm.Monitors[0];
monitor.DetachCommand.Execute(null);
Assert.True(monitor.IsDetached);
Assert.Empty(vm.Monitors);
reDock!.Invoke();
Assert.False(monitor.IsDetached);
Assert.Single(vm.Monitors);
}
[Fact]
public void DetachCommand_WhenDetached_RequestsWindowClose()
{
var worker = new FakeWorker();
using var vm = BuildVm(worker);
vm.ShowDetached = (m, rd) => { };
vm.EnsureMonitor("t1");
var monitor = vm.Monitors[0];
var closeRequested = false;
monitor.DetachCommand.Execute(null); // detach (IsDetached = true)
monitor.CloseWindowRequested = () => closeRequested = true;
monitor.DetachCommand.Execute(null); // now acts as re-dock
Assert.True(closeRequested);
}
[Fact]
public void MoveMonitor_ReordersCollection()
{
var worker = new FakeWorker();
using var vm = BuildVm(worker);
vm.EnsureMonitor("t1");
vm.EnsureMonitor("t2");
vm.EnsureMonitor("t3");
vm.MoveMonitor(vm.Monitors[0], vm.Monitors[2]); // move t1 to t3's slot
Assert.Equal(new[] { "t2", "t3", "t1" }, vm.Monitors.Select(m => m.SubscribedTaskId).ToArray());
}
[Fact]
public async Task Queue_ReflectsQueuedTasks_InSortOrder()
{
@@ -423,17 +264,17 @@ public class MissionControlViewModelTests : IDisposable
}
[Fact]
public async Task Panes_CombinesMonitorsThenConPtySessions_InOrder()
public async Task Panes_MirrorsConPtySessions_InOrder()
{
var worker = new FakeWorker();
using var vm = BuildVm(worker);
vm.EnsureMonitor("t1");
await vm.OpenConPtySessionAsync("t1");
await vm.OpenConPtySessionAsync("t2");
Assert.Equal(2, vm.Panes.Count);
Assert.IsType<TaskMonitorViewModel>(vm.Panes[0]);
Assert.IsType<ConPtyPaneViewModel>(vm.Panes[1]);
Assert.Same(vm.ConPtySessions[0], vm.Panes[0]);
Assert.Same(vm.ConPtySessions[1], vm.Panes[1]);
Assert.Equal(2, vm.ColumnCount);
}
@@ -109,18 +109,6 @@ public class TaskMonitorViewModelTests : IDisposable
Assert.True(vm.HasRoadblock);
}
[Fact]
public void Detach_WhenNotDetached_InvokesDetachRequested()
{
var worker = new FakeWorker();
using var vm = Build(worker);
vm.SetTaskId("t1");
TaskMonitorViewModel? requested = null;
vm.DetachRequested = m => requested = m;
vm.DetachCommand.Execute(null);
Assert.Same(vm, requested);
}
[Fact]
public void TaskQuestionAsked_SurfacesQuestion()
{
@@ -68,7 +68,6 @@ public class TasksIslandMoveToListTests : IDisposable
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 ShowDetachedMonitor(TaskMonitorViewModel monitor, Action onClosed) => throw new NotImplementedException();
}
private TasksIslandViewModel BuildViewModel() => new(new TestDbFactory(NewContext), worker: null);