A lexicon-driven AppView for ATProto.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183mod common;
use axum::body::Body;use axum::http::{Request, StatusCode};use happyview::db::DatabaseBackend;use http_body_util::BodyExt;use serde_json::Value;use serial_test::serial;use tower::ServiceExt;
use common::app::TestApp;
async fn json_body(resp: axum::response::Response) -> Value { let bytes = resp.into_body().collect().await.unwrap().to_bytes(); serde_json::from_slice(&bytes).unwrap()}
#[tokio::test]#[serial]async fn status_reports_backend_and_vacuum_state() { common::require_db!(); let app = TestApp::new().await;
let resp = app .router .clone() .oneshot( Request::builder() .uri("/admin/database/status") .header(app.admin_cookie().0, app.admin_cookie().1) .body(Body::empty()) .unwrap(), ) .await .unwrap();
assert_eq!(resp.status(), StatusCode::OK); let body = json_body(resp).await; assert!(body["backend"].is_string()); assert!(body["vacuum"].is_object()); assert!( body["journal_size_limit"].is_number(), "journal_size_limit missing or not a number: {body}" );
// `disk`/`feasibility` are only meaningful for a file-backed SQLite // database (`disk::report` returns `None` for any other backend, which // serializes to JSON `null`). Asserting the full shape here — rather than // just `is_object()` on the whole body — is what would catch a future // `unwrap_or(0)` on `db_fs_free`/`temp_fs_free`: that regression would // turn a real disk's free space into a suspicious literal `0` rather than // leaving it `null` or a genuine (non-zero, on any real test host) count. if app.state.db_backend == DatabaseBackend::Sqlite { assert!(body["disk"].is_object(), "disk missing: {body}"); let disk = &body["disk"]; assert!( disk["db_fs_free"].is_u64() || disk["db_fs_free"].is_null(), "db_fs_free must be a number or null, never a stand-in like 0 \ collapsed from `unwrap_or(0)`: {body}" ); assert!( disk["temp_fs_free"].is_u64() || disk["temp_fs_free"].is_null(), "temp_fs_free must be a number or null: {body}" ); assert!( disk["same_filesystem"].is_boolean(), "same_filesystem missing or not a bool: {body}" ); assert!( body["feasibility"]["status"].is_string(), "feasibility.status missing: {body}" ); } else { assert!( body["disk"].is_null(), "disk should be null on a non-SQLite backend: {body}" ); assert!( body["feasibility"].is_null(), "feasibility should be null on a non-SQLite backend: {body}" ); }}
#[tokio::test]#[serial]async fn scheduling_and_cancelling_a_vacuum_round_trips() { common::require_db!(); let app = TestApp::new().await;
let post = |uri: &str, method: &str| { Request::builder() .method(method) .uri(uri.to_string()) .header(app.admin_cookie().0, app.admin_cookie().1) .body(Body::empty()) .unwrap() };
let resp = app .router .clone() .oneshot(post("/admin/database/vacuum/schedule", "POST")) .await .unwrap();
// VACUUM is a SQLite-only operation, so scheduling it on Postgres is // refused outright rather than silently armed-but-inert: `status` would // otherwise report "scheduled" forever since nothing ever runs it. if app.state.db_backend != DatabaseBackend::Sqlite { assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let resp = app .router .clone() .oneshot(post("/admin/database/status", "GET")) .await .unwrap(); assert_eq!(resp.status(), StatusCode::OK); let body = json_body(resp).await; assert!( body["vacuum"]["requested_at"].is_null(), "should not have armed on a non-SQLite backend: {body}" ); return; }
assert_eq!(resp.status(), StatusCode::OK);
let resp = app .router .clone() .oneshot(post("/admin/database/status", "GET")) .await .unwrap(); let body = json_body(resp).await; assert!( body["vacuum"]["requested_at"].is_string(), "not armed: {body}" );
let resp = app .router .clone() .oneshot(post("/admin/database/vacuum/schedule", "DELETE")) .await .unwrap(); assert_eq!(resp.status(), StatusCode::OK);
let resp = app .router .clone() .oneshot(post("/admin/database/status", "GET")) .await .unwrap(); let body = json_body(resp).await; assert!( body["vacuum"]["requested_at"].is_null(), "not disarmed: {body}" );}
#[tokio::test]#[serial]async fn status_requires_auth() { common::require_db!(); let app = TestApp::new().await;
let resp = app .router .clone() .oneshot( Request::builder() .uri("/admin/database/status") .body(Body::empty()) .unwrap(), ) .await .unwrap();
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);}