favorites and image details model tests#155
Conversation
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds two new test suites to pin important model behaviors: favorites deduplication keyed by Model.Image.cid, and URL routing from the Image Details description links (recursive navigation for pastvu.com/p/<cid> vs. opening everything else in the browser).
Changes:
- Introduces
FavoritesModelTestscovering add/remove deduplication semantics bycid(including no-op persistence behavior). - Introduces
ImageDetailsModelTestscovering link routing for PastVu photo links vs. external/non-photo PastVu links. - Adds local async polling helper (
eventually) in the new Image Details tests to observe async reducer effects.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| RewindTests/FavoritesModelTests.swift | New tests that lock in favorites add/remove semantics and persistence no-ops when deduping by cid. |
| RewindTests/ImageDetailsModelTests.swift | New tests that lock in description-link routing behavior for PastVu photo links vs. browser hand-off. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| remote: Remote { [weak self] cid in | ||
| self?.requestedCids.append(cid) | ||
| return modified(.mock) { $0.cid = cid } | ||
| }, |
There was a problem hiding this comment.
🤖 Written by Claude Code (AI agent), posted from @chizberg's account.
Checked this — it's a false positive; the closure body does run on the main actor.
Remote.impl is declared as (Args) async throws -> Response (Rewind/Utils/Remote.swift:11), without @Sendable. A non-@Sendable closure literal formed inside an isolated context inherits that isolation, and Harness.makeModel() is @MainActor-isolated. So the closure is @MainActor, and await impl(args) from the reducer's Task hops back to the main actor before entering the body.
Two checks:
-
Runtime. Temporarily put
MainActor.assertIsolated()as the first line of the closure body and ran the target in Debug (whereassertIsolatedis live):TEST SUCCEEDED, no trap. The closure definitely executes —pastvuPhotoLinkOpensNestedDetailsassertsrequestedCids == [linkedCid]. -
Compiler. Forced a clean recompile of the test files: zero isolation/Sendable diagnostics for
ImageDetailsModelTests.swift. Meanwhile the already-mergedMapModelTests.swift, which writesawait self.record(params)in the exact same position, gets threeno 'async' operations occur within 'await' expressionwarnings (lines 370, 371, 379) — the compiler considers thoseawaits redundant precisely because that closure is likewise main-actor-isolated.
One caveat worth noting: this rests on isolation inheritance under Swift 5 language mode (SWIFT_VERSION = 5.0). Under Swift 6, converting a @MainActor closure to a non-isolated function type would be diagnosed — but that would affect MapModelTests and production call sites equally, so it's a project-wide migration question rather than something specific to this PR.
Added a comment at the harness so this isn't re-derived next time (06fb802).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two more model test suites.
FavoritesModelTests — pins that favorites dedup keys off
cidalone rather than the whole struct (Model.Image.==compares onlycid):cidis a no-op: the first entry is kept and the storage effect is not re-invoked;cideven when every other field of the passed instance differs (e.g. one rehydrated fromStorage.Image); an unknowncidtouches neither state nor storage.ImageDetailsModelTests — description-link routing, which hinges on a branchy URL parse (host +
/p/+ integer cid):loadingAnotherImageis set synchronously and cleared once the nested model is presented;Rebased onto fresh origin/main. Locally:
xcodebuild test— TEST SUCCEEDED;swiftlint --strictandswiftformat . --lint— clean.🤖 Generated with Claude Code