diff --git a/Cargo.lock b/Cargo.lock index 703d5ec..7098b88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -852,6 +852,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 = "dtoa" version = "1.0.10" @@ -1000,6 +1006,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 = "frontend" version = "0.1.0" @@ -1971,6 +1983,32 @@ dependencies = [ "windows-sys 0.52.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 = "new_debug_unreachable" version = "1.0.6" @@ -2303,6 +2341,32 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[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 = "pretty_assertions" version = "1.4.1" @@ -2464,6 +2528,7 @@ dependencies = [ "cookie", "itertools", "lettre", + "mockall", "rand 0.9.1", "rand_core 0.9.3", "ron", @@ -3308,6 +3373,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 = "thiserror" version = "1.0.69" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 7486cf3..48f85b1 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -56,3 +56,4 @@ thiserror = "2" axum-test = "17" cookie = "0.18" scraper = "0.23" +mockall = "0.13" diff --git a/backend/tests/http.rs b/backend/tests/http.rs index 7db1e7c..2b3e56a 100644 --- a/backend/tests/http.rs +++ b/backend/tests/http.rs @@ -1,10 +1,11 @@ -use std::error::Error; +use std::{error::Error, sync::Arc}; use axum_test::TestServer; use cookie::Cookie; +use mockall::predicate; use scraper::{ElementRef, Html, Selector}; -use recipes::app; +use recipes::{app, email}; use serde::Serialize; mod utils; @@ -104,7 +105,18 @@ pub struct SignUpFormData { #[tokio::test] async fn sign_up() -> Result<(), Box> { // Arrange. - let state = utils::common_state().await?; + let mut mock_email_service = utils::mock_email::MockEmailService::new(); + mock_email_service + .expect_send_email() + .with( + predicate::eq("president@spaceball.planet"), + predicate::always(), + predicate::always(), + ) + .times(1) + .returning(|_email, _title, _message| Ok(())); + + let state = utils::common_state_with_email_service(Arc::new(mock_email_service)).await?; let server = TestServer::new(app::make_service(state))?; // Act. @@ -122,7 +134,6 @@ async fn sign_up() -> Result<(), Box> { let document = Html::parse_document(&response.text()); assert_eq!(document.errors.len(), 0); - dbg!(response); Ok(()) } diff --git a/backend/tests/utils/mock_email.rs b/backend/tests/utils/mock_email.rs index 0f5e074..5e9debe 100644 --- a/backend/tests/utils/mock_email.rs +++ b/backend/tests/utils/mock_email.rs @@ -1,23 +1,20 @@ use std::sync::Arc; -use recipes::email; +use async_trait::async_trait; +use mockall::{mock, predicate::*}; -pub struct MockEmailService; +use crate::email; -impl MockEmailService { - pub fn create_service() -> Arc { - Arc::new(Self {}) +mock! { + pub EmailService {} + #[async_trait] + impl email::EmailServiceTrait for EmailService { + async fn send_email(&self, email: &str, title: &str, message: &str) + -> Result<(), email::Error>; } } -#[async_trait::async_trait] -impl email::EmailServiceTrait for MockEmailService { - async fn send_email( - &self, - _email: &str, - _title: &str, - _message: &str, - ) -> Result<(), email::Error> { - Ok(()) - } +// Default email service: will crash if `send_email` method is called. +pub fn new_mock_email_service() -> Arc { + Arc::new(MockEmailService::new()) } diff --git a/backend/tests/utils/mod.rs b/backend/tests/utils/mod.rs index 213fef4..3c209ab 100644 --- a/backend/tests/utils/mod.rs +++ b/backend/tests/utils/mod.rs @@ -1,10 +1,16 @@ -use std::error::Error; +use std::{error::Error, sync::Arc}; -use recipes::{app, config, data::db, log}; +use recipes::{app, config, data::db, email, log}; -mod mock_email; +pub mod mock_email; pub async fn common_state() -> Result> { + common_state_with_email_service(mock_email::new_mock_email_service()).await +} + +pub async fn common_state_with_email_service( + email_service: Arc, +) -> Result> { let db_connection = db::Connection::new_in_memory().await?; let config = config::Config::default(); let log = log::Log::new_no_log(); @@ -12,7 +18,7 @@ pub async fn common_state() -> Result> { config, db_connection, log, - email_service: mock_email::MockEmailService::create_service(), + email_service, }) }