Merge branch 'claudedo/9f963a215bb34ca4a28050d4f39178d3'

This commit is contained in:
mika kuns
2026-08-10 15:19:57 +02:00
23 changed files with 481 additions and 32 deletions
@@ -1,10 +1,12 @@
using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.Git;
using ClaudeDo.Worker.Planning;
using ClaudeDo.Worker.State;
using ClaudeDo.Worker.Tests.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging.Abstractions;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
namespace ClaudeDo.Worker.Tests.State;
@@ -22,6 +24,9 @@ public sealed class TaskStateServiceTests : IDisposable
private readonly TaskStateServiceBuilder.Built _built;
private readonly ITaskStateService _sut;
private readonly string _listId;
private readonly List<GitRepoFixture> _repos = new();
private static bool GitAvailable => GitRepoFixture.IsGitAvailable();
public TaskStateServiceTests()
{
@@ -41,7 +46,19 @@ public sealed class TaskStateServiceTests : IDisposable
ctx.SaveChanges();
}
public void Dispose() => _db.Dispose();
public void Dispose()
{
foreach (var r in _repos) r.Dispose();
_db.Dispose();
}
private async Task SetListWorkingDirAsync(string workingDir)
{
await using var ctx = _factory.CreateDbContext();
var list = await ctx.Lists.FirstAsync(l => l.Id == _listId);
list.WorkingDir = workingDir;
await ctx.SaveChangesAsync();
}
private async Task<string> SeedTaskAsync(
TaskStatus status,
@@ -98,6 +115,74 @@ public sealed class TaskStateServiceTests : IDisposable
Assert.Contains(_built.Hub.Proxy.Calls, c => c.Method == "TaskUpdated");
}
[Fact]
public async Task EnqueueAsync_NoWorkingDir_QueuesNormally_NoBaseDirtyWarning()
{
// The list created in the constructor has no WorkingDir -- covers the
// "no valid repo" acceptance case: queuing must not throw or block.
var id = await SeedTaskAsync(TaskStatus.Idle);
var result = await _sut.EnqueueAsync(id, default);
Assert.True(result.Ok);
Assert.Null(result.BaseDirty);
Assert.Equal(TaskStatus.Queued, await GetStatusAsync(id));
}
[Fact]
public async Task EnqueueAsync_CleanBaseRepo_NoBaseDirtyWarning()
{
if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; }
var repo = new GitRepoFixture();
_repos.Add(repo);
await SetListWorkingDirAsync(repo.RepoDir);
var id = await SeedTaskAsync(TaskStatus.Idle);
var result = await _sut.EnqueueAsync(id, default);
Assert.True(result.Ok);
Assert.Null(result.BaseDirty);
}
[Fact]
public async Task EnqueueAsync_UntrackedFileInBaseRepo_ReturnsBaseDirtyWarning()
{
if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; }
var repo = new GitRepoFixture();
_repos.Add(repo);
File.WriteAllText(Path.Combine(repo.RepoDir, "new-file.txt"), "not committed");
await SetListWorkingDirAsync(repo.RepoDir);
var id = await SeedTaskAsync(TaskStatus.Idle);
var result = await _sut.EnqueueAsync(id, default);
Assert.True(result.Ok);
Assert.NotNull(result.BaseDirty);
Assert.Equal(0, result.BaseDirty!.ModifiedCount);
Assert.Equal(1, result.BaseDirty!.UntrackedCount);
}
[Fact]
public async Task EnqueueAsync_ModifiedFileInBaseRepo_ReturnsBaseDirtyWarning()
{
if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; }
var repo = new GitRepoFixture();
_repos.Add(repo);
File.WriteAllText(Path.Combine(repo.RepoDir, "README.md"), "changed on disk");
await SetListWorkingDirAsync(repo.RepoDir);
var id = await SeedTaskAsync(TaskStatus.Idle);
var result = await _sut.EnqueueAsync(id, default);
Assert.True(result.Ok);
Assert.NotNull(result.BaseDirty);
Assert.Equal(1, result.BaseDirty!.ModifiedCount);
Assert.Equal(0, result.BaseDirty!.UntrackedCount);
}
[Fact]
public async Task EnqueueAsync_ManualTask_Rejected_AndStaysIdle()
{