using ClaudeDo.Data; using ClaudeDo.Data.Models; using ClaudeDo.Localization; using ClaudeDo.Ui.Localization; using ClaudeDo.Ui.Services; using ClaudeDo.Ui.ViewModels.Islands; using Microsoft.EntityFrameworkCore; namespace ClaudeDo.Ui.Tests.ViewModels; // Polish-Audit 2026-08-20 #3: a list created by a running MCP session broadcasts ListUpdated with // the raw list id, which RefreshRowAsync had no add-branch for — the list stayed invisible until // the next app restart. And a reconnect only refreshed counts, missing offline list changes. public class ListsIslandListUpdatedTests : IDisposable { private readonly string _dbPath; public ListsIslandListUpdatedTests() { _dbPath = Path.Combine(Path.GetTempPath(), $"claudedo_lists_updated_test_{Guid.NewGuid():N}.db"); using var ctx = NewContext(); ctx.Database.EnsureCreated(); var dir = AppContext.BaseDirectory; while (dir is not null && !Directory.Exists(Path.Combine(dir, "src", "ClaudeDo.Localization", "locales"))) dir = Path.GetDirectoryName(dir); Loc.Current = new Localizer( LocaleStore.Load(Path.Combine(dir!, "src", "ClaudeDo.Localization", "locales")), "en"); } public void Dispose() { try { File.Delete(_dbPath); } catch { } try { File.Delete(_dbPath + "-wal"); } catch { } try { File.Delete(_dbPath + "-shm"); } catch { } } private ClaudeDoDbContext NewContext() { var opts = new DbContextOptionsBuilder() .UseSqlite($"Data Source={_dbPath}") .Options; return new ClaudeDoDbContext(opts); } private sealed class TestDbFactory : IDbContextFactory { private readonly Func _create; public TestDbFactory(Func create) => _create = create; public ClaudeDoDbContext CreateDbContext() => _create(); } private sealed class FakeWorker : StubWorkerClient { public override bool IsConnected => true; } private async Task SeedListAsync(string id, string name, int sortOrder) { await using var db = NewContext(); db.Lists.Add(new ListEntity { Id = id, Name = name, SortOrder = sortOrder, CreatedAt = DateTime.UtcNow }); await db.SaveChangesAsync(); } [Fact] public async Task ListUpdated_ForAnIdNotYetLoaded_AddsItToUserLists() { await SeedListAsync("existing", "Existing", sortOrder: 0); var worker = new FakeWorker(); var vm = new ListsIslandViewModel(new TestDbFactory(NewContext), worker: worker); await vm.LoadAsync(); Assert.Single(vm.UserLists); // A running MCP session creates the list after our initial load — the worker broadcasts // the raw db id, never the "user:" prefixed row id. await SeedListAsync("new-from-mcp", "Created via MCP", sortOrder: 1); worker.RaiseListUpdated("new-from-mcp"); await Task.Delay(100); Assert.Equal(2, vm.UserLists.Count); Assert.Contains(vm.UserLists, r => r.Id == "user:new-from-mcp" && r.Name == "Created via MCP"); // Inserted at its DB sort position (after "existing"), not just appended — same relative // order as if it had been present since LoadAsync. Assert.Equal("user:existing", vm.UserLists[0].Id); Assert.Equal("user:new-from-mcp", vm.UserLists[1].Id); Assert.Contains(vm.Items, r => r.Id == "user:new-from-mcp"); } [Fact] public async Task ListUpdated_ForAnIdMissingFromTheDbToo_DoesNothingAndDoesNotThrow() { var worker = new FakeWorker(); var vm = new ListsIslandViewModel(new TestDbFactory(NewContext), worker: worker); await vm.LoadAsync(); var countBefore = vm.UserLists.Count; worker.RaiseListUpdated("ghost-id"); await Task.Delay(100); Assert.Equal(countBefore, vm.UserLists.Count); } [Fact] public async Task ConnectionRestored_FullyReloadsLists_AndKeepsSelectionIfStillPresent() { await SeedListAsync("keep-me", "Keep Me", sortOrder: 0); var worker = new FakeWorker(); var vm = new ListsIslandViewModel(new TestDbFactory(NewContext), worker: worker); await vm.LoadAsync(); var toSelect = vm.UserLists.Single(r => r.Id == "user:keep-me"); vm.SelectedList = toSelect; // A list created while offline — the reconnect must pick it up, unlike a bare count refresh. await SeedListAsync("came-in-offline", "Came In Offline", sortOrder: 1); worker.RaiseConnectionRestored(); await Task.Delay(150); Assert.Contains(vm.UserLists, r => r.Id == "user:came-in-offline"); Assert.NotNull(vm.SelectedList); Assert.Equal("user:keep-me", vm.SelectedList!.Id); } }