DownloadStatus was declared four times, byte for byte identically, in
radarr_models, sonarr_models, lidarr_models, and readarr_models: the same
variants, the same derive list, and the same serde, strum, and display_style
attributes. Every servarr's queue endpoint reports the same status
vocabulary, so there is nothing servarr specific about the type.
Move it into servarr_models, preserving that file's alphabetical ordering
(between DiskSpace and IndexerSettings), and drop the four copies. The
DownloadRecord types keep their per-servarr shapes and now reference the
shared enum.
The two enum tests, test_download_status_display and
test_download_status_to_display_str, were likewise duplicated in the four
per-servarr model test files. Keep one copy of each in servarr_models_tests
alongside the other shared enum display tests and delete the rest. The
per-servarr DownloadRecord fallback deserialization tests stay where they
are since they exercise each servarr's own record type.
Behavior is unchanged. Every remaining edit is an import path update.
The shared download record held id and episode id both equal to 1, and
the shared episode held id and episode file id both equal to 1, so
reading the wrong one of those fields could not fail any test that uses
them. Move the download record to id 9 and the episode to id 11, keeping
the download record's episode id equal to the episode's id so the
downloading episode still matches, and move the episode file to id 7
together with the episode's episode file id so the season view still
finds its quality and size. Series id, season number, and episode number
stay at 1. The handler tree keeps its own copy of these fixtures; update
it identically, along with the JSON twins in the network tests and the
seven handler assertions that named the old ids. The two get episodes
tests that asserted the sorted table now spell out the sorted order,
since the fixture episode no longer sorts first by id.
The artist, album and season statistics fixtures set the file count equal to the total count, so the columns rendering the pair showed the same number twice and swapping the two fields changed nothing. The JSON fixtures move with them so the round trip assertions still hold.
Sonarr and Lidarr each keep their own copy of the shared fixture, both with minimum_age and retention set to 1, so transposing those two fields stayed invisible in either tree. Radarr built its fixture from IndexerSettings::default(), leaving availability_delay, maximum_size, minimum_age and retention all 0: four mutually transposable fields, none of which could distinguish a threaded value from a hardcoded 0.
The no-op guard test seeded IndexerSettings::default() as its sentinel, which cannot show whether the guard held or the field was simply never written. It now seeds a distinct stale_indexer_settings(), mirroring the Readarr convention.
Verified by transposing minimum_age and retention in the Lidarr indexer settings UI, now caught by its snapshot, and by inverting the Radarr no-op guard, now caught by both GetAllIndexerSettings tests.
A series' overview renders as one line of a description box that does
not scroll, so a long overview overflows with no way to read the rest.
Pressing v on the series details Seasons tab now opens a popup holding
the full overview.
The popup owns its own ScrollableText, built fresh from the series'
overview when it opens and torn down on Esc so a reopened popup starts
at the top. Scrolling mutates only that owned state. The background
paragraph renders the Series record's overview, which carries no scroll
offset, so it cannot scroll along with the popup.
Cite the existing view keybinding from the series details context clues
and give the popup its own clue set so it does not inherit the hints of
the view underneath.
Enrich the shared series fixture to a multi-paragraph overview, and the
raw response fixture that encodes the same series, so a scrolled popup
is distinguishable from an unscrolled one.