Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ia09d2d3ad7f6613e21d20321e0877bc16a6a6964
738 lines
19 KiB
Rust
738 lines
19 KiB
Rust
mod common;
|
|
use axum::{
|
|
body::Body,
|
|
http::{Request, StatusCode},
|
|
};
|
|
use common::{
|
|
delete_authed,
|
|
get,
|
|
get_authed,
|
|
patch_json_authed,
|
|
post_json,
|
|
post_json_authed,
|
|
put_json_authed,
|
|
response_body,
|
|
setup_app,
|
|
setup_app_with_auth,
|
|
test_addr,
|
|
};
|
|
use http_body_util::BodyExt;
|
|
use tower::ServiceExt;
|
|
|
|
#[tokio::test]
|
|
async fn test_list_media_empty() {
|
|
let app = setup_app().await;
|
|
|
|
let response = app.oneshot(get("/api/v1/media")).await.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
|
let items: Vec<serde_json::Value> = serde_json::from_slice(&body).unwrap();
|
|
assert_eq!(items.len(), 0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_create_and_list_tags() {
|
|
let app = setup_app().await;
|
|
|
|
// Create a tag
|
|
let response = app
|
|
.clone()
|
|
.oneshot(post_json("/api/v1/tags", r#"{"name":"Music"}"#))
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
// List tags
|
|
let response = app.oneshot(get("/api/v1/tags")).await.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
|
let tags: Vec<serde_json::Value> = serde_json::from_slice(&body).unwrap();
|
|
assert_eq!(tags.len(), 1);
|
|
assert_eq!(tags[0]["name"], "Music");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_search_empty() {
|
|
let app = setup_app().await;
|
|
|
|
let response = app.oneshot(get("/api/v1/search?q=test")).await.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
|
let result: serde_json::Value = serde_json::from_slice(&body).unwrap();
|
|
assert_eq!(result["total_count"], 0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_media_not_found() {
|
|
let app = setup_app().await;
|
|
|
|
let response = app
|
|
.oneshot(get("/api/v1/media/00000000-0000-0000-0000-000000000000"))
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_collections_crud() {
|
|
let app = setup_app().await;
|
|
|
|
// Create collection
|
|
let response = app
|
|
.clone()
|
|
.oneshot(post_json(
|
|
"/api/v1/collections",
|
|
r#"{"name":"Favorites","kind":"manual"}"#,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
// List collections
|
|
let response = app.oneshot(get("/api/v1/collections")).await.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
|
let cols: Vec<serde_json::Value> = serde_json::from_slice(&body).unwrap();
|
|
assert_eq!(cols.len(), 1);
|
|
assert_eq!(cols[0]["name"], "Favorites");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_statistics_endpoint() {
|
|
let app = setup_app().await;
|
|
|
|
let response = app.oneshot(get("/api/v1/statistics")).await.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
|
let stats: serde_json::Value = serde_json::from_slice(&body).unwrap();
|
|
assert_eq!(stats["total_media"], 0);
|
|
assert_eq!(stats["total_size_bytes"], 0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_scheduled_tasks_endpoint() {
|
|
let app = setup_app().await;
|
|
|
|
let response = app.oneshot(get("/api/v1/tasks/scheduled")).await.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
|
let tasks: Vec<serde_json::Value> = serde_json::from_slice(&body).unwrap();
|
|
assert!(!tasks.is_empty(), "should have default scheduled tasks");
|
|
// Verify structure of first task
|
|
assert!(tasks[0]["id"].is_string());
|
|
assert!(tasks[0]["name"].is_string());
|
|
assert!(tasks[0]["schedule"].is_string());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_user_management_crud() {
|
|
let app = setup_app().await;
|
|
|
|
// Create a user
|
|
let response = app
|
|
.clone()
|
|
.oneshot(post_json(
|
|
"/api/v1/users",
|
|
r#"{"username":"testuser","password":"password123","role":"viewer"}"#,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
|
let user: serde_json::Value = serde_json::from_slice(&body).unwrap();
|
|
assert_eq!(user["username"], "testuser");
|
|
assert_eq!(user["role"], "viewer");
|
|
let user_id = user["id"].as_str().unwrap();
|
|
|
|
// List users
|
|
let response = app.clone().oneshot(get("/api/v1/users")).await.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
|
let users: Vec<serde_json::Value> = serde_json::from_slice(&body).unwrap();
|
|
assert_eq!(users.len(), 1);
|
|
assert_eq!(users[0]["username"], "testuser");
|
|
|
|
// Get specific user
|
|
let response = app
|
|
.clone()
|
|
.oneshot(get(&format!("/api/v1/users/{user_id}")))
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response.into_body().collect().await.unwrap().to_bytes();
|
|
let retrieved_user: serde_json::Value =
|
|
serde_json::from_slice(&body).unwrap();
|
|
assert_eq!(retrieved_user["username"], "testuser");
|
|
|
|
// Delete user
|
|
let mut req = Request::builder()
|
|
.method("DELETE")
|
|
.uri(format!("/api/v1/users/{user_id}"))
|
|
.body(Body::empty())
|
|
.unwrap();
|
|
req.extensions_mut().insert(test_addr());
|
|
|
|
let response = app.clone().oneshot(req).await.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
// Verify user is deleted
|
|
let response = app
|
|
.oneshot(get(&format!("/api/v1/users/{user_id}")))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_health_endpoint() {
|
|
let app = setup_app().await;
|
|
|
|
// Health endpoint should be publicly accessible
|
|
let response = app.oneshot(get("/api/v1/health")).await.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_user_duplicate_username() {
|
|
let app = setup_app().await;
|
|
|
|
// Create first user
|
|
let response = app
|
|
.clone()
|
|
.oneshot(post_json(
|
|
"/api/v1/users",
|
|
r#"{"username":"duplicate","password":"password1","role":"viewer"}"#,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
// Try to create user with same username
|
|
let response = app
|
|
.oneshot(post_json(
|
|
"/api/v1/users",
|
|
r#"{"username":"duplicate","password":"password2","role":"viewer"}"#,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
|
|
// Should fail with conflict (409) for duplicate username
|
|
assert_eq!(response.status(), StatusCode::CONFLICT);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_unauthenticated_request_rejected() {
|
|
let (app, ..) = setup_app_with_auth().await;
|
|
|
|
// Request without Bearer token
|
|
let response = app.oneshot(get("/api/v1/media")).await.unwrap();
|
|
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_invalid_token_rejected() {
|
|
let (app, ..) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(get_authed("/api/v1/media", "totally-invalid-token"))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_login_valid_credentials() {
|
|
let (app, ..) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(post_json(
|
|
"/api/v1/auth/login",
|
|
r#"{"username":"admin","password":"adminpass"}"#,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response_body(response).await;
|
|
assert!(body["token"].is_string());
|
|
assert_eq!(body["username"], "admin");
|
|
assert_eq!(body["role"], "admin");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_login_invalid_password() {
|
|
let (app, ..) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(post_json(
|
|
"/api/v1/auth/login",
|
|
r#"{"username":"admin","password":"wrongpassword"}"#,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_login_unknown_user() {
|
|
let (app, ..) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(post_json(
|
|
"/api/v1/auth/login",
|
|
r#"{"username":"nonexistent","password":"whatever"}"#,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_auth_me_endpoint() {
|
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(get_authed("/api/v1/auth/me", &admin_token))
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response_body(response).await;
|
|
assert_eq!(body["username"], "admin");
|
|
assert_eq!(body["role"], "admin");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_logout() {
|
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
|
|
|
// Logout
|
|
let response = app
|
|
.clone()
|
|
.oneshot(post_json_authed("/api/v1/auth/logout", "", &admin_token))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
// Subsequent requests with same token should fail
|
|
let response = app
|
|
.oneshot(get_authed("/api/v1/media", &admin_token))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_viewer_cannot_access_editor_routes() {
|
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
|
|
|
// POST /tags is an editor-only route
|
|
let response = app
|
|
.oneshot(post_json_authed(
|
|
"/api/v1/tags",
|
|
r#"{"name":"test"}"#,
|
|
&viewer_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_viewer_cannot_access_admin_routes() {
|
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
|
|
|
// GET /users is an admin-only route
|
|
let response = app
|
|
.oneshot(get_authed("/api/v1/users", &viewer_token))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_editor_cannot_access_admin_routes() {
|
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(get_authed("/api/v1/users", &editor_token))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_editor_can_write() {
|
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(post_json_authed(
|
|
"/api/v1/tags",
|
|
r#"{"name":"EditorTag"}"#,
|
|
&editor_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_admin_can_access_all() {
|
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
|
|
|
// Viewer route
|
|
let response = app
|
|
.clone()
|
|
.oneshot(get_authed("/api/v1/media", &admin_token))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
// Editor route
|
|
let response = app
|
|
.clone()
|
|
.oneshot(post_json_authed(
|
|
"/api/v1/tags",
|
|
r#"{"name":"AdminTag"}"#,
|
|
&admin_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
// Admin route
|
|
let response = app
|
|
.oneshot(get_authed("/api/v1/users", &admin_token))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_rating_invalid_stars_zero() {
|
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(post_json_authed(
|
|
"/api/v1/media/00000000-0000-0000-0000-000000000000/ratings",
|
|
r#"{"stars":0}"#,
|
|
&editor_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_rating_invalid_stars_six() {
|
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(post_json_authed(
|
|
"/api/v1/media/00000000-0000-0000-0000-000000000000/ratings",
|
|
r#"{"stars":6}"#,
|
|
&editor_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_comment_empty_text() {
|
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(post_json_authed(
|
|
"/api/v1/media/00000000-0000-0000-0000-000000000000/comments",
|
|
r#"{"text":""}"#,
|
|
&editor_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_favorites_list_empty() {
|
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(get_authed("/api/v1/favorites", &viewer_token))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response_body(response).await;
|
|
assert!(body.as_array().unwrap().is_empty());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_playlist_crud() {
|
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
|
|
|
// Create
|
|
let response = app
|
|
.clone()
|
|
.oneshot(post_json_authed(
|
|
"/api/v1/playlists",
|
|
r#"{"name":"My Playlist"}"#,
|
|
&editor_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response_body(response).await;
|
|
let playlist_id = body["id"].as_str().unwrap().to_string();
|
|
assert_eq!(body["name"], "My Playlist");
|
|
|
|
// List
|
|
let response = app
|
|
.clone()
|
|
.oneshot(get_authed("/api/v1/playlists", &editor_token))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response_body(response).await;
|
|
assert_eq!(body.as_array().unwrap().len(), 1);
|
|
|
|
// Get
|
|
let response = app
|
|
.clone()
|
|
.oneshot(get_authed(
|
|
&format!("/api/v1/playlists/{playlist_id}"),
|
|
&editor_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
|
|
// Update
|
|
let response = app
|
|
.clone()
|
|
.oneshot(patch_json_authed(
|
|
&format!("/api/v1/playlists/{playlist_id}"),
|
|
r#"{"name":"Updated Playlist","description":"A test description"}"#,
|
|
&editor_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response_body(response).await;
|
|
assert_eq!(body["name"], "Updated Playlist");
|
|
|
|
// Delete
|
|
let response = app
|
|
.clone()
|
|
.oneshot(delete_authed(
|
|
&format!("/api/v1/playlists/{playlist_id}"),
|
|
&editor_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_playlist_empty_name() {
|
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(post_json_authed(
|
|
"/api/v1/playlists",
|
|
r#"{"name":""}"#,
|
|
&editor_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_most_viewed_empty() {
|
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(get_authed("/api/v1/analytics/most-viewed", &viewer_token))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response_body(response).await;
|
|
assert!(body.as_array().unwrap().is_empty());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_record_event_and_query() {
|
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
|
|
|
// Record an event
|
|
let response = app
|
|
.clone()
|
|
.oneshot(post_json_authed(
|
|
"/api/v1/analytics/events",
|
|
r#"{"event_type":"view","duration_secs":5.0}"#,
|
|
&editor_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response_body(response).await;
|
|
assert_eq!(body["recorded"], true);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_transcode_session_not_found() {
|
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(get_authed(
|
|
"/api/v1/transcode/00000000-0000-0000-0000-000000000000",
|
|
&viewer_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
// Should be 404 or 500 (not found in DB)
|
|
assert!(
|
|
response.status() == StatusCode::NOT_FOUND
|
|
|| response.status() == StatusCode::INTERNAL_SERVER_ERROR
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_transcode_list_empty() {
|
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(get_authed("/api/v1/transcode", &viewer_token))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
let body = response_body(response).await;
|
|
assert!(body.as_array().unwrap().is_empty());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_hls_segment_no_session() {
|
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(get_authed(
|
|
"/api/v1/media/00000000-0000-0000-0000-000000000000/stream/hls/720p/\
|
|
segment0.ts",
|
|
&viewer_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
// Should fail because media doesn't exist or no transcode session
|
|
assert!(
|
|
response.status() == StatusCode::BAD_REQUEST
|
|
|| response.status() == StatusCode::NOT_FOUND
|
|
|| response.status() == StatusCode::INTERNAL_SERVER_ERROR
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_subtitles_list() {
|
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
|
|
|
// Should return empty for nonexistent media (or not found)
|
|
let response = app
|
|
.oneshot(get_authed(
|
|
"/api/v1/media/00000000-0000-0000-0000-000000000000/subtitles",
|
|
&viewer_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert!(
|
|
response.status() == StatusCode::OK
|
|
|| response.status() == StatusCode::NOT_FOUND
|
|
|| response.status() == StatusCode::INTERNAL_SERVER_ERROR
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_health_public() {
|
|
let (app, ..) = setup_app_with_auth().await;
|
|
|
|
// Health endpoint should be accessible without auth even when accounts
|
|
// enabled
|
|
let response = app.oneshot(get("/api/v1/health")).await.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_invalid_uuid_in_path() {
|
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
|
|
|
let response = app
|
|
.oneshot(get_authed("/api/v1/media/not-a-uuid", &viewer_token))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_oversized_comment() {
|
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
|
|
|
let long_text: String = "x".repeat(10_001);
|
|
let body = format!(r#"{{"text":"{long_text}"}}"#);
|
|
let response = app
|
|
.oneshot(post_json_authed(
|
|
"/api/v1/media/00000000-0000-0000-0000-000000000000/comments",
|
|
&body,
|
|
&editor_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_share_link_expired() {
|
|
// Uses no-auth setup since share links are complex to test with auth
|
|
// (need real media items). Verify the expire check logic works.
|
|
let app = setup_app().await;
|
|
|
|
// First import a dummy file to get a media_id, but we can't without a real
|
|
// file. So let's test the public share access endpoint with a nonexistent
|
|
// token.
|
|
let response = app
|
|
.oneshot(get("/api/v1/s/nonexistent_token"))
|
|
.await
|
|
.unwrap();
|
|
// Should fail with not found or internal error (no such share link)
|
|
assert!(
|
|
response.status() == StatusCode::NOT_FOUND
|
|
|| response.status() == StatusCode::INTERNAL_SERVER_ERROR
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_update_sync_device_requires_editor() {
|
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
|
let fake_id = uuid::Uuid::now_v7();
|
|
let response = app
|
|
.clone()
|
|
.oneshot(put_json_authed(
|
|
&format!("/api/v1/sync/devices/{fake_id}"),
|
|
r#"{"name":"renamed"}"#,
|
|
&viewer_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
|
}
|