feat(db): align movie state with canonical MediaState (#85)
This commit was merged in pull request #85.
This commit is contained in:
@@ -352,7 +352,7 @@ impl GrabAction {
|
||||
.fetch_optional(database.pool())
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
"UPDATE movies SET state = 'grabbed',
|
||||
"UPDATE movies SET state = 'downloading',
|
||||
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
||||
WHERE id = ?",
|
||||
movie.id
|
||||
@@ -852,7 +852,7 @@ mod tests {
|
||||
.fetch_one(database.pool())
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(state, "grabbed");
|
||||
assert_eq!(state, "downloading");
|
||||
}
|
||||
|
||||
/// The failure mode the issue names: killed after the torrent is sent and
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
-- #73. movies.state used missing|grabbed|imported, predating arr-core's
|
||||
-- canonical MediaState vocabulary (missing|downloading|available) that
|
||||
-- series/episodes (0005) already follow.
|
||||
--
|
||||
-- SQLite can't ALTER a CHECK constraint, so this rebuilds the table -- which
|
||||
-- means dropping the old copy. `movie_releases` references it with
|
||||
-- ON DELETE CASCADE, so the drop would take its rows down too; stash them
|
||||
-- first and restore them once the new `movies` exists with the same ids.
|
||||
|
||||
CREATE TABLE movie_releases_backup AS SELECT * FROM movie_releases;
|
||||
|
||||
CREATE TABLE movies_new (
|
||||
id INTEGER PRIMARY KEY,
|
||||
tmdb_id INTEGER NOT NULL UNIQUE,
|
||||
title TEXT NOT NULL,
|
||||
year INTEGER,
|
||||
original_language TEXT,
|
||||
root_id INTEGER NOT NULL REFERENCES roots (id),
|
||||
wanted INTEGER NOT NULL DEFAULT 1 CHECK (wanted IN (0, 1)),
|
||||
overrides TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(overrides)),
|
||||
state TEXT NOT NULL DEFAULT 'missing'
|
||||
CHECK (state IN ('missing', 'downloading', 'available')),
|
||||
blocked INTEGER NOT NULL DEFAULT 0 CHECK (blocked IN (0, 1)),
|
||||
search_attempts INTEGER NOT NULL DEFAULT 0,
|
||||
last_searched_at TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
||||
) STRICT;
|
||||
|
||||
INSERT INTO movies_new (
|
||||
id, tmdb_id, title, year, original_language, root_id, wanted, overrides,
|
||||
state, blocked, search_attempts, last_searched_at, created_at, updated_at
|
||||
)
|
||||
SELECT
|
||||
id, tmdb_id, title, year, original_language, root_id, wanted, overrides,
|
||||
CASE state
|
||||
WHEN 'grabbed' THEN 'downloading'
|
||||
WHEN 'imported' THEN 'available'
|
||||
ELSE state
|
||||
END,
|
||||
blocked, search_attempts, last_searched_at, created_at, updated_at
|
||||
FROM movies;
|
||||
|
||||
DROP TABLE movies;
|
||||
|
||||
ALTER TABLE movies_new RENAME TO movies;
|
||||
|
||||
CREATE INDEX movies_pending_search
|
||||
ON movies (last_searched_at)
|
||||
WHERE wanted = 1 AND blocked = 0;
|
||||
|
||||
CREATE INDEX movies_state ON movies (state);
|
||||
CREATE INDEX movies_root ON movies (root_id);
|
||||
|
||||
-- Dropping `movies` drops the triggers defined on it too (0005); recreate
|
||||
-- the movie-root invariant they enforced.
|
||||
CREATE TRIGGER movies_require_movie_root
|
||||
BEFORE INSERT ON movies
|
||||
WHEN EXISTS (SELECT 1 FROM roots WHERE id = NEW.root_id AND kind != 'movie')
|
||||
BEGIN
|
||||
SELECT RAISE(ABORT, 'movies require a movie root');
|
||||
END;
|
||||
|
||||
CREATE TRIGGER movies_require_movie_root_on_update
|
||||
BEFORE UPDATE OF root_id ON movies
|
||||
WHEN EXISTS (SELECT 1 FROM roots WHERE id = NEW.root_id AND kind != 'movie')
|
||||
BEGIN
|
||||
SELECT RAISE(ABORT, 'movies require a movie root');
|
||||
END;
|
||||
|
||||
INSERT INTO movie_releases SELECT * FROM movie_releases_backup;
|
||||
|
||||
DROP TABLE movie_releases_backup;
|
||||
@@ -252,6 +252,62 @@ mod tests {
|
||||
.expect_err("state is a closed set");
|
||||
}
|
||||
|
||||
/// #73: migration 0007 renames the pre-canonical `grabbed`/`imported`
|
||||
/// values in place rather than dropping the rows that held them.
|
||||
#[tokio::test]
|
||||
async fn movie_state_migration_renames_existing_rows_in_place() {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let db = Db::connect(dir.path().join("arr.db"))
|
||||
.await
|
||||
.expect("connect");
|
||||
|
||||
let pre_0007 = sqlx::migrate::Migrator {
|
||||
migrations: std::borrow::Cow::Owned(
|
||||
MIGRATOR.iter().filter(|m| m.version < 7).cloned().collect(),
|
||||
),
|
||||
ignore_missing: MIGRATOR.ignore_missing,
|
||||
locking: MIGRATOR.locking,
|
||||
no_tx: MIGRATOR.no_tx,
|
||||
};
|
||||
pre_0007
|
||||
.run(db.pool())
|
||||
.await
|
||||
.expect("migrations before #73");
|
||||
|
||||
sqlx::query(
|
||||
"INSERT INTO movies (tmdb_id, title, root_id, state)
|
||||
SELECT 1, 'Old Vocabulary', id, 'grabbed' FROM roots WHERE kind = 'movie' LIMIT 1",
|
||||
)
|
||||
.execute(db.pool())
|
||||
.await
|
||||
.expect("legacy 'grabbed' row, valid under the pre-#73 constraint");
|
||||
sqlx::query(
|
||||
"INSERT INTO movies (tmdb_id, title, root_id, state)
|
||||
SELECT 2, 'Also Old', id, 'imported' FROM roots WHERE kind = 'movie' LIMIT 1",
|
||||
)
|
||||
.execute(db.pool())
|
||||
.await
|
||||
.expect("legacy 'imported' row, valid under the pre-#73 constraint");
|
||||
|
||||
db.migrate()
|
||||
.await
|
||||
.expect("remaining migrations, including #73");
|
||||
|
||||
let renamed_from_grabbed: String =
|
||||
sqlx::query_scalar("SELECT state FROM movies WHERE tmdb_id = 1")
|
||||
.fetch_one(db.pool())
|
||||
.await
|
||||
.expect("row survives the migration");
|
||||
let renamed_from_imported: String =
|
||||
sqlx::query_scalar("SELECT state FROM movies WHERE tmdb_id = 2")
|
||||
.fetch_one(db.pool())
|
||||
.await
|
||||
.expect("row survives the migration");
|
||||
|
||||
assert_eq!(renamed_from_grabbed, "downloading");
|
||||
assert_eq!(renamed_from_imported, "available");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn seeds_two_tv_roots_with_distinct_policies() {
|
||||
let (_dir, db) = fresh().await;
|
||||
|
||||
Reference in New Issue
Block a user