diff --git a/tests/common/logging.rs b/tests/common/logging.rs index 71be2ece7..d69be2a94 100644 --- a/tests/common/logging.rs +++ b/tests/common/logging.rs @@ -10,9 +10,11 @@ //! ยดยดยด //! -use std::sync::Once; +use std::io; +use std::sync::{Arc, Mutex, Once}; use tracing::level_filters::LevelFilter; +use tracing_subscriber::fmt::MakeWriter; #[allow(dead_code)] pub static INIT: Once = Once::new(); @@ -28,3 +30,94 @@ pub fn tracing_stderr_init(filter: LevelFilter) { tracing::info!("Logging initialized"); } + +#[allow(dead_code)] +pub fn tracing_init_with_capturer(filter: LevelFilter, log_capturer: Arc>) { + let writer = LogCapturerWrapper::new(log_capturer); + + let builder = tracing_subscriber::fmt() + .with_max_level(filter) + .with_ansi(true) + .with_writer(writer); + + builder.pretty().with_file(true).init(); + + tracing::info!("Logging initialized"); +} + +pub struct LogCapturerWrapper { + inner: Arc>, +} + +impl LogCapturerWrapper { + pub fn new(inner: Arc>) -> Self { + Self { inner } + } +} + +impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for LogCapturerWrapper { + type Writer = LogCapturerGuard; + + fn make_writer(&'a self) -> Self::Writer { + LogCapturerGuard { + inner: self.inner.clone(), + } + } +} + +pub struct LogCapturerGuard { + inner: Arc>, +} + +impl io::Write for LogCapturerGuard { + fn write(&mut self, buf: &[u8]) -> io::Result { + let mut capturer = self.inner.lock().unwrap(); + capturer.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + let mut capturer = self.inner.lock().unwrap(); + capturer.flush() + } +} + +#[derive(Debug, Default)] +pub struct LogCapturer { + output: String, +} + +impl LogCapturer { + pub fn new() -> Self { + Self::default() + } + + pub fn contains(&self, message: &str) -> bool { + self.output.contains(message) + } +} + +impl io::Write for LogCapturer { + fn write(&mut self, buf: &[u8]) -> io::Result { + let out_str = String::from_utf8_lossy(buf); + + // We print to stdout so that the output is visible in the terminal + // when you run the tests with `cargo test -- --nocapture`. + println!("{out_str}"); + + self.output.push_str(&out_str); + + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl<'a> MakeWriter<'a> for LogCapturer { + type Writer = Self; + + fn make_writer(&'a self) -> Self::Writer { + Self::default() + } +} diff --git a/tests/servers/api/v1/contract/context/auth_key.rs b/tests/servers/api/v1/contract/context/auth_key.rs index 2792a513c..00a143495 100644 --- a/tests/servers/api/v1/contract/context/auth_key.rs +++ b/tests/servers/api/v1/contract/context/auth_key.rs @@ -1,3 +1,4 @@ +use std::sync::{Arc, Mutex}; use std::time::Duration; use serde::Serialize; @@ -5,7 +6,7 @@ use torrust_tracker::core::auth::Key; use torrust_tracker_test_helpers::configuration; use tracing::level_filters::LevelFilter; -use crate::common::logging::{tracing_stderr_init, INIT}; +use crate::common::logging::{tracing_init_with_capturer, tracing_stderr_init, LogCapturer, INIT}; use crate::servers::api::connection_info::{connection_with_invalid_token, connection_with_no_token}; use crate::servers::api::v1::asserts::{ assert_auth_key_utf8, assert_failed_to_delete_key, assert_failed_to_generate_key, assert_failed_to_reload_keys, @@ -250,8 +251,10 @@ async fn should_fail_deleting_an_auth_key_when_the_key_id_is_invalid() { #[tokio::test] async fn should_fail_when_the_auth_key_cannot_be_deleted() { + let log_capturer = Arc::new(Mutex::new(LogCapturer::new())); + INIT.call_once(|| { - tracing_stderr_init(LevelFilter::ERROR); + tracing_init_with_capturer(LevelFilter::ERROR, log_capturer.clone()); }); let env = Started::new(&configuration::ephemeral().into()).await; @@ -271,6 +274,11 @@ async fn should_fail_when_the_auth_key_cannot_be_deleted() { assert_failed_to_delete_key(response).await; + // We expect to see a 500 error, it's the current API behavior + assert!(log_capturer.lock().unwrap().contains("ERROR")); + assert!(log_capturer.lock().unwrap().contains("tower_http")); + assert!(log_capturer.lock().unwrap().contains("500 Internal Server Error")); + env.stop().await; }