using ClaudeDo.Data; using ClaudeDo.Data.Models; using ClaudeDo.Data.Repositories; using ClaudeDo.Worker.External; using ClaudeDo.Worker.Hub; using ClaudeDo.Worker.Tests.Infrastructure; using TaskStatus = ClaudeDo.Data.Models.TaskStatus; namespace ClaudeDo.Worker.Tests.External; /// /// get_effective_run_config must report exactly what TaskRunner will actually run with, plus /// where each value came from — task/list/preset/global — so a caller can tell a real default /// from an override before queuing a run. See docs task "MCP: get_effective_run_config". /// public sealed class EffectiveRunConfigTests : IDisposable { private readonly DbFixture _db = new(); private readonly ClaudeDoDbContext _ctx; private readonly ListRepository _lists; private readonly TaskRepository _tasks; private readonly ConfigMcpTools _sut; public EffectiveRunConfigTests() { _ctx = _db.CreateContext(); _lists = new ListRepository(_ctx); _tasks = new TaskRepository(_ctx); _sut = new ConfigMcpTools(_lists, _tasks, new HubBroadcaster(new CapturingHubContext()), _db.CreateFactory()); } public void Dispose() { _ctx.Dispose(); _db.Dispose(); } private async Task SeedListAsync() { var id = Guid.NewGuid().ToString(); await _lists.AddAsync(new ListEntity { Id = id, Name = "L", CreatedAt = DateTime.UtcNow }); return id; } private async Task SeedTaskAsync(string listId, Action? configure = null) { var task = new TaskEntity { Id = Guid.NewGuid().ToString(), ListId = listId, Title = "t", Status = TaskStatus.Idle, CreatedAt = DateTime.UtcNow, CommitType = "chore", }; configure?.Invoke(task); await _tasks.AddAsync(task); return task; } [Fact] public async Task No_overrides_reports_preset_and_global_sources() { var listId = await SeedListAsync(); var task = await SeedTaskAsync(listId); var result = await _sut.GetEffectiveRunConfig(task.Id, CancellationToken.None); Assert.Equal("sonnet", result.Model.Value); Assert.Equal("global", result.Model.Source); Assert.Equal("preset", result.MaxTurns.Source); Assert.Equal(30, result.MaxTurns.Effective); Assert.Equal(30, result.MaxTurns.Requested); Assert.False(result.MaxTurns.Clamped); Assert.Equal("high", result.Effort); Assert.Equal("auto", result.PermissionMode); Assert.Null(result.AgentPath.Value); Assert.Null(result.AgentPath.Source); Assert.Empty(result.SkillNames); } [Fact] public async Task Task_override_beats_list_override_beats_preset() { var listId = await SeedListAsync(); await _sut.SetListConfig(listId, "opus", null, "list-agent.md", 50, CancellationToken.None); var task = await SeedTaskAsync(listId, t => { t.Model = "haiku"; t.MaxTurns = 12; }); var result = await _sut.GetEffectiveRunConfig(task.Id, CancellationToken.None); Assert.Equal("haiku", result.Model.Value); Assert.Equal("task", result.Model.Source); Assert.Equal(12, result.MaxTurns.Effective); Assert.Equal("task", result.MaxTurns.Source); Assert.Equal("list-agent.md", result.AgentPath.Value); Assert.Equal("list", result.AgentPath.Source); } [Fact] public async Task List_override_wins_when_no_task_override() { var listId = await SeedListAsync(); await _sut.SetListConfig(listId, "opus", null, null, 50, CancellationToken.None); var task = await SeedTaskAsync(listId); var result = await _sut.GetEffectiveRunConfig(task.Id, CancellationToken.None); Assert.Equal("opus", result.Model.Value); Assert.Equal("list", result.Model.Source); Assert.Equal(50, result.MaxTurns.Effective); Assert.Equal("list", result.MaxTurns.Source); } [Fact] public async Task Requested_max_turns_above_ceiling_is_clamped_and_both_values_reported() { var listId = await SeedListAsync(); var task = await SeedTaskAsync(listId, t => t.MaxTurns = 999); var result = await _sut.GetEffectiveRunConfig(task.Id, CancellationToken.None); Assert.Equal(999, result.MaxTurns.Requested); Assert.Equal(80, result.MaxTurns.Effective); // AppSettingsEntity.MaxTurnsCeiling default Assert.True(result.MaxTurns.Clamped); Assert.Equal("task", result.MaxTurns.Source); } [Fact] public async Task System_prompt_reports_set_and_contributing_layers() { var listId = await SeedListAsync(); var task = await SeedTaskAsync(listId, t => t.SystemPrompt = "be terse"); var result = await _sut.GetEffectiveRunConfig(task.Id, CancellationToken.None); Assert.True(result.SystemPrompt.Set); Assert.Contains("task", result.SystemPrompt.Sources); } [Fact] public async Task Unknown_task_throws() { await Assert.ThrowsAsync( () => _sut.GetEffectiveRunConfig("nope", CancellationToken.None)); } }