Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ief16a2b3181bfa50193fb69a5ad4a9166a6a6964
234 lines
6 KiB
Rust
234 lines
6 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/users (admin)
|
|
|
|
#[tokio::test]
|
|
async fn list_users_requires_admin() {
|
|
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 list_users_viewer_forbidden() {
|
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
|
let response = app
|
|
.oneshot(get_authed("/api/v1/users", &viewer_token))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn list_users_admin_ok() {
|
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
|
let response = app
|
|
.oneshot(get_authed("/api/v1/users", &admin_token))
|
|
.await
|
|
.unwrap();
|
|
let status = response.status();
|
|
let body = response_body(response).await;
|
|
assert_eq!(status, StatusCode::OK);
|
|
let users = body.as_array().expect("users is array");
|
|
// setup_app_with_auth seeds three users
|
|
assert_eq!(users.len(), 3);
|
|
}
|
|
|
|
// POST /api/v1/users (admin)
|
|
|
|
#[tokio::test]
|
|
async fn create_user_requires_admin() {
|
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
|
let response = app
|
|
.oneshot(post_json_authed(
|
|
"/api/v1/users",
|
|
r#"{"username":"newuser","password":"password123","role":"viewer"}"#,
|
|
&editor_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn create_user_admin_ok() {
|
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
|
let response = app
|
|
.oneshot(post_json_authed(
|
|
"/api/v1/users",
|
|
r#"{"username":"newuser","password":"password123","role":"viewer"}"#,
|
|
&admin_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
let status = response.status();
|
|
let body = response_body(response).await;
|
|
assert!(
|
|
status == StatusCode::OK || status == StatusCode::CREATED,
|
|
"unexpected status: {status}"
|
|
);
|
|
assert!(body["id"].is_string(), "expected id field, got: {body}");
|
|
assert_eq!(body["username"].as_str().unwrap(), "newuser");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn create_user_duplicate_username() {
|
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
|
// "admin" already exists
|
|
let response = app
|
|
.oneshot(post_json_authed(
|
|
"/api/v1/users",
|
|
r#"{"username":"admin","password":"password123","role":"viewer"}"#,
|
|
&admin_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::CONFLICT);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn create_user_password_too_short() {
|
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
|
let response = app
|
|
.oneshot(post_json_authed(
|
|
"/api/v1/users",
|
|
r#"{"username":"shortpass","password":"short","role":"viewer"}"#,
|
|
&admin_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
// Password minimum is 8 chars; should be rejected
|
|
assert!(
|
|
response.status() == StatusCode::BAD_REQUEST
|
|
|| response.status() == StatusCode::UNPROCESSABLE_ENTITY
|
|
);
|
|
}
|
|
|
|
// GET /api/v1/users/{id} (admin)
|
|
|
|
#[tokio::test]
|
|
async fn get_user_requires_admin() {
|
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
|
let response = app
|
|
.oneshot(get_authed(
|
|
"/api/v1/users/00000000-0000-0000-0000-000000000000",
|
|
&viewer_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn get_user_not_found() {
|
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
|
let response = app
|
|
.oneshot(get_authed(
|
|
"/api/v1/users/00000000-0000-0000-0000-000000000000",
|
|
&admin_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
// PATCH /api/v1/users/{id} (admin)
|
|
|
|
#[tokio::test]
|
|
async fn update_user_requires_admin() {
|
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
|
let response = app
|
|
.oneshot(patch_json_authed(
|
|
"/api/v1/users/00000000-0000-0000-0000-000000000000",
|
|
r#"{"role":"editor"}"#,
|
|
&viewer_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
|
}
|
|
|
|
// DELETE /api/v1/users/{id} (admin)
|
|
|
|
#[tokio::test]
|
|
async fn delete_user_requires_admin() {
|
|
let (app, _, _, viewer_token) = setup_app_with_auth().await;
|
|
let response = app
|
|
.oneshot(delete_authed(
|
|
"/api/v1/users/00000000-0000-0000-0000-000000000000",
|
|
&viewer_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn delete_user_not_found() {
|
|
let (app, admin_token, ..) = setup_app_with_auth().await;
|
|
let response = app
|
|
.oneshot(delete_authed(
|
|
"/api/v1/users/00000000-0000-0000-0000-000000000000",
|
|
&admin_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
// GET /api/v1/users/{id}/libraries (admin)
|
|
|
|
#[tokio::test]
|
|
async fn get_user_libraries_requires_admin() {
|
|
let (app, _, editor_token, _) = setup_app_with_auth().await;
|
|
let response = app
|
|
.oneshot(get_authed(
|
|
"/api/v1/users/00000000-0000-0000-0000-000000000000/libraries",
|
|
&editor_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
|
}
|
|
|
|
// PUT coverage
|
|
|
|
#[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":"device"}"#,
|
|
&viewer_token,
|
|
))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(response.status(), StatusCode::FORBIDDEN);
|
|
}
|
|
|
|
// No-auth coverage (exercises setup_app and get helpers)
|
|
|
|
#[tokio::test]
|
|
async fn media_list_no_auth_users_file() {
|
|
let app = setup_app().await;
|
|
let response = app.oneshot(get("/api/v1/media")).await.unwrap();
|
|
assert_eq!(response.status(), StatusCode::OK);
|
|
}
|