Update cached entities by stable ID - #111
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The new test HTTP handler returns text/plain content which can break GetFromJsonAsync, and duplicate-ID failures should surface an actionable error message (not the raw ToDictionary exception) because it is shown to users via toast.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR addresses audit finding P2-09 by making cached-entity identity stable and ID-based (instead of relying on sort position / timestamps), ensuring refreshes correctly replace/reposition entities and persist edits for non-versioned entities while preserving version semantics for versioned ones.
Changes:
- Introduces
ICachedEntityand updatesCachedCollection<T>to update/replace entries by stableIdand to reject duplicate remote IDs before mutating state. - Makes News ordering deterministic when publication timestamps match (tie-break by
Id). - Adds a focused linked-source test project (
tests/p2-09) to validate refresh/restart behavior, persistence, duplicate-ID rejection, and cache DB recreation seeding.
File summaries
| File | Description |
|---|---|
| tests/p2-09/CacheIdentityTests.cs | Adds regression/behavior tests for ID-stable cache updates across News/Banners and versioned entities. |
| tests/p2-09/CacheIdentity.Tests.csproj | Adds a standalone linked-source test project to run cache-identity tests without MAUI workloads. |
| tests/p2-09/CacheBoundary.cs | Provides a test fixture and minimal EF context + HTTP handler boundary for cache tests. |
| OneGateApp/Models/ICachedEntity.cs | Introduces the stable ID contract used by caching. |
| OneGateApp/Models/CachedCollection.cs | Implements ID-based add/update/removal behavior and ID-uniqueness validation during refresh. |
| OneGateApp/Data/News.cs | Adds ICachedEntity and deterministic ordering for same-time items. |
| OneGateApp/Data/DApp.cs | Implements ICachedEntity to satisfy the updated cache contract. |
| OneGateApp/Data/Banner.cs | Implements ICachedEntity to satisfy the updated cache contract. |
| OneGateApp.slnx | Adds the new p2-09 test project to the solution. |
Review details
- Files reviewed: 9/9 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| { | ||
| Requests++; | ||
| if (Error is not null) throw Error; | ||
| return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(Json) }); |
| var items_new = (await httpClient.GetFromJsonAsync<T[]>(url))!; | ||
| var incomingById = items_new.ToDictionary(p => p.Id); | ||
| var currentById = this.ToDictionary(p => p.Id); |
Summary
Fix audit P2-09: cache identity must use the entity ID, not its sort position or publication timestamp.
Validation
dotnet test tests/p2-09/CacheIdentity.Tests.csproj --no-restore --nologo: 7/7 passed using production cache/entity source and SQLite.4cdb404a92eca99d7f9c5d49bbdeff136ce58342585f5a64d9a01ec73cf3228b; matched to iOS and Android evidence before committing.Limitations and integration
Native refresh responses were controlled fixtures, not proof of a production backend change. No wallet signing or chain submissions were performed; screenshots and QA fixtures remain outside the repository and are not attached here, and no hosted CI pass is claimed.
Independently based and validated on
master@623603d; preserve the union of solution project entries and revalidate after integrating other audit changes, which have not been jointly validated. When combining audit P2-08/P2-07, retain forced-refresh and offline/preference behavior alongside ID-based updates, and add thisICachedEntitydependency to the P2-08-cache linked sources and VersionedItem test boundary.