test(496,484): record projection failures during enumeration, not eagerly [decisions-edit]
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 11s
PR Gates / Docs update reminder (pull_request) Successful in 10s
PR Gates / decisions lifecycle (pull_request) Successful in 15s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 6m21s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 9s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 8s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 17m21s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 21m44s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped

Re-review Low. The substitute incremented the failure counter inside .Returns(...), i.e. when
the enumerable was handed out, while the real paginator records from ProjectToMusicVideo's catch
DURING enumeration. A refactor that snapshotted Count before the enumeration completed would
then break production while both replacement tests kept passing — exactly the regression the
guard exists to prevent.

Moves the recording into an async iterator, and corrects the decision record to describe #484's
removed music-video test accurately and name its two replacements.
This commit is contained in:
2026-07-25 17:36:34 +02:00
parent 4bdad4bf52
commit b5b6e7f636
2 changed files with 33 additions and 11 deletions
+7 -1
View File
@@ -3905,7 +3905,13 @@ owner's sweep **destroyed** the row the other library still served. This is the
the protection — which is what this scanner did until the counter was threaded through
(`MediaServerProjectionFailureCounter`, one per enumeration, never a field on the singleton api client). The
single guard call covers the legacy path diff too, which is if anything more exposed: a legacy row has no etag
to fall back on.
to fall back on. #484's own music-video test asserted the pre-#496 hard delete
(`FindMusicVideoPaths`/`DeleteByPath`) and could not survive this change; it is replaced by
`ScanLibrary_Should_Not_Sweep_When_The_Projection_Reported_Failures` (identity sweep) and
`..._Should_Not_Sweep_Legacy_Rows_When_The_Projection_Reported_Failures` (legacy sweep). Their substitute
records failures **during** enumeration, not when the enumerable is handed out — an eager substitute would keep
passing if a refactor snapshotted `Count` before the enumeration completed, which is precisely the regression
the guard exists to prevent.
- **Scope honesty: this is parity, not a total fix.** One file path is still one `MediaItem` row globally
(`MediaFileAlreadyExists` is a global path-hash guard), so a second library serving the same file still gets
no row of its own — exactly as for movies/TV. What changes is that the first library's sweep now *flags*