Merge branch 'claudedo/df0e925e1ff4419d8b7a45f8a184f6d0'
This commit is contained in:
@@ -94,7 +94,6 @@ public abstract class StubWorkerClient : IWorkerClient
|
||||
public virtual Task<MergeResultDto> ContinueConflictMergeAsync(string taskId) => Task.FromResult(new MergeResultDto("merged", System.Array.Empty<string>(), null));
|
||||
public virtual Task AbortConflictMergeAsync(string taskId) => Task.CompletedTask;
|
||||
public virtual Task StartPlanningSessionAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask;
|
||||
public virtual Task ResumeTaskInTerminalAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask;
|
||||
public virtual Task SubmitTaskForReviewAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask;
|
||||
public virtual Task<LaunchSpec> GetInteractiveLaunchSpecAsync(string taskId, CancellationToken ct = default)
|
||||
=> Task.FromResult(new LaunchSpec(".", "claude", Array.Empty<string>(), new Dictionary<string, string>()));
|
||||
|
||||
@@ -85,88 +85,6 @@ public sealed class PlanningHubTests : IDisposable
|
||||
return (listId, task.Id);
|
||||
}
|
||||
|
||||
private async Task SeedRunAsync(string taskId, string? sessionId)
|
||||
{
|
||||
await new TaskRunRepository(_ctx).AddAsync(new TaskRunEntity
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
TaskId = taskId,
|
||||
RunNumber = 1,
|
||||
IsRetry = false,
|
||||
Prompt = "p",
|
||||
SessionId = sessionId,
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<string> SeedWorktreeAsync(string taskId, WorktreeState state)
|
||||
{
|
||||
var path = Path.Combine(_rootDir, $"wt_{Guid.NewGuid():N}");
|
||||
Directory.CreateDirectory(path);
|
||||
await new WorktreeRepository(_ctx).AddAsync(new WorktreeEntity
|
||||
{
|
||||
TaskId = taskId,
|
||||
Path = path,
|
||||
BranchName = "claudedo/x",
|
||||
BaseCommit = "base",
|
||||
State = state,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
});
|
||||
return path;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResumeTaskInTerminal_LaunchesWithWorktreePathAndSessionId()
|
||||
{
|
||||
var (_, taskId) = await SeedAsync();
|
||||
await SeedRunAsync(taskId, "sess-99");
|
||||
var wtPath = await SeedWorktreeAsync(taskId, WorktreeState.Active);
|
||||
var hub = CreateHub();
|
||||
|
||||
await hub.ResumeTaskInTerminal(taskId);
|
||||
|
||||
Assert.Equal(1, _launcher.LaunchTerminalResumeCalls);
|
||||
Assert.Equal(wtPath, _launcher.LastResumeWorkingDir);
|
||||
Assert.Equal("sess-99", _launcher.LastResumeSessionId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResumeTaskInTerminal_NoSession_Throws()
|
||||
{
|
||||
var (_, taskId) = await SeedAsync();
|
||||
await SeedRunAsync(taskId, sessionId: null);
|
||||
await SeedWorktreeAsync(taskId, WorktreeState.Active);
|
||||
var hub = CreateHub();
|
||||
|
||||
await Assert.ThrowsAsync<HubException>(() => hub.ResumeTaskInTerminal(taskId));
|
||||
Assert.Equal(0, _launcher.LaunchTerminalResumeCalls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResumeTaskInTerminal_RunningTask_Throws()
|
||||
{
|
||||
var (_, taskId) = await SeedAsync();
|
||||
await SeedRunAsync(taskId, "sess-1");
|
||||
await SeedWorktreeAsync(taskId, WorktreeState.Active);
|
||||
var task = await _tasks.GetByIdAsync(taskId);
|
||||
task!.Status = TaskStatus.Running;
|
||||
await _tasks.UpdateAsync(task);
|
||||
var hub = CreateHub();
|
||||
|
||||
await Assert.ThrowsAsync<HubException>(() => hub.ResumeTaskInTerminal(taskId));
|
||||
Assert.Equal(0, _launcher.LaunchTerminalResumeCalls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResumeTaskInTerminal_NoWorktree_Throws()
|
||||
{
|
||||
var (_, taskId) = await SeedAsync();
|
||||
await SeedRunAsync(taskId, "sess-1");
|
||||
var hub = CreateHub();
|
||||
|
||||
await Assert.ThrowsAsync<HubException>(() => hub.ResumeTaskInTerminal(taskId));
|
||||
Assert.Equal(0, _launcher.LaunchTerminalResumeCalls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task StartPlanningSessionAsync_ChangesStatusToPlanning_AndInvokesLauncher()
|
||||
{
|
||||
@@ -274,19 +192,6 @@ internal sealed class FakeTerminalLauncher : ITerminalLauncher
|
||||
LaunchResumeCalls++;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public int LaunchTerminalResumeCalls { get; private set; }
|
||||
public string? LastResumeWorkingDir { get; private set; }
|
||||
public string? LastResumeSessionId { get; private set; }
|
||||
|
||||
public Task LaunchResumeAsync(string workingDir, string claudeSessionId, CancellationToken cancellationToken)
|
||||
{
|
||||
if (ShouldThrow) throw new TerminalLaunchException("fake launch failure");
|
||||
LaunchTerminalResumeCalls++;
|
||||
LastResumeWorkingDir = workingDir;
|
||||
LastResumeSessionId = claudeSessionId;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class RecordingClientProxy : IClientProxy
|
||||
|
||||
@@ -104,25 +104,9 @@ public sealed class WindowsTerminalLauncherTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildResumeCommand_ResumesSessionSingleQuoted()
|
||||
public void BuildResumeArgs_ResumesSession()
|
||||
{
|
||||
var command = WindowsTerminalLauncher.BuildResumeCommand("claude.exe", "sess-42");
|
||||
|
||||
Assert.Contains("--resume", command);
|
||||
// The session id is single-quoted so ';' and friends pass through untouched.
|
||||
Assert.Contains("'--resume' 'sess-42'", command);
|
||||
// A plain resume drives the real interactive TUI — no planning-only flags.
|
||||
Assert.DoesNotContain("--permission-mode", command);
|
||||
Assert.DoesNotContain("--append-system-prompt-file", command);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LaunchResumeAsync_WorkingDirMissing_Throws()
|
||||
{
|
||||
var sut = new WindowsTerminalLauncher(wtPath: "wt", claudePath: "claude");
|
||||
var missing = Path.Combine(Path.GetTempPath(), "nonexistent_" + Guid.NewGuid());
|
||||
var ex = await Assert.ThrowsAsync<TerminalLaunchException>(() =>
|
||||
sut.LaunchResumeAsync(missing, "sess-1", CancellationToken.None));
|
||||
Assert.Contains("Working directory", ex.Message);
|
||||
var args = WindowsTerminalLauncher.BuildResumeArgs("sess-42");
|
||||
Assert.Equal(new[] { "--resume", "sess-42" }, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||
|
||||
namespace ClaudeDo.Worker.Tests.Runner;
|
||||
|
||||
/// Verifies InteractiveLaunchSpecService's guards mirror WorkerHub.ResumeTaskInTerminal
|
||||
/// Verifies InteractiveLaunchSpecService's guards
|
||||
/// (Running/Queued rejected, missing/invalid worktree rejected) and that it reuses the
|
||||
/// autonomous-run worktree prep: session-skills seeding via ISessionSkillSeeder, and
|
||||
/// produces --resume for a resumable task vs. a fresh-start spec for a never-run task.
|
||||
|
||||
@@ -69,9 +69,6 @@ sealed class FakeWorkerClient : IWorkerClient
|
||||
public Task CancelReviewAsync(string taskId) => Task.CompletedTask;
|
||||
public Task WakeQueueAsync() { WakeQueueCalls++; return Task.CompletedTask; }
|
||||
public Task StartPlanningSessionAsync(string taskId, CancellationToken ct = default) { StartPlanningCalls++; return Task.CompletedTask; }
|
||||
public int PickUpInTerminalCalls { get; private set; }
|
||||
public string? LastPickUpTaskId { get; private set; }
|
||||
public Task ResumeTaskInTerminalAsync(string taskId, CancellationToken ct = default) { PickUpInTerminalCalls++; LastPickUpTaskId = taskId; return Task.CompletedTask; }
|
||||
public Task SubmitTaskForReviewAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask;
|
||||
public Task<LaunchSpec> GetInteractiveLaunchSpecAsync(string taskId, CancellationToken ct = default)
|
||||
=> Task.FromResult(new LaunchSpec(".", "claude", Array.Empty<string>(), new Dictionary<string, string>()));
|
||||
@@ -191,28 +188,6 @@ public class TasksIslandViewModelPlanningTests
|
||||
PlanningPhase phase = PlanningPhase.None)
|
||||
=> new TaskRowViewModel { Id = id, Status = status, ParentTaskId = parentId, PlanningPhase = phase };
|
||||
|
||||
[Fact]
|
||||
public async Task PickUpInTerminal_InvokesWorkerWithTaskId()
|
||||
{
|
||||
var row = MakeRow("t1", TaskStatus.WaitingForReview);
|
||||
var (vm, worker) = VmFactory.Create([row]);
|
||||
|
||||
await vm.PickUpInTerminalCommand.ExecuteAsync(row);
|
||||
|
||||
Assert.Equal(1, worker.PickUpInTerminalCalls);
|
||||
Assert.Equal("t1", worker.LastPickUpTaskId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanPickUpInTerminal_OnlyForReviewOrFailed()
|
||||
{
|
||||
Assert.True(MakeRow("a", TaskStatus.WaitingForReview).CanPickUpInTerminal);
|
||||
Assert.True(MakeRow("b", TaskStatus.Failed).CanPickUpInTerminal);
|
||||
Assert.False(MakeRow("c", TaskStatus.Idle).CanPickUpInTerminal);
|
||||
Assert.False(MakeRow("d", TaskStatus.Running).CanPickUpInTerminal);
|
||||
Assert.False(MakeRow("e", TaskStatus.Done).CanPickUpInTerminal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PlanningParentWithChildren_CollapsedByDefault_ToggleExpands()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user