From e7b35953ca0400733d7e7bd351609fc456229b9a Mon Sep 17 00:00:00 2001 From: JeremyLARDENOIS Date: Sat, 9 Aug 2025 22:43:02 +0200 Subject: [PATCH] test(app): add unit tests for app --- Cargo.lock | 77 ++++++++++++++++++++++++++++++ Cargo.toml | 3 ++ src/app/ports/driven.rs | 7 +++ src/app/service.rs | 101 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 3573524..14531fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + [[package]] name = "atomic-waker" version = "1.1.2" @@ -119,6 +125,12 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -180,6 +192,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fragile" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" + [[package]] name = "futures-channel" version = "0.3.31" @@ -618,6 +636,32 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "mockall" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "native-tls" version = "0.2.14" @@ -641,6 +685,7 @@ version = "0.1.0" dependencies = [ "discord_client", "dotenvy", + "mockall", "system_monitor", ] @@ -764,6 +809,32 @@ dependencies = [ "zerovec", ] +[[package]] +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -1110,6 +1181,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + [[package]] name = "tinystr" version = "0.8.1" diff --git a/Cargo.toml b/Cargo.toml index 8cc22d2..f4bbf0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,6 @@ dotenvy = "0.15.7" discord_client = { path = "lib/actors/discord_client" } system_monitor = { path = "lib/actors/system_monitor" } +[dev-dependencies] +mockall = "0.13.1" + diff --git a/src/app/ports/driven.rs b/src/app/ports/driven.rs index ebdb5a5..93c2e11 100644 --- a/src/app/ports/driven.rs +++ b/src/app/ports/driven.rs @@ -1,19 +1,26 @@ use std::collections::HashMap; +#[cfg(test)] +use mockall::automock; + +#[cfg_attr(test, automock)] pub trait ForSendingNotification { fn send_notification(&self, message: &str) -> Result<(), Box>; } +#[cfg_attr(test, automock)] pub trait ForMonitoringSystem { fn check_thresholds(&self) -> Vec>; fn get_metrics(&self) -> HashMap; } +#[cfg_attr(test, automock)] pub trait ForFormattingMessage { fn format_violation(&self, violation: Box) -> String; fn format_summary(&self, metrics: &HashMap) -> String; } +#[cfg_attr(test, automock)] pub trait ForGettingViolationData { fn is_critical(&self) -> bool; fn get_metric_name(&self) -> String; diff --git a/src/app/service.rs b/src/app/service.rs index 7ca1a10..a4c757f 100644 --- a/src/app/service.rs +++ b/src/app/service.rs @@ -40,3 +40,104 @@ impl Service { Ok(()) } } + + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use crate::app::ports::driven::{MockForFormattingMessage, MockForMonitoringSystem, MockForSendingNotification}; + + use super::*; + + #[test] + fn test_alert_on_threshold_violation() { + let mut notifier = MockForSendingNotification::new(); + let mut monitor = MockForMonitoringSystem::new(); + let mut formatter = MockForFormattingMessage::new(); + + // Set up expectations for the mocks + notifier.expect_send_notification().returning(|_| Ok(())); + monitor.expect_check_thresholds().returning(|| vec![]); + monitor.expect_get_metrics().returning(|| HashMap::new()); + formatter.expect_format_violation().returning(|_| "".to_string()); + formatter.expect_format_summary().returning(|_| "".to_string()); + + let service = new( + Box::new(notifier), + Box::new(monitor), + Box::new(formatter), + ); + + assert!(service.alert_on_threshold_violation().is_ok()); + } + + #[test] + fn test_send_all_metrics_notification() { + let mut notifier = MockForSendingNotification::new(); + let mut monitor = MockForMonitoringSystem::new(); + let mut formatter = MockForFormattingMessage::new(); + + // Set up expectations for the mocks + notifier.expect_send_notification().returning(|_| Ok(())); + monitor.expect_check_thresholds().returning(|| vec![]); + monitor.expect_get_metrics().returning(|| HashMap::new()); + formatter.expect_format_violation().returning(|_| "".to_string()); + formatter.expect_format_summary().returning(|_| "".to_string()); + + let service = new( + Box::new(notifier), + Box::new(monitor), + Box::new(formatter), + ); + + assert!(service.send_all_metrics_notification().is_ok()); + } + + #[test] + fn test_alert_on_threshold_violation_handles_send_error() { + let mut notifier = MockForSendingNotification::new(); + let mut monitor = MockForMonitoringSystem::new(); + let mut formatter = MockForFormattingMessage::new(); + + // Mock a single violation to trigger notification + monitor.expect_check_thresholds().returning(|| { + vec![Box::new(crate::app::ports::driven::MockForGettingViolationData::new())] + }); + formatter.expect_format_violation().returning(|_| "Test violation".to_string()); + + // Mock notification failure + notifier.expect_send_notification() + .returning(|_| Err("Network error".into())); + + let service = new( + Box::new(notifier), + Box::new(monitor), + Box::new(formatter), + ); + + assert!(service.alert_on_threshold_violation().is_err()); + } + + #[test] + fn test_send_all_metrics_notification_handles_send_error() { + let mut notifier = MockForSendingNotification::new(); + let mut monitor = MockForMonitoringSystem::new(); + let mut formatter = MockForFormattingMessage::new(); + + monitor.expect_get_metrics().returning(|| HashMap::new()); + formatter.expect_format_summary().returning(|_| "Test summary".to_string()); + + // Mock notification failure + notifier.expect_send_notification() + .returning(|_| Err("Network error".into())); + + let service = new( + Box::new(notifier), + Box::new(monitor), + Box::new(formatter), + ); + + assert!(service.send_all_metrics_notification().is_err()); + } +}