using ClawdDotNet.Core.Storage; using ClawdDotNet.Core.Tasks; using ClawdDotNet.Core.Tests.Infrastructure; using Microsoft.Extensions.Logging.Abstractions; using Shouldly; namespace ClawdDotNet.Core.Tests.Tasks; /// /// #689/#715: A status the run set itself is the run's word. An agent that escalates, or sets /// its task to blocked to report up, must not see it overwritten with "done" when the run ends. /// public sealed class RunSetStatusTests : IDisposable { private readonly string _directory = Path.Combine(Path.GetTempPath(), "clawd-tests", Guid.NewGuid().ToString("N")); private readonly SqliteTaskRepository _repo; private readonly FakeTimeProvider _clock = new(new DateTimeOffset(2027, 3, 10, 12, 0, 0, TimeSpan.Zero)); public RunSetStatusTests() { _repo = new SqliteTaskRepository(new SqliteStorage(Path.Combine(_directory, "state.db"))); } public void Dispose() { SqliteStorage.ReleasePooledConnections(_directory); try { Directory.Delete(_directory, recursive: true); } catch { /* cleanup is secondary */ } } /// A run that sets its own task to a status, as the taskboard tool would. private sealed class SettingDispatcher(SqliteTaskRepository repo, TaskItemStatus? set) : ITaskDispatcher { public async Task DispatchAsync(TaskItem task, CancellationToken ct) { if (set is { } status) await repo.SetStatusAsync(task.Id, status, DateTime.UtcNow, ct); return TaskLauf.Erledigt; } } private async Task RunWith(TaskItemStatus? set) { await _repo.UpsertAsync(new TaskItem { Id = "t-1", Title = "Work", Assignee = "@worker" }, default); var scanner = new TaskScanner(_repo, new SettingDispatcher(_repo, set), NullLoggerFactory.Instance, clock: _clock); await scanner.ScanOnceAsync(default); return (await _repo.GetAsync("t-1", default))!.Status; } [Theory] [InlineData(TaskItemStatus.Blocked)] [InlineData(TaskItemStatus.Canceled)] public async Task A_status_the_run_set_itself_is_kept(TaskItemStatus set) => (await RunWith(set)).ShouldBe(set); [Fact] public async Task A_run_that_sets_nothing_is_done() => (await RunWith(null)).ShouldBe(TaskItemStatus.Done); }