feat(db): add series, season and episode model (#71)
This commit was merged in pull request #71.
This commit is contained in:
+128
-8
@@ -156,13 +156,13 @@ mod tests {
|
||||
|
||||
let roots = db.list_roots().await.expect("list roots");
|
||||
|
||||
assert_eq!(roots.len(), 2, "§5.1: two movie roots, TV comes with #34");
|
||||
assert!(roots.iter().all(|r| r.kind == "movie"));
|
||||
assert_eq!(roots[0].audience, "kids");
|
||||
assert_eq!(roots[0].path, "/mnt/media/movies/kids");
|
||||
assert_eq!(roots[1].audience, "main");
|
||||
assert_eq!(roots[1].path, "/mnt/media/movies/main");
|
||||
assert_ne!(roots[0].policy_id, roots[1].policy_id);
|
||||
let movie_roots: Vec<_> = roots.iter().filter(|root| root.kind == "movie").collect();
|
||||
assert_eq!(movie_roots.len(), 2);
|
||||
assert_eq!(movie_roots[0].audience, "kids");
|
||||
assert_eq!(movie_roots[0].path, "/mnt/media/movies/kids");
|
||||
assert_eq!(movie_roots[1].audience, "main");
|
||||
assert_eq!(movie_roots[1].path, "/mnt/media/movies/main");
|
||||
assert_ne!(movie_roots[0].policy_id, movie_roots[1].policy_id);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -200,7 +200,7 @@ mod tests {
|
||||
.await
|
||||
.expect("policies");
|
||||
|
||||
assert_eq!(rows.len(), 2);
|
||||
assert_eq!(rows.len(), 4);
|
||||
for row in rows {
|
||||
let size_bands: String = row.get(0);
|
||||
let score_weights: String = row.get(1);
|
||||
@@ -247,4 +247,124 @@ mod tests {
|
||||
.await
|
||||
.expect_err("state is a closed set");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn seeds_two_tv_roots_with_distinct_policies() {
|
||||
let (_dir, db) = fresh().await;
|
||||
let roots = db.list_roots().await.expect("list roots");
|
||||
let tv_roots: Vec<_> = roots.iter().filter(|root| root.kind == "tv").collect();
|
||||
|
||||
assert_eq!(tv_roots.len(), 2);
|
||||
assert_eq!(tv_roots[0].audience, "kids");
|
||||
assert_eq!(tv_roots[0].path, "/mnt/media/tv/kids");
|
||||
assert_eq!(tv_roots[1].audience, "main");
|
||||
assert_eq!(tv_roots[1].path, "/mnt/media/tv/main");
|
||||
assert_ne!(tv_roots[0].policy_id, tv_roots[1].policy_id);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn episode_wanted_is_independent_of_tracking_rules() {
|
||||
let (_dir, db) = fresh().await;
|
||||
let root_id: i64 =
|
||||
sqlx::query_scalar("SELECT id FROM roots WHERE kind = 'tv' AND audience = 'main'")
|
||||
.fetch_one(db.pool())
|
||||
.await
|
||||
.expect("TV root");
|
||||
let series_id = sqlx::query("INSERT INTO series (tmdb_id, title, root_id, auto_track) VALUES (82728, 'Bluey', ?, 1)")
|
||||
.bind(root_id)
|
||||
.execute(db.pool())
|
||||
.await
|
||||
.expect("series")
|
||||
.last_insert_rowid();
|
||||
let season_id =
|
||||
sqlx::query("INSERT INTO seasons (series_id, number, tracked) VALUES (?, 1, 1)")
|
||||
.bind(series_id)
|
||||
.execute(db.pool())
|
||||
.await
|
||||
.expect("season")
|
||||
.last_insert_rowid();
|
||||
sqlx::query("INSERT INTO episodes (season_id, number, title) VALUES (?, 2, 'Hospital')")
|
||||
.bind(season_id)
|
||||
.execute(db.pool())
|
||||
.await
|
||||
.expect("episode");
|
||||
|
||||
let wanted: bool = sqlx::query_scalar("SELECT wanted FROM episodes")
|
||||
.fetch_one(db.pool())
|
||||
.await
|
||||
.expect("wanted");
|
||||
assert!(
|
||||
!wanted,
|
||||
"auto_track and tracked are rules, not episode intent"
|
||||
);
|
||||
|
||||
sqlx::query("INSERT INTO movies (tmdb_id, title, root_id) SELECT 693134, 'Dune Part Two', id FROM roots WHERE kind = 'movie' AND audience = 'main'")
|
||||
.execute(db.pool())
|
||||
.await
|
||||
.expect("existing movie schema remains writable");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn aggregates_require_roots_of_their_own_kind() {
|
||||
let (_dir, db) = fresh().await;
|
||||
let movie_root: i64 =
|
||||
sqlx::query_scalar("SELECT id FROM roots WHERE kind = 'movie' LIMIT 1")
|
||||
.fetch_one(db.pool())
|
||||
.await
|
||||
.expect("movie root");
|
||||
let tv_root: i64 = sqlx::query_scalar("SELECT id FROM roots WHERE kind = 'tv' LIMIT 1")
|
||||
.fetch_one(db.pool())
|
||||
.await
|
||||
.expect("TV root");
|
||||
|
||||
sqlx::query("INSERT INTO movies (tmdb_id, title, root_id) VALUES (1, 'x', ?)")
|
||||
.bind(tv_root)
|
||||
.execute(db.pool())
|
||||
.await
|
||||
.expect_err("movie cannot use TV root");
|
||||
sqlx::query("INSERT INTO series (tmdb_id, title, root_id) VALUES (2, 'x', ?)")
|
||||
.bind(movie_root)
|
||||
.execute(db.pool())
|
||||
.await
|
||||
.expect_err("series cannot use movie root");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn episode_state_and_upstream_completion_are_constrained() {
|
||||
let (_dir, db) = fresh().await;
|
||||
let root_id: i64 =
|
||||
sqlx::query_scalar("SELECT id FROM roots WHERE kind = 'tv' AND audience = 'main'")
|
||||
.fetch_one(db.pool())
|
||||
.await
|
||||
.expect("TV root");
|
||||
let series_id = sqlx::query(
|
||||
"INSERT INTO series (tmdb_id, title, root_id, upstream_ended)
|
||||
VALUES (82728, 'Bluey', ?, 1)",
|
||||
)
|
||||
.bind(root_id)
|
||||
.execute(db.pool())
|
||||
.await
|
||||
.expect("ended series")
|
||||
.last_insert_rowid();
|
||||
let season_id = sqlx::query("INSERT INTO seasons (series_id, number) VALUES (?, 1)")
|
||||
.bind(series_id)
|
||||
.execute(db.pool())
|
||||
.await
|
||||
.expect("season")
|
||||
.last_insert_rowid();
|
||||
|
||||
sqlx::query(
|
||||
"INSERT INTO episodes (season_id, number, title, state)
|
||||
VALUES (?, 1, 'Magic Xylophone', 'grabbed')",
|
||||
)
|
||||
.bind(season_id)
|
||||
.execute(db.pool())
|
||||
.await
|
||||
.expect_err("database mirrors MediaState");
|
||||
sqlx::query("UPDATE series SET upstream_ended = 2 WHERE id = ?")
|
||||
.bind(series_id)
|
||||
.execute(db.pool())
|
||||
.await
|
||||
.expect_err("upstream completion is boolean");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user