test(worker): adapt planning tests to git-backed worktree flow
Update constructor calls (6-arg), seed AppSettings with sibling strategy, git-init working dirs via GitRepoFixture.InitRepoWithInitialCommit, and replace McpConfigPath assertions with worktree-path / .mcp.json checks. Also fixes PlanningHubTests which had the same 3-arg constructor. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
using ClaudeDo.Data;
|
using ClaudeDo.Data;
|
||||||
|
using ClaudeDo.Data.Git;
|
||||||
using ClaudeDo.Data.Models;
|
using ClaudeDo.Data.Models;
|
||||||
using ClaudeDo.Data.Repositories;
|
using ClaudeDo.Data.Repositories;
|
||||||
|
using ClaudeDo.Worker.Config;
|
||||||
using ClaudeDo.Worker.Hub;
|
using ClaudeDo.Worker.Hub;
|
||||||
using ClaudeDo.Worker.Planning;
|
using ClaudeDo.Worker.Planning;
|
||||||
using ClaudeDo.Worker.Services;
|
using ClaudeDo.Worker.Services;
|
||||||
@@ -28,7 +30,11 @@ public sealed class PlanningHubTests : IDisposable
|
|||||||
_tasks = new TaskRepository(_ctx);
|
_tasks = new TaskRepository(_ctx);
|
||||||
_lists = new ListRepository(_ctx);
|
_lists = new ListRepository(_ctx);
|
||||||
_rootDir = Path.Combine(Path.GetTempPath(), $"cd_hub_planning_{Guid.NewGuid():N}");
|
_rootDir = Path.Combine(Path.GetTempPath(), $"cd_hub_planning_{Guid.NewGuid():N}");
|
||||||
_planning = new PlanningSessionManager(_tasks, _lists, _rootDir);
|
var git = new GitService();
|
||||||
|
var cfg = new WorkerConfig { CentralWorktreeRoot = Path.Combine(_rootDir, "central") };
|
||||||
|
var settingsRepo = new AppSettingsRepository(_ctx);
|
||||||
|
settingsRepo.UpdateAsync(new AppSettingsEntity { WorktreeStrategy = "sibling" }).GetAwaiter().GetResult();
|
||||||
|
_planning = new PlanningSessionManager(_tasks, _lists, settingsRepo, git, cfg, _rootDir);
|
||||||
_launcher = new FakePlanningLauncher();
|
_launcher = new FakePlanningLauncher();
|
||||||
_proxy = new RecordingClientProxy();
|
_proxy = new RecordingClientProxy();
|
||||||
}
|
}
|
||||||
@@ -54,7 +60,7 @@ public sealed class PlanningHubTests : IDisposable
|
|||||||
{
|
{
|
||||||
var listId = Guid.NewGuid().ToString();
|
var listId = Guid.NewGuid().ToString();
|
||||||
var wd = Path.Combine(Path.GetTempPath(), $"cd_wd_{Guid.NewGuid():N}");
|
var wd = Path.Combine(Path.GetTempPath(), $"cd_wd_{Guid.NewGuid():N}");
|
||||||
Directory.CreateDirectory(wd);
|
GitRepoFixture.InitRepoWithInitialCommit(wd);
|
||||||
await _lists.AddAsync(new ListEntity
|
await _lists.AddAsync(new ListEntity
|
||||||
{
|
{
|
||||||
Id = listId, Name = "L", WorkingDir = wd, CreatedAt = DateTime.UtcNow,
|
Id = listId, Name = "L", WorkingDir = wd, CreatedAt = DateTime.UtcNow,
|
||||||
|
|||||||
@@ -78,6 +78,21 @@ public sealed class GitRepoFixture : IDisposable
|
|||||||
return stdout;
|
return stdout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new git repo at <paramref name="dir"/> with a seed commit.
|
||||||
|
/// Used by planning tests that need a real git repo as list working directory.
|
||||||
|
/// </summary>
|
||||||
|
public static void InitRepoWithInitialCommit(string dir)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
RunGit(dir, "init", "-b", "main");
|
||||||
|
RunGit(dir, "config", "user.email", "test@claudedo.local");
|
||||||
|
RunGit(dir, "config", "user.name", "test");
|
||||||
|
File.WriteAllText(Path.Combine(dir, "README.md"), "seed\n");
|
||||||
|
RunGit(dir, "add", "-A");
|
||||||
|
RunGit(dir, "commit", "-m", "chore: seed");
|
||||||
|
}
|
||||||
|
|
||||||
private static void ForceDeleteDirectory(string path)
|
private static void ForceDeleteDirectory(string path)
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(path)) return;
|
if (!Directory.Exists(path)) return;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using ClaudeDo.Data;
|
using ClaudeDo.Data;
|
||||||
|
using ClaudeDo.Data.Git;
|
||||||
using ClaudeDo.Data.Models;
|
using ClaudeDo.Data.Models;
|
||||||
using ClaudeDo.Data.Repositories;
|
using ClaudeDo.Data.Repositories;
|
||||||
|
using ClaudeDo.Worker.Config;
|
||||||
using ClaudeDo.Worker.Hub;
|
using ClaudeDo.Worker.Hub;
|
||||||
using ClaudeDo.Worker.Planning;
|
using ClaudeDo.Worker.Planning;
|
||||||
using ClaudeDo.Worker.Tests.Infrastructure;
|
using ClaudeDo.Worker.Tests.Infrastructure;
|
||||||
@@ -60,7 +62,11 @@ public sealed class PlanningEndToEndTests : IDisposable
|
|||||||
_lists = new ListRepository(_ctx);
|
_lists = new ListRepository(_ctx);
|
||||||
|
|
||||||
var root = Path.Combine(Path.GetTempPath(), $"cd_e2e_{Guid.NewGuid():N}");
|
var root = Path.Combine(Path.GetTempPath(), $"cd_e2e_{Guid.NewGuid():N}");
|
||||||
_manager = new PlanningSessionManager(_tasks, _lists, root);
|
var git = new GitService();
|
||||||
|
var cfg = new WorkerConfig { CentralWorktreeRoot = Path.Combine(root, "central") };
|
||||||
|
var settingsRepo = new AppSettingsRepository(_ctx);
|
||||||
|
settingsRepo.UpdateAsync(new AppSettingsEntity { WorktreeStrategy = "sibling" }).GetAwaiter().GetResult();
|
||||||
|
_manager = new PlanningSessionManager(_tasks, _lists, settingsRepo, git, cfg, root);
|
||||||
|
|
||||||
_httpContext = new DefaultHttpContext();
|
_httpContext = new DefaultHttpContext();
|
||||||
_accessor = new PlanningMcpContextAccessor(new E2EFakeHttpContextAccessor { HttpContext = _httpContext });
|
_accessor = new PlanningMcpContextAccessor(new E2EFakeHttpContextAccessor { HttpContext = _httpContext });
|
||||||
@@ -74,7 +80,8 @@ public sealed class PlanningEndToEndTests : IDisposable
|
|||||||
public async Task StartThenCreateThenFinalize_FullFlow()
|
public async Task StartThenCreateThenFinalize_FullFlow()
|
||||||
{
|
{
|
||||||
var listId = Guid.NewGuid().ToString();
|
var listId = Guid.NewGuid().ToString();
|
||||||
var wd = Path.GetTempPath();
|
var wd = Path.Combine(Path.GetTempPath(), $"cd_e2e_wd_{Guid.NewGuid():N}");
|
||||||
|
GitRepoFixture.InitRepoWithInitialCommit(wd);
|
||||||
await _lists.AddAsync(new ListEntity { Id = listId, Name = "L", WorkingDir = wd, CreatedAt = DateTime.UtcNow });
|
await _lists.AddAsync(new ListEntity { Id = listId, Name = "L", WorkingDir = wd, CreatedAt = DateTime.UtcNow });
|
||||||
|
|
||||||
var parent = new TaskEntity
|
var parent = new TaskEntity
|
||||||
@@ -89,7 +96,7 @@ public sealed class PlanningEndToEndTests : IDisposable
|
|||||||
await _tasks.AddAsync(parent);
|
await _tasks.AddAsync(parent);
|
||||||
|
|
||||||
var startCtx = await _manager.StartAsync(parent.Id, CancellationToken.None);
|
var startCtx = await _manager.StartAsync(parent.Id, CancellationToken.None);
|
||||||
Assert.True(File.Exists(startCtx.Files.McpConfigPath));
|
Assert.True(File.Exists(Path.Combine(startCtx.WorktreePath, ".mcp.json")));
|
||||||
|
|
||||||
// Wire the ambient context so _svc reads the correct parent
|
// Wire the ambient context so _svc reads the correct parent
|
||||||
_httpContext.Items["PlanningContext"] = new PlanningMcpContext { ParentTaskId = parent.Id };
|
_httpContext.Items["PlanningContext"] = new PlanningMcpContext { ParentTaskId = parent.Id };
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using ClaudeDo.Data;
|
using ClaudeDo.Data;
|
||||||
|
using ClaudeDo.Data.Git;
|
||||||
using ClaudeDo.Data.Models;
|
using ClaudeDo.Data.Models;
|
||||||
using ClaudeDo.Data.Repositories;
|
using ClaudeDo.Data.Repositories;
|
||||||
|
using ClaudeDo.Worker.Config;
|
||||||
using ClaudeDo.Worker.Planning;
|
using ClaudeDo.Worker.Planning;
|
||||||
using ClaudeDo.Worker.Tests.Infrastructure;
|
using ClaudeDo.Worker.Tests.Infrastructure;
|
||||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||||
@@ -14,6 +16,9 @@ public sealed class PlanningSessionManagerTests : IDisposable
|
|||||||
private readonly TaskRepository _tasks;
|
private readonly TaskRepository _tasks;
|
||||||
private readonly ListRepository _lists;
|
private readonly ListRepository _lists;
|
||||||
private readonly string _rootDir;
|
private readonly string _rootDir;
|
||||||
|
private readonly GitService _git;
|
||||||
|
private readonly WorkerConfig _cfg;
|
||||||
|
private readonly AppSettingsRepository _settingsRepo;
|
||||||
private readonly PlanningSessionManager _sut;
|
private readonly PlanningSessionManager _sut;
|
||||||
|
|
||||||
public PlanningSessionManagerTests()
|
public PlanningSessionManagerTests()
|
||||||
@@ -22,7 +27,11 @@ public sealed class PlanningSessionManagerTests : IDisposable
|
|||||||
_tasks = new TaskRepository(_ctx);
|
_tasks = new TaskRepository(_ctx);
|
||||||
_lists = new ListRepository(_ctx);
|
_lists = new ListRepository(_ctx);
|
||||||
_rootDir = Path.Combine(Path.GetTempPath(), $"cd_planning_{Guid.NewGuid():N}");
|
_rootDir = Path.Combine(Path.GetTempPath(), $"cd_planning_{Guid.NewGuid():N}");
|
||||||
_sut = new PlanningSessionManager(_tasks, _lists, _rootDir);
|
_git = new GitService();
|
||||||
|
_cfg = new WorkerConfig { CentralWorktreeRoot = Path.Combine(_rootDir, "central") };
|
||||||
|
_settingsRepo = new AppSettingsRepository(_ctx);
|
||||||
|
_settingsRepo.UpdateAsync(new AppSettingsEntity { WorktreeStrategy = "sibling" }).GetAwaiter().GetResult();
|
||||||
|
_sut = new PlanningSessionManager(_tasks, _lists, _settingsRepo, _git, _cfg, _rootDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -36,7 +45,7 @@ public sealed class PlanningSessionManagerTests : IDisposable
|
|||||||
{
|
{
|
||||||
var listId = Guid.NewGuid().ToString();
|
var listId = Guid.NewGuid().ToString();
|
||||||
var wd = Path.Combine(Path.GetTempPath(), $"cd_wd_{Guid.NewGuid():N}");
|
var wd = Path.Combine(Path.GetTempPath(), $"cd_wd_{Guid.NewGuid():N}");
|
||||||
Directory.CreateDirectory(wd);
|
GitRepoFixture.InitRepoWithInitialCommit(wd);
|
||||||
await _lists.AddAsync(new ListEntity
|
await _lists.AddAsync(new ListEntity
|
||||||
{
|
{
|
||||||
Id = listId,
|
Id = listId,
|
||||||
@@ -72,14 +81,17 @@ public sealed class PlanningSessionManagerTests : IDisposable
|
|||||||
var ctx = await _sut.StartAsync(parent.Id, CancellationToken.None);
|
var ctx = await _sut.StartAsync(parent.Id, CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal(parent.Id, ctx.ParentTaskId);
|
Assert.Equal(parent.Id, ctx.ParentTaskId);
|
||||||
Assert.Equal(wd, ctx.WorkingDir);
|
Assert.Equal(ctx.WorktreePath, ctx.WorkingDir);
|
||||||
Assert.True(File.Exists(ctx.Files.McpConfigPath));
|
Assert.True(Directory.Exists(ctx.WorktreePath));
|
||||||
|
var mcpPath = Path.Combine(ctx.WorktreePath, ".mcp.json");
|
||||||
|
Assert.True(File.Exists(mcpPath));
|
||||||
|
Assert.True(File.Exists(Path.Combine(ctx.WorktreePath, ".claude", "settings.local.json")));
|
||||||
Assert.True(File.Exists(ctx.Files.SystemPromptPath));
|
Assert.True(File.Exists(ctx.Files.SystemPromptPath));
|
||||||
Assert.True(File.Exists(ctx.Files.InitialPromptPath));
|
Assert.True(File.Exists(ctx.Files.InitialPromptPath));
|
||||||
|
|
||||||
var mcp = await File.ReadAllTextAsync(ctx.Files.McpConfigPath);
|
var mcp = await File.ReadAllTextAsync(mcpPath);
|
||||||
Assert.Contains("\"type\": \"http\"", mcp);
|
Assert.Contains("${CLAUDEDO_PLANNING_TOKEN}", mcp);
|
||||||
Assert.Contains("Bearer ", mcp);
|
Assert.DoesNotContain(ctx.Token, mcp);
|
||||||
|
|
||||||
var initial = await File.ReadAllTextAsync(ctx.Files.InitialPromptPath);
|
var initial = await File.ReadAllTextAsync(ctx.Files.InitialPromptPath);
|
||||||
Assert.Contains("Brainstorm auth", initial);
|
Assert.Contains("Brainstorm auth", initial);
|
||||||
@@ -132,10 +144,10 @@ public sealed class PlanningSessionManagerTests : IDisposable
|
|||||||
var resumeCtx = await _sut.ResumeAsync(parent.Id, CancellationToken.None);
|
var resumeCtx = await _sut.ResumeAsync(parent.Id, CancellationToken.None);
|
||||||
|
|
||||||
Assert.Equal(parent.Id, resumeCtx.ParentTaskId);
|
Assert.Equal(parent.Id, resumeCtx.ParentTaskId);
|
||||||
Assert.Equal(wd, resumeCtx.WorkingDir);
|
Assert.Equal(resumeCtx.WorktreePath, resumeCtx.WorkingDir);
|
||||||
Assert.Equal("claude-session-42", resumeCtx.ClaudeSessionId);
|
Assert.Equal("claude-session-42", resumeCtx.ClaudeSessionId);
|
||||||
Assert.Equal(startCtx.Files.McpConfigPath, resumeCtx.McpConfigPath);
|
Assert.True(Directory.Exists(resumeCtx.WorktreePath));
|
||||||
Assert.True(File.Exists(resumeCtx.McpConfigPath));
|
Assert.True(File.Exists(Path.Combine(resumeCtx.WorktreePath, ".mcp.json")));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -12,9 +12,11 @@ public sealed class WindowsTerminalPlanningLauncherTests
|
|||||||
return new PlanningSessionStartContext(
|
return new PlanningSessionStartContext(
|
||||||
ParentTaskId: "task-1",
|
ParentTaskId: "task-1",
|
||||||
WorkingDir: workingDir,
|
WorkingDir: workingDir,
|
||||||
|
Token: "test-token",
|
||||||
|
WorktreePath: workingDir,
|
||||||
|
BranchName: "claudedo/planning/task1",
|
||||||
Files: new PlanningSessionFiles(
|
Files: new PlanningSessionFiles(
|
||||||
SessionDirectory: dir,
|
SessionDirectory: dir,
|
||||||
McpConfigPath: Path.Combine(dir, "mcp.json"),
|
|
||||||
SystemPromptPath: Path.Combine(dir, "system-prompt.md"),
|
SystemPromptPath: Path.Combine(dir, "system-prompt.md"),
|
||||||
InitialPromptPath: Path.Combine(dir, "initial-prompt.txt")));
|
InitialPromptPath: Path.Combine(dir, "initial-prompt.txt")));
|
||||||
}
|
}
|
||||||
@@ -33,7 +35,6 @@ public sealed class WindowsTerminalPlanningLauncherTests
|
|||||||
public async Task LaunchStartAsync_WtMissing_Throws()
|
public async Task LaunchStartAsync_WtMissing_Throws()
|
||||||
{
|
{
|
||||||
var ctx = MakeStartCtx();
|
var ctx = MakeStartCtx();
|
||||||
File.WriteAllText(ctx.Files.McpConfigPath, "{}");
|
|
||||||
File.WriteAllText(ctx.Files.SystemPromptPath, "sp");
|
File.WriteAllText(ctx.Files.SystemPromptPath, "sp");
|
||||||
File.WriteAllText(ctx.Files.InitialPromptPath, "ip");
|
File.WriteAllText(ctx.Files.InitialPromptPath, "ip");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user