mirror of
https://github.com/perstarkse/minne.git
synced 2026-05-31 03:40:38 +02:00
chore: harden api-router errors and add router integration tests while slimming html handlers.
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
#![allow(clippy::expect_used)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use api_router::{api_routes_v1, api_state::ApiState};
|
||||
use axum::{
|
||||
body::{to_bytes, Body},
|
||||
http::{Request, StatusCode},
|
||||
Router,
|
||||
};
|
||||
use common::{
|
||||
storage::{
|
||||
db::SurrealDbClient,
|
||||
store::StorageManager,
|
||||
types::user::User,
|
||||
},
|
||||
utils::config::{AppConfig, StorageKind},
|
||||
};
|
||||
use tower::ServiceExt;
|
||||
|
||||
async fn build_test_app() -> (Router, Arc<SurrealDbClient>) {
|
||||
let namespace = "api_router_test";
|
||||
let database = uuid::Uuid::new_v4().to_string();
|
||||
let db = Arc::new(
|
||||
SurrealDbClient::memory(namespace, &database)
|
||||
.await
|
||||
.expect("in-memory db"),
|
||||
);
|
||||
db.apply_migrations()
|
||||
.await
|
||||
.expect("migrations should apply");
|
||||
|
||||
let config = AppConfig {
|
||||
storage: StorageKind::Memory,
|
||||
..Default::default()
|
||||
};
|
||||
let storage = StorageManager::new(&config)
|
||||
.await
|
||||
.expect("storage manager");
|
||||
|
||||
let state = ApiState {
|
||||
db: Arc::clone(&db),
|
||||
config,
|
||||
storage,
|
||||
};
|
||||
|
||||
let router = api_routes_v1(&state).with_state(state);
|
||||
|
||||
(router, db)
|
||||
}
|
||||
|
||||
async fn response_body(response: axum::response::Response) -> String {
|
||||
let body = to_bytes(response.into_body(), usize::MAX)
|
||||
.await
|
||||
.expect("response body");
|
||||
String::from_utf8(body.to_vec()).expect("utf-8 body")
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn live_probe_is_public() {
|
||||
let (app, _db) = build_test_app().await;
|
||||
|
||||
let response = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.uri("/live")
|
||||
.body(Body::empty())
|
||||
.expect("live request"),
|
||||
)
|
||||
.await
|
||||
.expect("live response");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert!(response_body(response).await.contains("\"status\":\"ok\""));
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn ready_probe_is_public_and_reports_db_ok() {
|
||||
let (app, _db) = build_test_app().await;
|
||||
|
||||
let response = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.uri("/ready")
|
||||
.body(Body::empty())
|
||||
.expect("ready request"),
|
||||
)
|
||||
.await
|
||||
.expect("ready response");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let body = response_body(response).await;
|
||||
assert!(body.contains("\"checks\":{\"db\":\"ok\"}") || body.contains("\"db\":\"ok\""));
|
||||
assert!(!body.contains("reason"));
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn protected_route_requires_api_key() {
|
||||
let (app, _db) = build_test_app().await;
|
||||
|
||||
let response = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.uri("/categories")
|
||||
.body(Body::empty())
|
||||
.expect("categories request"),
|
||||
)
|
||||
.await
|
||||
.expect("categories response");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn protected_route_rejects_invalid_api_key() {
|
||||
let (app, _db) = build_test_app().await;
|
||||
|
||||
let response = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.uri("/categories")
|
||||
.header("X-API-Key", "sk_invalid")
|
||||
.body(Body::empty())
|
||||
.expect("categories request"),
|
||||
)
|
||||
.await
|
||||
.expect("categories response");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn authenticated_user_can_list_categories() {
|
||||
let (app, db) = build_test_app().await;
|
||||
|
||||
let user = User::create_new(
|
||||
"api_router_test@example.com".to_string(),
|
||||
"test_password".to_string(),
|
||||
&db,
|
||||
"UTC".to_string(),
|
||||
"system".to_string(),
|
||||
)
|
||||
.await
|
||||
.expect("test user");
|
||||
|
||||
let api_key = User::set_api_key(&user.id, &db)
|
||||
.await
|
||||
.expect("api key");
|
||||
|
||||
let response = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.uri("/categories")
|
||||
.header("X-API-Key", api_key)
|
||||
.body(Body::empty())
|
||||
.expect("categories request"),
|
||||
)
|
||||
.await
|
||||
.expect("categories response");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
}
|
||||
Reference in New Issue
Block a user