Files
Infrastruktur-Monitoring-Sy…/backend/crates/api/tests/users.rs
Dennis Nemec 83aa500f5d
Some checks failed
CI / backend (push) Has been cancelled
CI / frontend (push) Has been cancelled
CI / ui (push) Has been cancelled
WP-01: domain contract and failing tests for auth and user management
Domain entities/ports, service stubs, 18 application unit tests with in-memory
fakes, API integration tests for /api/auth and /api/users, Vitest specs for the
auth store, login page and user form, Playwright auth/user-management flow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 21:31:59 +02:00

141 lines
4.4 KiB
Rust

//! WP-01: /api/users routes, admin only.
mod common;
use axum::http::StatusCode;
use common::{get, patch, post, test_app_with_admin};
use serde_json::json;
const ADMIN: &str = "admin@example.com";
const PW: &str = "admin-password-123";
fn new_user(email: &str) -> serde_json::Value {
json!({"email": email, "display_name": "Test User", "password": "user-password-123", "role": "user"})
}
#[tokio::test]
async fn admin_can_create_list_get_and_update_users() {
let app = test_app_with_admin().await;
let token = common::login(&app, ADMIN, PW).await.access;
let res = post(&app, "/api/users", new_user("u@x.de"), Some(&token)).await;
assert_eq!(res.status, StatusCode::CREATED, "{}", res.json);
let id = res.json["id"].as_str().unwrap().to_string();
assert_eq!(res.json["role"], "user");
assert_eq!(res.json["is_active"], true);
assert!(res.json.get("password_hash").is_none());
let list = get(&app, "/api/users", Some(&token)).await;
assert_eq!(list.json.as_array().unwrap().len(), 2);
let one = get(&app, &format!("/api/users/{id}"), Some(&token)).await;
assert_eq!(one.json["email"], "u@x.de");
let upd = patch(
&app,
&format!("/api/users/{id}"),
json!({"display_name": "Renamed", "role": "admin"}),
Some(&token),
)
.await;
assert_eq!(upd.status, StatusCode::OK);
assert_eq!(upd.json["display_name"], "Renamed");
assert_eq!(upd.json["role"], "admin");
}
#[tokio::test]
async fn validation_and_conflict_errors_are_mapped() {
let app = test_app_with_admin().await;
let token = common::login(&app, ADMIN, PW).await.access;
let res = post(&app, "/api/users", json!({"email": "bad", "display_name": "x", "password": "user-password-123", "role": "user"}), Some(&token)).await;
assert_eq!(res.status, StatusCode::UNPROCESSABLE_ENTITY);
assert_eq!(res.json["error"], "validation");
let res = post(&app, "/api/users", new_user(ADMIN), Some(&token)).await;
assert_eq!(res.status, StatusCode::CONFLICT);
assert_eq!(res.json["error"], "email_taken");
let res = get(
&app,
"/api/users/00000000-0000-0000-0000-000000000000",
Some(&token),
)
.await;
assert_eq!(res.status, StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn last_admin_cannot_be_deactivated() {
let app = test_app_with_admin().await;
let login = common::login(&app, ADMIN, PW).await;
let res = patch(
&app,
&format!("/api/users/{}", login.user_id),
json!({"is_active": false}),
Some(&login.access),
)
.await;
assert_eq!(res.status, StatusCode::CONFLICT);
assert_eq!(res.json["error"], "last_admin");
}
#[tokio::test]
async fn admin_can_reset_password_and_user_can_login_with_it() {
let app = test_app_with_admin().await;
let token = common::login(&app, ADMIN, PW).await.access;
let id = post(&app, "/api/users", new_user("u@x.de"), Some(&token))
.await
.json["id"]
.as_str()
.unwrap()
.to_string();
let res = post(
&app,
&format!("/api/users/{id}/password"),
json!({"password": "brand-new-password"}),
Some(&token),
)
.await;
assert_eq!(res.status, StatusCode::NO_CONTENT);
let res = post(
&app,
"/api/auth/login",
json!({"email": "u@x.de", "password": "brand-new-password"}),
None,
)
.await;
assert_eq!(res.status, StatusCode::OK);
}
#[tokio::test]
async fn non_admin_gets_403_on_user_management() {
let app = test_app_with_admin().await;
let admin = common::login(&app, ADMIN, PW).await.access;
post(&app, "/api/users", new_user("u@x.de"), Some(&admin)).await;
let user = common::login(&app, "u@x.de", "user-password-123")
.await
.access;
assert_eq!(
get(&app, "/api/users", Some(&user)).await.status,
StatusCode::FORBIDDEN
);
assert_eq!(
post(&app, "/api/users", new_user("v@x.de"), Some(&user))
.await
.status,
StatusCode::FORBIDDEN
);
// but the user can see themself
assert_eq!(
get(&app, "/api/auth/me", Some(&user)).await.json["email"],
"u@x.de"
);
}
#[tokio::test]
async fn unauthenticated_requests_are_401() {
let app = test_app_with_admin().await;
assert_eq!(
get(&app, "/api/users", None).await.status,
StatusCode::UNAUTHORIZED
);
}