fix(api): document and test error paths
This commit is contained in:
@@ -165,7 +165,11 @@ async fn load_movie(state: &AppState, id: i64) -> Result<Movie, ApiError> {
|
||||
|
||||
#[utoipa::path(
|
||||
get, path = "/api/movies", tag = "movies",
|
||||
responses((status = 200, body = [Movie]), (status = 503, body = ErrorBody))
|
||||
responses(
|
||||
(status = 200, body = [Movie]),
|
||||
(status = 500, body = ErrorBody),
|
||||
(status = 503, body = ErrorBody)
|
||||
)
|
||||
)]
|
||||
pub async fn list(State(state): State<AppState>) -> Result<Json<Vec<Movie>>, ApiError> {
|
||||
let movies = sqlx::query_as!(Movie, r#"SELECT id AS "id!: i64", tmdb_id AS "tmdb_id!: i64", title AS "title!: String", year, original_language, root_id AS "root_id!: i64", wanted AS "wanted!: bool", overrides AS "overrides!: serde_json::Value", state AS "state!: String", blocked AS "blocked!: bool", search_attempts AS "search_attempts!: i64", last_searched_at FROM movies ORDER BY title, year, id"#)
|
||||
@@ -176,7 +180,13 @@ pub async fn list(State(state): State<AppState>) -> Result<Json<Vec<Movie>>, Api
|
||||
|
||||
#[utoipa::path(
|
||||
post, path = "/api/movies", tag = "movies", request_body = CreateMovie,
|
||||
responses((status = 201, body = Movie), (status = 409, body = ErrorBody), (status = 422, body = ErrorBody))
|
||||
responses(
|
||||
(status = 201, body = Movie),
|
||||
(status = 409, body = ErrorBody),
|
||||
(status = 422, body = ErrorBody),
|
||||
(status = 500, body = ErrorBody),
|
||||
(status = 503, body = ErrorBody)
|
||||
)
|
||||
)]
|
||||
pub async fn create(
|
||||
State(state): State<AppState>,
|
||||
@@ -214,7 +224,12 @@ pub async fn create(
|
||||
#[utoipa::path(
|
||||
get, path = "/api/movies/{movie_id}", tag = "movies",
|
||||
params(("movie_id" = i64, Path, description = "Movie row id")),
|
||||
responses((status = 200, body = Movie), (status = 404, body = ErrorBody))
|
||||
responses(
|
||||
(status = 200, body = Movie),
|
||||
(status = 404, body = ErrorBody),
|
||||
(status = 500, body = ErrorBody),
|
||||
(status = 503, body = ErrorBody)
|
||||
)
|
||||
)]
|
||||
pub async fn get(
|
||||
State(state): State<AppState>,
|
||||
@@ -226,7 +241,14 @@ pub async fn get(
|
||||
#[utoipa::path(
|
||||
patch, path = "/api/movies/{movie_id}", tag = "movies", request_body = UpdateMovie,
|
||||
params(("movie_id" = i64, Path, description = "Movie row id")),
|
||||
responses((status = 200, body = Movie), (status = 404, body = ErrorBody), (status = 422, body = ErrorBody))
|
||||
responses(
|
||||
(status = 200, body = Movie),
|
||||
(status = 404, body = ErrorBody),
|
||||
(status = 409, body = ErrorBody),
|
||||
(status = 422, body = ErrorBody),
|
||||
(status = 500, body = ErrorBody),
|
||||
(status = 503, body = ErrorBody)
|
||||
)
|
||||
)]
|
||||
pub async fn update(
|
||||
State(state): State<AppState>,
|
||||
@@ -268,7 +290,13 @@ pub async fn update(
|
||||
#[utoipa::path(
|
||||
delete, path = "/api/movies/{movie_id}", tag = "movies",
|
||||
params(("movie_id" = i64, Path, description = "Movie row id")),
|
||||
responses((status = 204), (status = 404, body = ErrorBody))
|
||||
responses(
|
||||
(status = 204),
|
||||
(status = 404, body = ErrorBody),
|
||||
(status = 409, body = ErrorBody),
|
||||
(status = 500, body = ErrorBody),
|
||||
(status = 503, body = ErrorBody)
|
||||
)
|
||||
)]
|
||||
pub async fn delete(
|
||||
State(state): State<AppState>,
|
||||
@@ -286,7 +314,12 @@ pub async fn delete(
|
||||
#[utoipa::path(
|
||||
post, path = "/api/movies/{movie_id}/search", tag = "movies",
|
||||
params(("movie_id" = i64, Path, description = "Movie row id")),
|
||||
responses((status = 202, body = Accepted), (status = 404, body = ErrorBody))
|
||||
responses(
|
||||
(status = 202, body = Accepted),
|
||||
(status = 404, body = ErrorBody),
|
||||
(status = 500, body = ErrorBody),
|
||||
(status = 503, body = ErrorBody)
|
||||
)
|
||||
)]
|
||||
pub async fn search(
|
||||
State(state): State<AppState>,
|
||||
@@ -300,7 +333,12 @@ pub async fn search(
|
||||
#[utoipa::path(
|
||||
get, path = "/api/movies/{movie_id}/releases", tag = "movies",
|
||||
params(("movie_id" = i64, Path, description = "Movie row id")),
|
||||
responses((status = 200, body = [Release]), (status = 404, body = ErrorBody))
|
||||
responses(
|
||||
(status = 200, body = [Release]),
|
||||
(status = 404, body = ErrorBody),
|
||||
(status = 500, body = ErrorBody),
|
||||
(status = 503, body = ErrorBody)
|
||||
)
|
||||
)]
|
||||
pub async fn releases(
|
||||
State(state): State<AppState>,
|
||||
@@ -316,7 +354,12 @@ pub async fn releases(
|
||||
#[utoipa::path(
|
||||
post, path = "/api/movies/{movie_id}/releases/{release_id}/grab", tag = "movies",
|
||||
params(("movie_id" = i64, Path), ("release_id" = i64, Path)),
|
||||
responses((status = 202, body = Accepted), (status = 404, body = ErrorBody))
|
||||
responses(
|
||||
(status = 202, body = Accepted),
|
||||
(status = 404, body = ErrorBody),
|
||||
(status = 500, body = ErrorBody),
|
||||
(status = 503, body = ErrorBody)
|
||||
)
|
||||
)]
|
||||
pub async fn grab(
|
||||
State(state): State<AppState>,
|
||||
@@ -337,7 +380,11 @@ pub async fn grab(
|
||||
|
||||
#[utoipa::path(
|
||||
get, path = "/api/queues/attention", tag = "movies",
|
||||
responses((status = 200, body = AttentionQueues), (status = 503, body = ErrorBody))
|
||||
responses(
|
||||
(status = 200, body = AttentionQueues),
|
||||
(status = 500, body = ErrorBody),
|
||||
(status = 503, body = ErrorBody)
|
||||
)
|
||||
)]
|
||||
pub async fn attention(State(state): State<AppState>) -> Result<Json<AttentionQueues>, ApiError> {
|
||||
let no_pt_source = sqlx::query_as!(Movie, r#"SELECT id AS "id!: i64", tmdb_id AS "tmdb_id!: i64", title AS "title!: String", year, original_language, root_id AS "root_id!: i64", wanted AS "wanted!: bool", overrides AS "overrides!: serde_json::Value", state AS "state!: String", blocked AS "blocked!: bool", search_attempts AS "search_attempts!: i64", last_searched_at FROM movies WHERE id IN (SELECT m.id FROM movies m JOIN roots root ON root.id = m.root_id WHERE root.audience = 'kids' AND m.wanted = 1 AND m.state = 'missing' AND m.search_attempts > 0 AND NOT EXISTS (SELECT 1 FROM movie_releases mr JOIN releases r ON r.id = mr.release_id WHERE mr.movie_id = m.id AND r.verdict IN ('eligible', 'waived'))) ORDER BY title"#)
|
||||
@@ -481,4 +528,70 @@ mod tests {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn attention_queues_follow_search_and_failure_state() {
|
||||
let (_dir, state, base) = application().await;
|
||||
let pool = state.database().expect("database").pool();
|
||||
let root_id: i64 = sqlx::query_scalar("SELECT id FROM roots WHERE audience = 'kids'")
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.expect("kids root");
|
||||
let movie = add_movie(&base, 82728, root_id).await;
|
||||
let movie_id = movie["id"].as_i64().expect("id");
|
||||
sqlx::query("UPDATE movies SET search_attempts = 1 WHERE id = ?")
|
||||
.bind(movie_id)
|
||||
.execute(pool)
|
||||
.await
|
||||
.expect("record search");
|
||||
|
||||
let mut release_ids = Vec::new();
|
||||
for suffix in ["a", "b"] {
|
||||
let release_id = sqlx::query("INSERT INTO releases (indexer_id, guid, name, size, download_url, parsed, verdict, rejected_rule) VALUES (1, ?, ?, 1, 'url', '{}', 'rejected', 'required_audio')")
|
||||
.bind(suffix)
|
||||
.bind(suffix)
|
||||
.execute(pool)
|
||||
.await
|
||||
.expect("release")
|
||||
.last_insert_rowid();
|
||||
release_ids.push(release_id);
|
||||
sqlx::query("INSERT INTO grabs (release_id, target_kind, target_id, infohash, state) VALUES (?, 'movie', ?, ?, 'failed')")
|
||||
.bind(release_id)
|
||||
.bind(movie_id)
|
||||
.bind(format!("hash-{suffix}"))
|
||||
.execute(pool)
|
||||
.await
|
||||
.expect("failed grab");
|
||||
}
|
||||
|
||||
let queues: serde_json::Value = reqwest::get(format!("{base}/api/queues/attention"))
|
||||
.await
|
||||
.expect("queues")
|
||||
.json()
|
||||
.await
|
||||
.expect("queues json");
|
||||
assert_eq!(queues["no_pt_source"][0]["id"], movie_id);
|
||||
assert_eq!(queues["needs_decision"][0]["id"], movie_id);
|
||||
|
||||
sqlx::query("UPDATE releases SET verdict = 'eligible', rejected_rule = NULL WHERE id = ?")
|
||||
.bind(release_ids[0])
|
||||
.execute(pool)
|
||||
.await
|
||||
.expect("eligible release");
|
||||
sqlx::query("INSERT INTO movie_releases (movie_id, release_id) VALUES (?, ?)")
|
||||
.bind(movie_id)
|
||||
.bind(release_ids[0])
|
||||
.execute(pool)
|
||||
.await
|
||||
.expect("associate release");
|
||||
|
||||
let queues: serde_json::Value = reqwest::get(format!("{base}/api/queues/attention"))
|
||||
.await
|
||||
.expect("queues")
|
||||
.json()
|
||||
.await
|
||||
.expect("queues json");
|
||||
assert_eq!(queues["no_pt_source"].as_array().map(Vec::len), Some(0));
|
||||
assert_eq!(queues["needs_decision"][0]["id"], movie_id);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user