fix(core): auto-track skips the seeding refresh
A series' first metadata refresh reveals its whole back catalogue, and apply_auto_track flagged every season as new — adding Rick and Morty tracked S01-S09 and wanted 91 episodes (#160). Per DESIGN.md 4.1 the rule applies from the second refresh onward. The caller passes whether metadata_refreshed_at is set; arr-core stays IO-free and decides. Wired through the daemon's daily refresh and the API's create-season endpoint.
This commit is contained in:
@@ -168,7 +168,13 @@ impl SeriesRefreshAction {
|
||||
});
|
||||
}
|
||||
}
|
||||
apply_auto_track(&core_series(stale), &mut revealed);
|
||||
// #160: the fact of a previous refresh rides in from the row, so the
|
||||
// seeding refresh never tracks the back catalogue it reveals.
|
||||
apply_auto_track(
|
||||
&core_series(stale),
|
||||
stale.metadata_refreshed_at.is_some(),
|
||||
&mut revealed,
|
||||
);
|
||||
for refreshed in revealed {
|
||||
self.insert_revealed_season(&mut transaction, stale.id, refreshed)
|
||||
.await?;
|
||||
@@ -779,8 +785,11 @@ mod tests {
|
||||
assert_eq!(vote, Some(8.417));
|
||||
}
|
||||
|
||||
/// #160. A series' first refresh reveals its back catalogue, but §4.1
|
||||
/// never tracks what was already there at add time: nothing is tracked,
|
||||
/// nothing arrives wanted.
|
||||
#[tokio::test]
|
||||
async fn first_refresh_reveals_seasons_and_applies_auto_track() {
|
||||
async fn the_seeding_refresh_reveals_but_tracks_nothing() {
|
||||
let (_dir, database) = seeded_series(true).await;
|
||||
let server = tmdb("Returning Series", season_one_body(&two_episodes())).await;
|
||||
|
||||
@@ -792,14 +801,14 @@ mod tests {
|
||||
.fetch_all(database.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(rows, vec![(1, true), (2, true)]);
|
||||
assert_eq!(rows, vec![(1, false), (2, false)]);
|
||||
let wanted: i64 = sqlx::query_scalar(
|
||||
"SELECT count(*) FROM episodes WHERE wanted = 1 AND state = 'missing'",
|
||||
)
|
||||
.fetch_one(database.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(wanted, 3);
|
||||
assert_eq!(wanted, 0);
|
||||
// #120 backfill: the series was added before it had an id on file.
|
||||
let tvdb_id: Option<i64> =
|
||||
sqlx::query_scalar("SELECT tvdb_id FROM series WHERE tmdb_id = 82728")
|
||||
@@ -816,6 +825,50 @@ mod tests {
|
||||
assert_eq!(stamped, 1);
|
||||
}
|
||||
|
||||
/// #160. Once the series has been refreshed before, a season TMDB reveals
|
||||
/// that the library has never seen genuinely appeared after add time, so
|
||||
/// it is tracked and its episodes arrive wanted.
|
||||
#[tokio::test]
|
||||
async fn a_later_refresh_tracks_a_genuinely_new_season() {
|
||||
let (_dir, database) = seeded_series(true).await;
|
||||
let server = MockServer::start().await;
|
||||
mount_with(&server, "Returning Series", &[1]).await;
|
||||
|
||||
action(&server).tick(&database).await.unwrap();
|
||||
let tracked: i64 = sqlx::query_scalar("SELECT count(*) FROM seasons WHERE tracked = 1")
|
||||
.fetch_one(database.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(tracked, 0, "the seeding refresh tracks nothing");
|
||||
|
||||
expire_refresh(&database).await;
|
||||
server.reset().await;
|
||||
mount(
|
||||
&server,
|
||||
"Returning Series",
|
||||
season_one_body(&two_episodes()),
|
||||
)
|
||||
.await;
|
||||
|
||||
let outcomes = action(&server).tick(&database).await.unwrap();
|
||||
|
||||
assert_eq!(outcomes.len(), 1);
|
||||
let rows: Vec<(i64, bool)> =
|
||||
sqlx::query_as("SELECT number, tracked FROM seasons ORDER BY number")
|
||||
.fetch_all(database.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(rows, vec![(1, false), (2, true)]);
|
||||
let wanted: Vec<i64> = sqlx::query_scalar(
|
||||
"SELECT e.number FROM episodes e JOIN seasons s ON s.id = e.season_id
|
||||
WHERE s.number = 2 AND e.wanted = 1",
|
||||
)
|
||||
.fetch_all(database.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(wanted, vec![1]);
|
||||
}
|
||||
|
||||
/// `auto_track` is a rule about reveals, not intent: without it nothing
|
||||
/// arrives wanted, even though everything is revealed.
|
||||
#[tokio::test]
|
||||
@@ -877,13 +930,13 @@ mod tests {
|
||||
.fetch_all(database.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(rows, vec![(1, true), (2, true)]);
|
||||
assert_eq!(rows, vec![(1, false), (2, false)]);
|
||||
let counts: (i64, i64) =
|
||||
sqlx::query_as("SELECT count(*), coalesce(sum(wanted), 0) FROM episodes")
|
||||
.fetch_one(database.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(counts, (3, 3));
|
||||
assert_eq!(counts, (3, 0));
|
||||
}
|
||||
|
||||
/// Episodes revealed into an existing season follow that season's rule:
|
||||
|
||||
Reference in New Issue
Block a user