pinakes/crates/pinakes-server/tests/enrichment.rs
NotAShelf f1eacc8484
pinakes-server: add more route tests
Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Ief16a2b3181bfa50193fb69a5ad4a9166a6a6964
2026-03-22 22:05:04 +03:00

210 lines
5.4 KiB
Rust

mod common;
use axum::http::StatusCode;
use common::{
delete_authed,
get,
get_authed,
patch_json_authed,
post_json_authed,
put_json_authed,
response_body,
setup_app,
setup_app_with_auth,
};
use tower::ServiceExt;
// GET /api/v1/media/{id}/metadata/external (viewer)
#[tokio::test]
async fn get_external_metadata_requires_auth() {
let (app, ..) = setup_app_with_auth().await;
let response = app
.oneshot(get(
"/api/v1/media/00000000-0000-0000-0000-000000000000/external-metadata",
))
.await
.unwrap();
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}
#[tokio::test]
async fn get_external_metadata_viewer_ok() {
let (app, _, _, viewer_token) = setup_app_with_auth().await;
let response = app
.oneshot(get_authed(
"/api/v1/media/00000000-0000-0000-0000-000000000000/external-metadata",
&viewer_token,
))
.await
.unwrap();
// Media does not exist; 200 with empty array or 404 are both valid
assert!(
response.status() == StatusCode::OK
|| response.status() == StatusCode::NOT_FOUND
);
}
// POST /api/v1/media/{id}/enrich (editor)
#[tokio::test]
async fn trigger_enrichment_requires_editor() {
let (app, _, _, viewer_token) = setup_app_with_auth().await;
let response = app
.oneshot(post_json_authed(
"/api/v1/media/00000000-0000-0000-0000-000000000000/enrich",
"{}",
&viewer_token,
))
.await
.unwrap();
assert_eq!(response.status(), StatusCode::FORBIDDEN);
}
#[tokio::test]
async fn trigger_enrichment_editor_accepted() {
let (app, _, editor_token, _) = setup_app_with_auth().await;
let response = app
.oneshot(post_json_authed(
"/api/v1/media/00000000-0000-0000-0000-000000000000/enrich",
"{}",
&editor_token,
))
.await
.unwrap();
// Route is accessible to editors; media not found returns 404, job queued
// returns 200
assert!(
response.status() == StatusCode::OK
|| response.status() == StatusCode::NOT_FOUND
);
}
// POST /api/v1/jobs/enrich (editor, batch)
#[tokio::test]
async fn batch_enrich_requires_editor() {
let (app, _, _, viewer_token) = setup_app_with_auth().await;
let response = app
.oneshot(post_json_authed(
"/api/v1/jobs/enrich",
r#"{"media_ids":["00000000-0000-0000-0000-000000000000"]}"#,
&viewer_token,
))
.await
.unwrap();
assert_eq!(response.status(), StatusCode::FORBIDDEN);
}
#[tokio::test]
async fn batch_enrich_empty_ids_rejected() {
let (app, _, editor_token, _) = setup_app_with_auth().await;
let response = app
.oneshot(post_json_authed(
"/api/v1/jobs/enrich",
r#"{"media_ids":[]}"#,
&editor_token,
))
.await
.unwrap();
// Validation requires 1-1000 ids
assert!(
response.status() == StatusCode::BAD_REQUEST
|| response.status() == StatusCode::UNPROCESSABLE_ENTITY
);
}
#[tokio::test]
async fn batch_enrich_editor_accepted() {
let (app, _, editor_token, _) = setup_app_with_auth().await;
let response = app
.oneshot(post_json_authed(
"/api/v1/jobs/enrich",
r#"{"media_ids":["00000000-0000-0000-0000-000000000000"]}"#,
&editor_token,
))
.await
.unwrap();
// Job is queued and a job_id is returned
assert_eq!(response.status(), StatusCode::OK);
}
// No-auth coverage (exercises setup_app and get)
#[tokio::test]
async fn get_external_metadata_auth_disabled() {
let app = setup_app().await;
let response = app
.oneshot(get(
"/api/v1/media/00000000-0000-0000-0000-000000000000/external-metadata",
))
.await
.unwrap();
assert!(
response.status() == StatusCode::OK
|| response.status() == StatusCode::NOT_FOUND
);
}
// RBAC enforcement for editor-level HTTP methods
#[tokio::test]
async fn batch_enrich_response_has_job_id() {
let (app, _, editor_token, _) = setup_app_with_auth().await;
let response = app
.oneshot(post_json_authed(
"/api/v1/jobs/enrich",
r#"{"media_ids":["00000000-0000-0000-0000-000000000000"]}"#,
&editor_token,
))
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let body = response_body(response).await;
// Route queues a job and returns a job identifier
assert!(
body["job_id"].is_string() || body["id"].is_string(),
"expected job identifier in response: {body}"
);
}
#[tokio::test]
async fn delete_tag_requires_editor() {
let (app, _, _, viewer_token) = setup_app_with_auth().await;
let response = app
.oneshot(delete_authed(
"/api/v1/tags/00000000-0000-0000-0000-000000000000",
&viewer_token,
))
.await
.unwrap();
assert_eq!(response.status(), StatusCode::FORBIDDEN);
}
#[tokio::test]
async fn update_media_requires_editor() {
let (app, _, _, viewer_token) = setup_app_with_auth().await;
let response = app
.oneshot(patch_json_authed(
"/api/v1/media/00000000-0000-0000-0000-000000000000",
r#"{"title":"new title"}"#,
&viewer_token,
))
.await
.unwrap();
assert_eq!(response.status(), StatusCode::FORBIDDEN);
}
#[tokio::test]
async fn update_sync_device_requires_editor() {
let (app, _, _, viewer_token) = setup_app_with_auth().await;
let response = app
.oneshot(put_json_authed(
"/api/v1/sync/devices/00000000-0000-0000-0000-000000000000",
r#"{"name":"my device"}"#,
&viewer_token,
))
.await
.unwrap();
assert_eq!(response.status(), StatusCode::FORBIDDEN);
}