Merge branch 'claudedo/9f963a215bb34ca4a28050d4f39178d3'
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
using ClaudeDo.Data.Git;
|
||||
using ClaudeDo.Worker.Git;
|
||||
using ClaudeDo.Worker.Tests.Infrastructure;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
|
||||
namespace ClaudeDo.Worker.Tests.Git;
|
||||
|
||||
public sealed class BaseDirtyCheckerTests : IDisposable
|
||||
{
|
||||
private readonly List<GitRepoFixture> _repos = new();
|
||||
|
||||
private static bool GitAvailable => GitRepoFixture.IsGitAvailable();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var r in _repos) r.Dispose();
|
||||
}
|
||||
|
||||
private GitRepoFixture CreateRepo()
|
||||
{
|
||||
var repo = new GitRepoFixture();
|
||||
_repos.Add(repo);
|
||||
return repo;
|
||||
}
|
||||
|
||||
private static BaseDirtyChecker CreateSut() =>
|
||||
new(new GitService(), NullLogger<BaseDirtyChecker>.Instance);
|
||||
|
||||
[Fact]
|
||||
public async Task CheckAsync_NullWorkingDir_ReturnsNull()
|
||||
{
|
||||
var sut = CreateSut();
|
||||
Assert.Null(await sut.CheckAsync(null, default));
|
||||
Assert.Null(await sut.CheckAsync("", default));
|
||||
Assert.Null(await sut.CheckAsync(" ", default));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckAsync_NotAGitRepo_ReturnsNull()
|
||||
{
|
||||
var dir = Path.Combine(Path.GetTempPath(), $"claudedo_notgit_{Guid.NewGuid():N}");
|
||||
Directory.CreateDirectory(dir);
|
||||
try
|
||||
{
|
||||
var sut = CreateSut();
|
||||
Assert.Null(await sut.CheckAsync(dir, default));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.Delete(dir, true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckAsync_CleanRepo_ReturnsNull()
|
||||
{
|
||||
if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; }
|
||||
|
||||
var repo = CreateRepo();
|
||||
var sut = CreateSut();
|
||||
|
||||
Assert.Null(await sut.CheckAsync(repo.RepoDir, default));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckAsync_UntrackedFile_ReturnsUntrackedCountOnly()
|
||||
{
|
||||
if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; }
|
||||
|
||||
var repo = CreateRepo();
|
||||
File.WriteAllText(Path.Combine(repo.RepoDir, "scratch.txt"), "new");
|
||||
var sut = CreateSut();
|
||||
|
||||
var warning = await sut.CheckAsync(repo.RepoDir, default);
|
||||
|
||||
Assert.NotNull(warning);
|
||||
Assert.Equal(0, warning!.ModifiedCount);
|
||||
Assert.Equal(1, warning.UntrackedCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckAsync_ModifiedTrackedFile_ReturnsModifiedCountOnly()
|
||||
{
|
||||
if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; }
|
||||
|
||||
var repo = CreateRepo();
|
||||
File.WriteAllText(Path.Combine(repo.RepoDir, "README.md"), "edited");
|
||||
var sut = CreateSut();
|
||||
|
||||
var warning = await sut.CheckAsync(repo.RepoDir, default);
|
||||
|
||||
Assert.NotNull(warning);
|
||||
Assert.Equal(1, warning!.ModifiedCount);
|
||||
Assert.Equal(0, warning.UntrackedCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckAsync_ModifiedAndUntracked_ReportsBothSeparately()
|
||||
{
|
||||
if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; }
|
||||
|
||||
var repo = CreateRepo();
|
||||
File.WriteAllText(Path.Combine(repo.RepoDir, "README.md"), "edited");
|
||||
File.WriteAllText(Path.Combine(repo.RepoDir, "scratch.txt"), "new");
|
||||
var sut = CreateSut();
|
||||
|
||||
var warning = await sut.CheckAsync(repo.RepoDir, default);
|
||||
|
||||
Assert.NotNull(warning);
|
||||
Assert.Equal(1, warning!.ModifiedCount);
|
||||
Assert.Equal(1, warning.UntrackedCount);
|
||||
}
|
||||
|
||||
// Proves the TTL cache collapses repeated checks against the same working dir into a
|
||||
// single underlying `git status` call -- the acceptance requirement that a batch queue
|
||||
// op over many tasks in one list must not shell out once per task. A second check
|
||||
// immediately after a repo mutation still returns the first (stale) answer instead of
|
||||
// re-running git.
|
||||
[Fact]
|
||||
public async Task CheckAsync_RepeatedCallsWithinTtl_ReuseCachedResult()
|
||||
{
|
||||
if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; }
|
||||
|
||||
var repo = CreateRepo();
|
||||
var sut = CreateSut();
|
||||
|
||||
var first = await sut.CheckAsync(repo.RepoDir, default);
|
||||
Assert.Null(first);
|
||||
|
||||
File.WriteAllText(Path.Combine(repo.RepoDir, "scratch.txt"), "new");
|
||||
var second = await sut.CheckAsync(repo.RepoDir, default);
|
||||
|
||||
Assert.Null(second);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user