From 5232d5f130c8acb3f134f56cca103df9d806c7aa Mon Sep 17 00:00:00 2001 From: Mika Kuns Date: Tue, 14 Apr 2026 11:34:16 +0200 Subject: [PATCH] feat(data): add GetConfigAsync and SetConfigAsync to ListRepository Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Repositories/ListRepository.cs | 33 ++++++++++ .../Repositories/ListRepositoryConfigTests.cs | 61 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 tests/ClaudeDo.Worker.Tests/Repositories/ListRepositoryConfigTests.cs diff --git a/src/ClaudeDo.Data/Repositories/ListRepository.cs b/src/ClaudeDo.Data/Repositories/ListRepository.cs index eb185ae..ff0a639 100644 --- a/src/ClaudeDo.Data/Repositories/ListRepository.cs +++ b/src/ClaudeDo.Data/Repositories/ListRepository.cs @@ -113,6 +113,39 @@ public sealed class ListRepository await cmd.ExecuteNonQueryAsync(ct); } + public async Task GetConfigAsync(string listId, CancellationToken ct = default) + { + await using var conn = _factory.Open(); + await using var cmd = conn.CreateCommand(); + cmd.CommandText = "SELECT list_id, model, system_prompt, agent_path FROM list_config WHERE list_id = @list_id"; + cmd.Parameters.AddWithValue("@list_id", listId); + + await using var reader = await cmd.ExecuteReaderAsync(ct); + if (!await reader.ReadAsync(ct)) return null; + return new ListConfigEntity + { + ListId = reader.GetString(0), + Model = reader.IsDBNull(1) ? null : reader.GetString(1), + SystemPrompt = reader.IsDBNull(2) ? null : reader.GetString(2), + AgentPath = reader.IsDBNull(3) ? null : reader.GetString(3), + }; + } + + public async Task SetConfigAsync(ListConfigEntity entity, CancellationToken ct = default) + { + await using var conn = _factory.Open(); + await using var cmd = conn.CreateCommand(); + cmd.CommandText = """ + INSERT OR REPLACE INTO list_config (list_id, model, system_prompt, agent_path) + VALUES (@list_id, @model, @system_prompt, @agent_path) + """; + cmd.Parameters.AddWithValue("@list_id", entity.ListId); + cmd.Parameters.AddWithValue("@model", (object?)entity.Model ?? DBNull.Value); + cmd.Parameters.AddWithValue("@system_prompt", (object?)entity.SystemPrompt ?? DBNull.Value); + cmd.Parameters.AddWithValue("@agent_path", (object?)entity.AgentPath ?? DBNull.Value); + await cmd.ExecuteNonQueryAsync(ct); + } + private static ListEntity ReadList(SqliteDataReader reader) => new() { Id = reader.GetString(0), diff --git a/tests/ClaudeDo.Worker.Tests/Repositories/ListRepositoryConfigTests.cs b/tests/ClaudeDo.Worker.Tests/Repositories/ListRepositoryConfigTests.cs new file mode 100644 index 0000000..a9068f3 --- /dev/null +++ b/tests/ClaudeDo.Worker.Tests/Repositories/ListRepositoryConfigTests.cs @@ -0,0 +1,61 @@ +using ClaudeDo.Data.Models; +using ClaudeDo.Data.Repositories; +using ClaudeDo.Worker.Tests.Infrastructure; + +namespace ClaudeDo.Worker.Tests.Repositories; + +public sealed class ListRepositoryConfigTests : IDisposable +{ + private readonly DbFixture _db = new(); + private readonly ListRepository _repo; + private readonly string _listId; + + public ListRepositoryConfigTests() + { + _repo = new ListRepository(_db.Factory); + _listId = Guid.NewGuid().ToString(); + _repo.AddAsync(new ListEntity + { + Id = _listId, Name = "Test", CreatedAt = DateTime.UtcNow + }).GetAwaiter().GetResult(); + } + + [Fact] + public async Task GetConfig_Returns_Null_When_No_Config() + { + var config = await _repo.GetConfigAsync(_listId); + Assert.Null(config); + } + + [Fact] + public async Task SetConfig_And_GetConfig_Roundtrips() + { + var config = new ListConfigEntity + { + ListId = _listId, + Model = "sonnet-4-6", + SystemPrompt = "You are helpful.", + AgentPath = "/home/user/.todo-app/agents/dev.md", + }; + await _repo.SetConfigAsync(config); + + var fetched = await _repo.GetConfigAsync(_listId); + Assert.NotNull(fetched); + Assert.Equal("sonnet-4-6", fetched.Model); + Assert.Equal("You are helpful.", fetched.SystemPrompt); + Assert.Equal("/home/user/.todo-app/agents/dev.md", fetched.AgentPath); + } + + [Fact] + public async Task SetConfig_Upserts_On_Duplicate() + { + await _repo.SetConfigAsync(new ListConfigEntity { ListId = _listId, Model = "opus-4-6" }); + await _repo.SetConfigAsync(new ListConfigEntity { ListId = _listId, Model = "haiku-4-5" }); + + var fetched = await _repo.GetConfigAsync(_listId); + Assert.NotNull(fetched); + Assert.Equal("haiku-4-5", fetched.Model); + } + + public void Dispose() => _db.Dispose(); +}