Files
ClaudeDo/tests/ClaudeDo.Worker.Tests/Report/WeekReportServiceTests.cs
T
mika kuns 1c76852957 fix(worker): route WeekReportService permission-mode through PermissionModeResolver
Closes the last unrouted --permission-mode call site: haiku default models were
hardcoding raw "auto", which the CLI silently downgrades to interactive "default"
and hangs the report generation. Also drops the author's personal C:\Private
default exclude in favor of an empty default (config stays via ReportExcludedPaths).
2026-09-10 13:13:16 +02:00

109 lines
4.2 KiB
C#

using ClaudeDo.Data;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.Report;
using ClaudeDo.Worker.Report.Interfaces;
using ClaudeDo.Worker.Runner;
using ClaudeDo.Worker.Tests.Infrastructure;
using Microsoft.Extensions.Logging.Abstractions;
namespace ClaudeDo.Worker.Tests.Report;
public class WeekReportServiceTests : IDisposable
{
private readonly DbFixture _db = new();
public void Dispose() => _db.Dispose();
private static readonly DateOnly Start = new(2026, 5, 28);
private static readonly DateOnly End = new(2026, 6, 3);
private sealed class FakeReader : IClaudeHistoryReader
{
public IReadOnlyList<RepoActivity> Result = Array.Empty<RepoActivity>();
public Task<IReadOnlyList<RepoActivity>> ReadAsync(
DateOnly s, DateOnly e, IReadOnlyList<string> ex, CancellationToken ct) => Task.FromResult(Result);
}
private sealed class FakeClaude : IClaudeProcess
{
public int Calls;
public IReadOnlyList<string>? LastArgs;
public RunResult Next = new() { ExitCode = 0, ResultMarkdown = "## Bericht" };
public Task<RunResult> RunAsync(IReadOnlyList<string> args, string prompt, string wd, Func<string, Task> onLine, CancellationToken ct)
{ Calls++; LastArgs = args; return Task.FromResult(Next); }
}
private WeekReportService Make(FakeReader reader, FakeClaude claude) =>
new(reader, _db.CreateFactory(), claude, NullLogger<WeekReportService>.Instance);
[Fact]
public async Task EmptyWindow_ProducesNoActivityReport_WithoutCallingClaude()
{
var claude = new FakeClaude();
var svc = Make(new FakeReader(), claude);
var md = await svc.GenerateAsync(Start, End);
Assert.Equal(0, claude.Calls);
Assert.Contains("No activity", md);
using var ctx = _db.CreateContext();
Assert.NotNull(await new WeekReportRepository(ctx).GetByRangeAsync(Start, End));
}
[Fact]
public async Task SuccessPath_StoresAndReturnsClaudeMarkdown()
{
var repo = new RepoActivity { RepoPath = @"C:\Dev\App" };
var day = new DayActivity { Date = new DateOnly(2026, 6, 1) };
day.Prompts.Add("Add login");
repo.Days.Add(day);
var claude = new FakeClaude { Next = new RunResult { ExitCode = 0, ResultMarkdown = "## Bericht\n- Habe Login umgesetzt" } };
var svc = Make(new FakeReader { Result = new[] { repo } }, claude);
var md = await svc.GenerateAsync(Start, End);
Assert.Equal(1, claude.Calls);
Assert.Contains("Habe Login umgesetzt", md);
Assert.Equal(md, await svc.GetStoredAsync(Start, End));
}
[Fact]
public async Task HaikuDefaultModel_RoutesPermissionModeThroughResolver()
{
using (var ctx = _db.CreateContext())
{
var repo = new AppSettingsRepository(ctx);
var settings = await repo.GetAsync();
settings.DefaultModel = "haiku";
await repo.UpdateAsync(settings);
}
var activityRepo = new RepoActivity { RepoPath = @"C:\Dev\App" };
var day = new DayActivity { Date = new DateOnly(2026, 6, 1) };
day.Prompts.Add("x");
activityRepo.Days.Add(day);
var claude = new FakeClaude();
var svc = Make(new FakeReader { Result = new[] { activityRepo } }, claude);
await svc.GenerateAsync(Start, End);
Assert.NotNull(claude.LastArgs);
var modeIndex = claude.LastArgs!.ToList().IndexOf("--permission-mode") + 1;
Assert.Equal(PermissionModeResolver.Resolve("haiku", "auto"), claude.LastArgs[modeIndex]);
Assert.NotEqual("auto", claude.LastArgs[modeIndex]);
}
[Fact]
public async Task ClaudeFailure_Throws_AndDoesNotStore()
{
var repo = new RepoActivity { RepoPath = @"C:\Dev\App" };
var day = new DayActivity { Date = new DateOnly(2026, 6, 1) };
day.Prompts.Add("x");
repo.Days.Add(day);
var claude = new FakeClaude { Next = new RunResult { ExitCode = 1, ErrorMarkdown = "boom" } };
var svc = Make(new FakeReader { Result = new[] { repo } }, claude);
await Assert.ThrowsAsync<InvalidOperationException>(() => svc.GenerateAsync(Start, End));
Assert.Null(await svc.GetStoredAsync(Start, End));
}
}