feat(daily-prep): run daily prep from PrimeRunner via allowed MCP tools
This commit is contained in:
27
tests/ClaudeDo.Worker.Tests/Prime/DailyPrepPromptTests.cs
Normal file
27
tests/ClaudeDo.Worker.Tests/Prime/DailyPrepPromptTests.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using ClaudeDo.Worker.Prime;
|
||||
|
||||
namespace ClaudeDo.Worker.Tests.Prime;
|
||||
|
||||
public class DailyPrepPromptTests
|
||||
{
|
||||
[Fact]
|
||||
public void Build_prompt_contains_cap_and_date()
|
||||
{
|
||||
var prompt = DailyPrepPrompt.BuildPrompt(maxTasks: 5, today: new DateOnly(2026, 6, 3));
|
||||
Assert.Contains("5", prompt);
|
||||
Assert.Contains("2026-06-03", prompt);
|
||||
Assert.Contains("get_daily_prep_candidates", prompt);
|
||||
Assert.Contains("set_my_day", prompt);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_args_allows_only_the_two_tools()
|
||||
{
|
||||
var args = DailyPrepPrompt.BuildArgs(maxTurns: 30);
|
||||
Assert.Contains("--output-format stream-json", args);
|
||||
Assert.Contains("--max-turns 30", args);
|
||||
Assert.Contains("--allowedTools", args);
|
||||
Assert.Contains("mcp__claudedo__get_daily_prep_candidates", args);
|
||||
Assert.Contains("mcp__claudedo__set_my_day", args);
|
||||
}
|
||||
}
|
||||
86
tests/ClaudeDo.Worker.Tests/Prime/PrimeRunnerTests.cs
Normal file
86
tests/ClaudeDo.Worker.Tests/Prime/PrimeRunnerTests.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using ClaudeDo.Worker.Prime;
|
||||
using ClaudeDo.Worker.Runner;
|
||||
using ClaudeDo.Worker.Tests.Infrastructure;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
|
||||
namespace ClaudeDo.Worker.Tests.Prime;
|
||||
|
||||
public class PrimeRunnerTests : IDisposable
|
||||
{
|
||||
private readonly DbFixture _db = new();
|
||||
public void Dispose() => _db.Dispose();
|
||||
|
||||
private sealed class FakeClock : IPrimeClock
|
||||
{
|
||||
public DateTimeOffset Now { get; set; } = new DateTimeOffset(2026, 6, 3, 8, 0, 0, TimeSpan.FromHours(2));
|
||||
}
|
||||
|
||||
private sealed class FakeClaudeProcess : IClaudeProcess
|
||||
{
|
||||
private readonly TimeSpan _delay;
|
||||
private readonly int _exitCode;
|
||||
|
||||
public FakeClaudeProcess(TimeSpan delay = default, int exitCode = 0)
|
||||
{
|
||||
_delay = delay;
|
||||
_exitCode = exitCode;
|
||||
}
|
||||
|
||||
public async Task<RunResult> RunAsync(
|
||||
string arguments,
|
||||
string prompt,
|
||||
string workingDirectory,
|
||||
Func<string, Task> onStdoutLine,
|
||||
CancellationToken ct)
|
||||
{
|
||||
if (_delay > TimeSpan.Zero)
|
||||
await Task.Delay(_delay, ct);
|
||||
|
||||
return new RunResult { ExitCode = _exitCode, ResultMarkdown = _exitCode == 0 ? "ok" : null };
|
||||
}
|
||||
}
|
||||
|
||||
private PrimeRunner NewRunner(TimeSpan claudeDelay = default, int exitCode = 0) =>
|
||||
new PrimeRunner(
|
||||
new FakeClaudeProcess(claudeDelay, exitCode),
|
||||
_db.CreateFactory(),
|
||||
new FakeClock(),
|
||||
NullLogger<PrimeRunner>.Instance);
|
||||
|
||||
private static PrimeScheduleDto DefaultSchedule() =>
|
||||
new(Guid.Empty, 0, TimeSpan.Zero, true, null, null);
|
||||
|
||||
[Fact]
|
||||
public async Task FireAsync_returns_success_when_claude_exits_zero()
|
||||
{
|
||||
var runner = NewRunner();
|
||||
var outcome = await runner.FireAsync(DefaultSchedule(), CancellationToken.None);
|
||||
|
||||
Assert.True(outcome.Success);
|
||||
Assert.Equal("Daily prep complete", outcome.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FireAsync_returns_failure_when_claude_exits_nonzero()
|
||||
{
|
||||
var runner = NewRunner(exitCode: 1);
|
||||
var outcome = await runner.FireAsync(DefaultSchedule(), CancellationToken.None);
|
||||
|
||||
Assert.False(outcome.Success);
|
||||
Assert.Contains("exit code", outcome.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FireAsync_returns_already_running_when_gate_held()
|
||||
{
|
||||
var runner = NewRunner(claudeDelay: TimeSpan.FromSeconds(2));
|
||||
var schedule = DefaultSchedule();
|
||||
|
||||
var first = runner.FireAsync(schedule, CancellationToken.None);
|
||||
var second = await runner.FireAsync(schedule, CancellationToken.None);
|
||||
|
||||
Assert.False(second.Success);
|
||||
Assert.Contains("already running", second.Message, StringComparison.OrdinalIgnoreCase);
|
||||
await first;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user