use actix_web::{test, web, App, HttpResponse};
use solid_pod_rs_server::ErrorLoggingMiddleware;
#[actix_web::test]
async fn middleware_passthrough_on_2xx() {
async fn ok() -> HttpResponse {
HttpResponse::Ok().body("hello")
}
let app = test::init_service(
App::new()
.wrap(ErrorLoggingMiddleware)
.route("/ok", web::get().to(ok)),
)
.await;
let req = test::TestRequest::get().uri("/ok").to_request();
let rsp = test::call_service(&app, req).await;
assert!(rsp.status().is_success());
let body = test::read_body(rsp).await;
assert_eq!(body.as_ref(), b"hello", "middleware must not mutate body");
}
use std::io::Write;
use std::sync::{Arc, Mutex};
#[derive(Clone, Default)]
struct SharedBuffer(Arc<Mutex<Vec<u8>>>);
impl Write for SharedBuffer {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.0.lock().unwrap().extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for SharedBuffer {
type Writer = SharedBuffer;
fn make_writer(&'a self) -> Self::Writer {
self.clone()
}
}
#[actix_web::test]
async fn middleware_logs_5xx_with_full_chain() {
use tracing::dispatcher;
use tracing_subscriber::fmt::Subscriber;
use tracing_subscriber::EnvFilter;
async fn boom() -> Result<HttpResponse, actix_web::Error> {
Err(actix_web::error::ErrorInternalServerError(
"backend: simulated failure",
))
}
let buf = SharedBuffer::default();
let subscriber = Subscriber::builder()
.with_env_filter(EnvFilter::new("error"))
.with_writer(buf.clone())
.without_time()
.finish();
let dispatch = dispatcher::Dispatch::new(subscriber);
dispatcher::with_default(&dispatch, || {
});
let _guard = dispatcher::set_default(&dispatch);
let app = test::init_service(
App::new()
.wrap(ErrorLoggingMiddleware)
.route("/boom", web::get().to(boom)),
)
.await;
let req = test::TestRequest::get().uri("/boom").to_request();
let rsp = test::call_service(&app, req).await;
assert_eq!(rsp.status().as_u16(), 500);
let captured = {
let locked = buf.0.lock().unwrap();
String::from_utf8_lossy(&locked).to_string()
};
assert!(
captured.contains("5xx response"),
"5xx log marker missing, got: {captured}"
);
assert!(
captured.contains("/boom"),
"path missing from log line, got: {captured}"
);
assert!(
captured.contains("simulated failure"),
"inner error chain missing, got: {captured}"
);
}
#[actix_web::test]
async fn middleware_does_not_log_4xx() {
async fn not_found() -> HttpResponse {
HttpResponse::NotFound().finish()
}
let app = test::init_service(
App::new()
.wrap(ErrorLoggingMiddleware)
.route("/missing", web::get().to(not_found)),
)
.await;
let req = test::TestRequest::get().uri("/missing").to_request();
let rsp = test::call_service(&app, req).await;
assert_eq!(rsp.status().as_u16(), 404);
}