Nothing replayed against upstream reads a session while it is playing #204

Closed
opened 2026-09-12 17:16:57 +02:00 by Claude · 1 comment
Collaborator

No scenario reads /Sessions while anything is playing. playstate.json has no /Sessions request at all, and clients.json:78 (/Sessions?activeWithinSeconds=900) runs with nothing playing. So the whole of what 78be052e and 1fe062f5 changed — a playing session's position, and everything a reader sees between two reports — is covered by unit and route tests only, and by nothing the harness replays against upstream.

1fe062f5 added the ignore-list entry the scenario will need (**.PlayState.PositionTicks, value scope), because both servers now advance with the whole seconds of replay latency and the two disagree by however long the replay took to reach each of them. Without that entry the scenario flakes; with it, the position's shape is still diffed.

What the scenario should cover

A report, then a read; a pause and a read; a resume; a seek; and a stop. The values the two servers agree on are the shape and everything around the position — what a reader must actually see is asserted in crates/ephyra-http/tests/sessions.rs against a controlled clock, which is where it belongs.

Two divergences to settle while doing it

LastActivityDate does not advance here. Upstream's automated tick runs through UpdateNowPlayingItem, which sets it unconditionally (upstream/Emby.Server.Implementations/Session/SessionManager.cs:431), so a playing session's activity date keeps moving between reports. Measured 6s after one report: upstream 13:49:12.5 against a check-in of 13:49:06.5; ours reads 13:49:06.5 for both. It feeds /Sessions ordering (:158) and the activeWithinSeconds filter (:2159) — a client reporting every 10s passes activeWithinSeconds=5 upstream and fails it here, which is a real behavioural difference and not only a date. The ignore list already suppresses the value with until: Never, so the harness will never show it; that is exactly how it hides.

A session whose report carried an empty ItemId does advance upstream. StartAutomaticProgress is armed regardless of NowPlayingItem (:784); the timer's runtime is then null and the position advances unbounded. Our gate is now_playing.is_some() (crates/ephyra-app/src/session/sessions.rs), so it freezes. No real client reports playback without an item, so this is probably the right call — but it is a divergence and DECISIONS.md:1150-1155 says it gets written down either way.

Found reviewing #201 (1fe062f5). Related: #202, #203.

No scenario reads `/Sessions` while anything is playing. `playstate.json` has no `/Sessions` request at all, and `clients.json:78` (`/Sessions?activeWithinSeconds=900`) runs with nothing playing. So the whole of what `78be052e` and `1fe062f5` changed — a playing session's position, and everything a reader sees between two reports — is covered by unit and route tests only, and by nothing the harness replays against upstream. `1fe062f5` added the ignore-list entry the scenario will need (`**.PlayState.PositionTicks`, value scope), because both servers now advance with the whole seconds of replay latency and the two disagree by however long the replay took to reach each of them. Without that entry the scenario flakes; with it, the position's *shape* is still diffed. ## What the scenario should cover A report, then a read; a pause and a read; a resume; a seek; and a stop. The values the two servers agree on are the shape and everything around the position — what a reader must actually see is asserted in `crates/ephyra-http/tests/sessions.rs` against a controlled clock, which is where it belongs. ## Two divergences to settle while doing it **`LastActivityDate` does not advance here.** Upstream's automated tick runs through `UpdateNowPlayingItem`, which sets it unconditionally (`upstream/Emby.Server.Implementations/Session/SessionManager.cs:431`), so a playing session's activity date keeps moving between reports. Measured 6s after one report: upstream `13:49:12.5` against a check-in of `13:49:06.5`; ours reads `13:49:06.5` for both. It feeds `/Sessions` ordering (`:158`) and the `activeWithinSeconds` filter (`:2159`) — **a client reporting every 10s passes `activeWithinSeconds=5` upstream and fails it here**, which is a real behavioural difference and not only a date. The ignore list already suppresses the value with `until: Never`, so the harness will never show it; that is exactly how it hides. **A session whose report carried an empty `ItemId` does advance upstream.** `StartAutomaticProgress` is armed regardless of `NowPlayingItem` (`:784`); the timer's runtime is then null and the position advances unbounded. Our gate is `now_playing.is_some()` (`crates/ephyra-app/src/session/sessions.rs`), so it freezes. No real client reports playback without an item, so this is probably the right call — but it is a divergence and `DECISIONS.md:1150-1155` says it gets written down either way. Found reviewing #201 (`1fe062f5`). Related: #202, #203.
Author
Collaborator

Fixed in 198602a6, 87975a4a and 273dbdc9, landed on main at f0ffda81.

crates/ephyra-harness/scenarios/sessions.json reads /Sessions while a client plays: a report, pause, resume, seek and stop, each followed by a read. It was replayed against a private pair over copies of snapshots/default, because its reports write. The result was 13 responses, 13 in agreement and 19 values suppressed.

Both divergences named here are settled:

  • LastActivityDate now advances with the position, so activeWithinSeconds agrees between reports.
  • A report with an empty ItemId does not advance. This is recorded as D47 and held against the route in 4d34b079.
Fixed in `198602a6`, `87975a4a` and `273dbdc9`, landed on `main` at `f0ffda81`. `crates/ephyra-harness/scenarios/sessions.json` reads `/Sessions` while a client plays: a report, pause, resume, seek and stop, each followed by a read. It was replayed against a private pair over copies of `snapshots/default`, because its reports write. The result was 13 responses, 13 in agreement and 19 values suppressed. Both divergences named here are settled: - `LastActivityDate` now advances with the position, so `activeWithinSeconds` agrees between reports. - A report with an empty `ItemId` does not advance. This is recorded as D47 and held against the route in `4d34b079`.
Sign in to join this conversation.
No description provided.