91 lines
3.0 KiB
C#
91 lines
3.0 KiB
C#
using ClaudeDo.Data;
|
|
using ClaudeDo.Data.Models;
|
|
using ClaudeDo.Data.Repositories;
|
|
using ClaudeDo.Worker.External;
|
|
using ClaudeDo.Worker.Hub;
|
|
using ClaudeDo.Worker.Lifecycle;
|
|
using ClaudeDo.Worker.Runner;
|
|
using ClaudeDo.Worker.State;
|
|
using ClaudeDo.Worker.Tests.Infrastructure;
|
|
using ClaudeDo.Data.Git;
|
|
using ClaudeDo.Worker.Config;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
|
|
|
namespace ClaudeDo.Worker.Tests.External;
|
|
|
|
public sealed class LifecycleMcpToolsTests : IDisposable
|
|
{
|
|
private readonly DbFixture _db = new();
|
|
private readonly ClaudeDoDbContext _ctx;
|
|
private readonly TaskRepository _tasks;
|
|
private readonly ListRepository _lists;
|
|
|
|
public LifecycleMcpToolsTests()
|
|
{
|
|
_ctx = _db.CreateContext();
|
|
_tasks = new TaskRepository(_ctx);
|
|
_lists = new ListRepository(_ctx);
|
|
}
|
|
|
|
public void Dispose() { _ctx.Dispose(); _db.Dispose(); }
|
|
|
|
private LifecycleMcpTools BuildSut()
|
|
{
|
|
var cfg = new WorkerConfig
|
|
{
|
|
SandboxRoot = Path.Combine(Path.GetTempPath(), $"cd_{Guid.NewGuid():N}"),
|
|
LogRoot = Path.Combine(Path.GetTempPath(), $"cdl_{Guid.NewGuid():N}"),
|
|
};
|
|
var dbFactory = _db.CreateFactory();
|
|
var broadcaster = new HubBroadcaster(new CapturingHubContext());
|
|
var wtManager = new WorktreeManager(new GitService(), dbFactory, cfg, NullLogger<WorktreeManager>.Instance);
|
|
var state = TaskStateServiceBuilder.Build(dbFactory).State;
|
|
var reset = new TaskResetService(dbFactory, wtManager, broadcaster, state, NullLogger<TaskResetService>.Instance);
|
|
return new LifecycleMcpTools(_tasks, reset);
|
|
}
|
|
|
|
private async Task<TaskEntity> SeedTaskAsync(TaskStatus status)
|
|
{
|
|
var listId = Guid.NewGuid().ToString();
|
|
await _lists.AddAsync(new ListEntity { Id = listId, Name = "L", CreatedAt = DateTime.UtcNow });
|
|
var task = new TaskEntity
|
|
{
|
|
Id = Guid.NewGuid().ToString(), ListId = listId, Title = "t",
|
|
Status = status, CreatedAt = DateTime.UtcNow, CommitType = "chore",
|
|
};
|
|
await _tasks.AddAsync(task);
|
|
return task;
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ResetFailedTask_OnFailed_ResetsToIdle()
|
|
{
|
|
var task = await SeedTaskAsync(TaskStatus.Failed);
|
|
var sut = BuildSut();
|
|
|
|
await sut.ResetFailedTask(task.Id, CancellationToken.None);
|
|
|
|
var loaded = await _tasks.GetByIdAsync(task.Id);
|
|
Assert.Equal(TaskStatus.Idle, loaded!.Status);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ResetFailedTask_OnNonFailed_Throws()
|
|
{
|
|
var task = await SeedTaskAsync(TaskStatus.Done);
|
|
var sut = BuildSut();
|
|
|
|
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
|
sut.ResetFailedTask(task.Id, CancellationToken.None));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ResetFailedTask_NotFound_Throws()
|
|
{
|
|
var sut = BuildSut();
|
|
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
|
sut.ResetFailedTask("missing", CancellationToken.None));
|
|
}
|
|
}
|