//! PR 4 (feat/admin-system-api) integration tests. //! //! Shape-only assertions — we don't mock the system, just call the //! endpoint and check the response envelope. Threshold-triggering of //! alerts would require faking statvfs / sysinfo, which is more //! plumbing than the test gives back. mod common; use axum::http::StatusCode; use axum::Router; use sqlx::PgPool; use tower::ServiceExt; use mangalord::repo; async fn seed_admin(pool: &PgPool, app: &Router) -> String { let (username, cookie) = common::register_user(app).await; let u = repo::user::find_by_username(pool, &username) .await .unwrap() .unwrap(); repo::user::set_is_admin(pool, u.id, true).await.unwrap(); cookie } #[sqlx::test(migrations = "./migrations")] async fn requires_admin(pool: PgPool) { let h = common::harness(pool); let (_u, cookie) = common::register_user(&h.app).await; let resp = h .app .oneshot(common::get_with_cookie("/api/v1/admin/system", &cookie)) .await .unwrap(); assert_eq!(resp.status(), StatusCode::FORBIDDEN); } #[sqlx::test(migrations = "./migrations")] async fn unauthenticated_request_is_rejected(pool: PgPool) { let h = common::harness(pool); let resp = h .app .oneshot(common::get("/api/v1/admin/system")) .await .unwrap(); assert_eq!(resp.status(), StatusCode::UNAUTHORIZED); } #[sqlx::test(migrations = "./migrations")] async fn returns_disk_memory_cpu_alerts_shape(pool: PgPool) { let h = common::harness(pool.clone()); let cookie = seed_admin(&pool, &h.app).await; let resp = h .app .oneshot(common::get_with_cookie("/api/v1/admin/system", &cookie)) .await .unwrap(); assert_eq!(resp.status(), StatusCode::OK); let body = common::body_json(resp).await; // Disk: harness uses LocalStorage on a tempdir, so disk SHOULD be // populated. Validate the field shape and percent range. let disk = body .get("disk") .expect("disk key present") .as_object() .expect("disk is an object (LocalStorage exposes a path)"); assert!(disk["total_bytes"].as_u64().unwrap() > 0); let pct = disk["percent_used"].as_f64().unwrap(); assert!( (0.0..=100.0).contains(&pct), "percent_used outside [0,100]: {pct}" ); let mem = body.get("memory").expect("memory key").as_object().unwrap(); assert!(mem["total_bytes"].as_u64().unwrap() > 0); let mpct = mem["percent_used"].as_f64().unwrap(); assert!((0.0..=100.0).contains(&mpct)); let cpu = body.get("cpu").expect("cpu key").as_object().unwrap(); let cpu_pct = cpu["percent_used"].as_f64().unwrap(); assert!( (0.0..=100.0).contains(&cpu_pct), "cpu out of range: {cpu_pct}" ); let alerts = body.get("alerts").expect("alerts key").as_array().unwrap(); // Don't assert on length — the box may genuinely be >90% on memory // when the test runs. Just confirm shape of any present entry. for alert in alerts { assert!(alert["level"].is_string()); assert!(alert["message"].is_string()); } }