use axum::http::{header, StatusCode};
use axum::response::IntoResponse;
use larust_core::{AppError, Application};
fn enable_debug_mode() {
static INIT: std::sync::Once = std::sync::Once::new();
INIT.call_once(|| {
std::env::set_var("APP_DEBUG", "true");
fn config() -> serde_json::Value {
let mut value = serde_json::json!({});
if let Ok(debug) = std::env::var("APP_DEBUG")
.unwrap_or_default()
.parse::<bool>()
{
value["app_debug"] = serde_json::json!(debug);
}
value
}
Application::new(config).unwrap();
});
}
async fn body_text(response: axum::response::Response) -> String {
let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.unwrap();
String::from_utf8(bytes.to_vec()).unwrap()
}
#[derive(Debug)]
struct Root;
impl std::fmt::Display for Root {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "root cause: connection refused")
}
}
impl std::error::Error for Root {}
#[derive(Debug)]
struct Wrapper(Root);
impl std::fmt::Display for Wrapper {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "query failed")
}
}
impl std::error::Error for Wrapper {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.0)
}
}
#[tokio::test]
async fn internal_error_renders_the_full_source_chain_as_html() {
enable_debug_mode();
let error = AppError::Internal(Box::new(Wrapper(Root)));
let response = error.into_response();
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
assert_eq!(
response
.headers()
.get(header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok()),
Some("text/html; charset=utf-8")
);
let body = body_text(response).await;
assert!(body.contains("internal server error: query failed"));
assert!(body.contains("root cause: connection refused"));
assert!(body.contains("Caused by"));
}
#[tokio::test]
async fn not_found_renders_a_branded_html_page_in_debug_mode() {
enable_debug_mode();
let response = AppError::NotFound.into_response();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
let body = body_text(response).await;
assert!(body.contains("Not Found"));
assert!(body.contains("<html"));
}
#[tokio::test]
async fn http_variant_still_returns_the_caller_supplied_message_verbatim() {
enable_debug_mode();
let response = AppError::Http {
status: StatusCode::IM_A_TEAPOT,
message: "short and steamy".to_string(),
}
.into_response();
assert_eq!(response.status(), StatusCode::IM_A_TEAPOT);
assert_eq!(body_text(response).await, "short and steamy");
}