diff --git a/tests/ClaudeDo.Data.Tests/ExpectsNoDiffTests.cs b/tests/ClaudeDo.Data.Tests/ExpectsNoDiffTests.cs new file mode 100644 index 00000000..a9ba8171 --- /dev/null +++ b/tests/ClaudeDo.Data.Tests/ExpectsNoDiffTests.cs @@ -0,0 +1,58 @@ +using ClaudeDo.Data; +using ClaudeDo.Data.Models; +using Microsoft.EntityFrameworkCore; +using TaskStatus = ClaudeDo.Data.Models.TaskStatus; + +namespace ClaudeDo.Data.Tests; + +public sealed class ExpectsNoDiffTests : IDisposable +{ + private readonly string _dbPath; + private readonly ClaudeDoDbContext _ctx; + + private const string ListId = "l1"; + + public ExpectsNoDiffTests() + { + _dbPath = Path.Combine(Path.GetTempPath(), $"claudedo_endiff_{Guid.NewGuid():N}.db"); + var options = new DbContextOptionsBuilder() + .UseSqlite($"Data Source={_dbPath}") + .Options; + _ctx = new ClaudeDoDbContext(options); + _ctx.Database.EnsureCreated(); + + _ctx.Lists.Add(new ListEntity { Id = ListId, Name = "Test", CreatedAt = DateTime.UtcNow }); + _ctx.SaveChanges(); + } + + public void Dispose() + { + _ctx.Dispose(); + foreach (var suffix in new[] { "", "-wal", "-shm" }) + try { File.Delete(_dbPath + suffix); } catch { } + } + + [Fact] + public void DefaultsToFalse_WhenNotSet() + { + var task = new TaskEntity { Id = "t1", ListId = ListId, Title = "T", Status = TaskStatus.Idle, CreatedAt = DateTime.UtcNow }; + _ctx.Tasks.Add(task); + _ctx.SaveChanges(); + + using var reload = new ClaudeDoDbContext(new DbContextOptionsBuilder().UseSqlite(_ctx.Database.GetConnectionString()).Options); + var reloaded = reload.Tasks.Single(t => t.Id == "t1"); + Assert.False(reloaded.ExpectsNoDiff); + } + + [Fact] + public void PersistsTrue_Roundtrip() + { + var task = new TaskEntity { Id = "t2", ListId = ListId, Title = "T", Status = TaskStatus.Idle, CreatedAt = DateTime.UtcNow, ExpectsNoDiff = true }; + _ctx.Tasks.Add(task); + _ctx.SaveChanges(); + + using var reload = new ClaudeDoDbContext(new DbContextOptionsBuilder().UseSqlite(_ctx.Database.GetConnectionString()).Options); + var reloaded = reload.Tasks.Single(t => t.Id == "t2"); + Assert.True(reloaded.ExpectsNoDiff); + } +} diff --git a/tests/ClaudeDo.Worker.Tests/External/ExternalMcpServiceTests.cs b/tests/ClaudeDo.Worker.Tests/External/ExternalMcpServiceTests.cs index 6e13f993..fd04cf3d 100644 --- a/tests/ClaudeDo.Worker.Tests/External/ExternalMcpServiceTests.cs +++ b/tests/ClaudeDo.Worker.Tests/External/ExternalMcpServiceTests.cs @@ -272,6 +272,50 @@ public sealed class ExternalMcpServiceTests : IDisposable Assert.True(dto.IsManual); } + [Fact] + public async Task UpdateTask_SetsExpectsNoDiffTrue() + { + var listId = await SeedListAsync(); + var task = await SeedTaskAsync(listId); + var sut = BuildSut(CreateQueue()); + + var dto = await sut.UpdateTask(task.Id, expectsNoDiff: true, cancellationToken: CancellationToken.None); + + Assert.True(dto.ExpectsNoDiff); + var loaded = await _tasks.GetByIdAsync(task.Id); + Assert.True(loaded!.ExpectsNoDiff); + } + + [Fact] + public async Task UpdateTask_SetsExpectsNoDiffFalse_ClearsExistingFlag() + { + var listId = await SeedListAsync(); + var task = await SeedTaskAsync(listId); + task.ExpectsNoDiff = true; + await _tasks.UpdateAsync(task, CancellationToken.None); + var sut = BuildSut(CreateQueue()); + + var dto = await sut.UpdateTask(task.Id, expectsNoDiff: false, cancellationToken: CancellationToken.None); + + Assert.False(dto.ExpectsNoDiff); + var loaded = await _tasks.GetByIdAsync(task.Id); + Assert.False(loaded!.ExpectsNoDiff); + } + + [Fact] + public async Task UpdateTask_ExpectsNoDiffNull_LeavesUnchanged() + { + var listId = await SeedListAsync(); + var task = await SeedTaskAsync(listId); + task.ExpectsNoDiff = true; + await _tasks.UpdateAsync(task, CancellationToken.None); + var sut = BuildSut(CreateQueue()); + + var dto = await sut.UpdateTask(task.Id, title: "new title", cancellationToken: CancellationToken.None); + + Assert.True(dto.ExpectsNoDiff); + } + [Fact] public async Task GetTask_ReturnsTaskNumber() { @@ -2653,6 +2697,22 @@ public sealed class ExternalMcpServiceTests : IDisposable Assert.Empty(result.Overlaps); } + [Fact] + public async Task PreviewMergeSet_ReportsExpectsNoDiffPerTask() + { + if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; } + + var (task, list, _) = await SeedWorktreeAsync(TaskStatus.WaitingForReview); + task.ExpectsNoDiff = true; + await _tasks.UpdateAsync(task, CancellationToken.None); + var target = await new GitService().GetCurrentBranchAsync(list.WorkingDir!, CancellationToken.None); + var sut = BuildSut(CreateQueue()); + + var result = await sut.PreviewMergeSet([task.Id], target, cancellationToken: CancellationToken.None); + + Assert.True(Assert.Single(result.Tasks).ExpectsNoDiff); + } + [Fact] public async Task PreviewMergeSet_ReportsProgressPerTask() { @@ -2801,6 +2861,32 @@ public sealed class ExternalMcpServiceTests : IDisposable Assert.Equal(loaded!.Number, dto.Task.Number); } + [Fact] + public async Task AddTask_DefaultExpectsNoDiff_IsFalse() + { + var listId = await SeedListAsync(); + var sut = NewService(); + + var dto = await sut.AddTask(listId, "t", cancellationToken: CancellationToken.None); + + Assert.False(dto.Task.ExpectsNoDiff); + var loaded = await _tasks.GetByIdAsync(dto.Task.Id); + Assert.False(loaded!.ExpectsNoDiff); + } + + [Fact] + public async Task AddTask_ExpectsNoDiffTrue_PersistsFlag() + { + var listId = await SeedListAsync(); + var sut = NewService(); + + var dto = await sut.AddTask(listId, "t", expectsNoDiff: true, cancellationToken: CancellationToken.None); + + Assert.True(dto.Task.ExpectsNoDiff); + var loaded = await _tasks.GetByIdAsync(dto.Task.Id); + Assert.True(loaded!.ExpectsNoDiff); + } + // ── AddTask model override ──────────────────────────────────────────────── [Fact]