From ebb208baba326e250607a1bdfaaa0763e881c1f6 Mon Sep 17 00:00:00 2001 From: Priec Date: Fri, 19 Jun 2026 10:40:43 +0200 Subject: [PATCH] not working smtp test --- config/test.yaml | 53 ++++++++++++++------ tests/mailer/mod.rs | 1 + tests/mailer/smtp_send.rs | 101 ++++++++++++++++++++++++++++++++++++++ tests/mod.rs | 1 + tests/models/users.rs | 2 + 5 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 tests/mailer/mod.rs create mode 100644 tests/mailer/smtp_send.rs diff --git a/config/test.yaml b/config/test.yaml index aaa5904..2a30133 100644 --- a/config/test.yaml +++ b/config/test.yaml @@ -42,33 +42,56 @@ workers: # Mailer Configuration. +# Defaults keep the whole suite on the in-memory stub mailer. The real-SMTP +# smoke test (tests/mailer/smtp_send.rs) opts in by setting these env vars +# before boot; nothing else in the suite sends real mail. mailer: - stub: true + stub: {{ get_env(name="MAILER_STUB", default="true") }} # SMTP mailer configuration. smtp: # Enable/Disable smtp mailer. - enable: true + enable: {{ get_env(name="SMTP_ENABLE", default="true") }} # SMTP server host. e.x localhost, smtp.gmail.com - host: localhost + host: "{{ get_env(name="SMTP_HOST", default="localhost") }}" # SMTP server port - port: 1025 + port: {{ get_env(name="SMTP_PORT", default="1025") }} # Use secure connection (SSL/TLS). - secure: false - # auth: - # user: - # password: + secure: {{ get_env(name="SMTP_SECURE", default="false") }} + auth: + user: "{{ get_env(name="SMTP_USER", default="") }}" + password: "{{ get_env(name="SMTP_PASSWORD", default="") }}" # Initializers Configuration -# initializers: -# oauth2: -# authorization_code: # Authorization code grant type -# - client_identifier: google # Identifier for the OAuth2 provider. Replace 'google' with your provider's name if different, must be unique within the oauth2 config. -# ... other fields +# OAuth2StoreInitializer requires this block to boot (it builds the client store +# in after_routes). Static, non-secret placeholders: tests never perform a real +# OAuth2 handshake, they just need the store to construct successfully. +initializers: + oauth2: + secret_key: 0123456789012345678901234567890123456789012345678901234567890123 + authorization_code: + - client_identifier: google + client_credentials: + client_id: test-client-id + client_secret: test-client-secret + url_config: + auth_url: https://accounts.google.com/o/oauth2/auth + token_url: https://www.googleapis.com/oauth2/v3/token + redirect_url: http://localhost:5150/api/oauth2/google/callback + profile_url: https://openidconnect.googleapis.com/v1/userinfo + scopes: + - https://www.googleapis.com/auth/userinfo.email + - https://www.googleapis.com/auth/userinfo.profile + cookie_config: + protected_url: http://localhost:5150/ + timeout_seconds: 600 # Database Configuration database: - # Database connection URI - uri: {{ get_env(name="DATABASE_URL", default="postgres://uni_loco_web_user:3@localhost:5432/kompress_eshop_test") }} + # Database connection URI. Pinned to the throwaway test DB and intentionally + # NOT read from `DATABASE_URL`: the app loads `.env` on boot (app.rs + # `load_config`), and this config has `dangerously_recreate: true`, so honoring + # an env override here would let `cargo test` recreate the dev/prod database. + uri: "postgres://uni_loco_web_user:3@localhost:5432/kompress_eshop_test" # When enabled, the sql query will be logged. enable_logging: false # Set the timeout duration when acquiring a connection. diff --git a/tests/mailer/mod.rs b/tests/mailer/mod.rs new file mode 100644 index 0000000..abdc768 --- /dev/null +++ b/tests/mailer/mod.rs @@ -0,0 +1 @@ +mod smtp_send; diff --git a/tests/mailer/smtp_send.rs b/tests/mailer/smtp_send.rs new file mode 100644 index 0000000..2f38dad --- /dev/null +++ b/tests/mailer/smtp_send.rs @@ -0,0 +1,101 @@ +//! Real-SMTP smoke test. +//! +//! Sends an actual email through the live SMTP server using the real +//! `AuthMailer` pipeline (config -> Loco mailer -> templates -> SMTP). The test +//! PASSES when the SMTP server accepts the message (the send returns `Ok`); +//! confirm real delivery by checking the recipient's inbox. +//! +//! It is `#[ignore]`d so it never runs in CI or a normal `cargo test` (it opens +//! a real network connection, uses real credentials, and sends a real email). +//! Run it explicitly, inside `nix develop` so `SMTP_PASSWORD` is present: +//! +//! ```sh +//! nix develop -c cargo test --test mod -- --ignored mailer::smtp_send +//! # optional: choose the recipient (defaults to the address below) +//! MAILER_TEST_TO=you@example.com \ +//! nix develop -c cargo test --test mod -- --ignored mailer::smtp_send +//! ``` + +use kompress_eshop::{ + app::App, + mailers::auth::AuthMailer, + models::users::{Model, RegisterParams}, +}; +use loco_rs::testing::prelude::*; +use sea_orm::IntoActiveModel; +use serial_test::serial; + +// Non-secret production SMTP settings (mirror `.env`). The password is +// intentionally NOT here: it is supplied at runtime via `SMTP_PASSWORD` +// (direnv -> `pass`), and never committed. +const SMTP_HOST: &str = "smtp.kompress.sk"; +const SMTP_PORT: &str = "587"; +const SMTP_USER: &str = "kompres"; +const SMTP_SECURE: &str = "true"; + +// Where the test email is sent. Override with `MAILER_TEST_TO`. +const DEFAULT_RECIPIENT: &str = "filipriec@gmail.com"; + +#[tokio::test] +#[serial] +#[ignore = "sends a real email via live SMTP; run explicitly with --ignored"] +async fn sends_real_email() { + // The actual secret must come from the environment (direnv -> `pass`). + // Fail loudly with guidance rather than silently sending nothing. + let password = std::env::var("SMTP_PASSWORD").unwrap_or_default(); + assert!( + !password.is_empty(), + "SMTP_PASSWORD is not set. Run inside `nix develop` so direnv loads it from `pass`." + ); + + let recipient = + std::env::var("MAILER_TEST_TO").unwrap_or_else(|_| DEFAULT_RECIPIENT.to_string()); + + // Flip the booted context onto the real SMTP transport. `config/test.yaml` + // reads these via `get_env` at boot. We deliberately do NOT load `.env` + // here: it carries `DATABASE_URL`, and `test.yaml` has + // `dangerously_recreate: true`, so loading it would recreate the real DB. + // Leaving `DATABASE_URL` untouched keeps boot on the throwaway test DB. + // + // SAFETY: edition 2024 marks `set_var` as unsafe. This test is `#[serial]`, + // so no other test mutates the process environment concurrently. + unsafe { + std::env::set_var("MAILER_STUB", "false"); + std::env::set_var("SMTP_ENABLE", "true"); + std::env::set_var("SMTP_HOST", SMTP_HOST); + std::env::set_var("SMTP_PORT", SMTP_PORT); + std::env::set_var("SMTP_USER", SMTP_USER); + std::env::set_var("SMTP_SECURE", SMTP_SECURE); + } + + let boot = boot_test::() + .await + .expect("Failed to boot test application"); + + // A real user whose address is the recipient, so `send_welcome` targets it. + let user = Model::create_with_password( + &boot.app_context.db, + &RegisterParams { + email: recipient.clone(), + password: "smtp-smoke-test".to_string(), + name: "SMTP smoke test".to_string(), + account_type: Some("personal".to_string()), + }, + ) + .await + .expect("failed to create test user"); + + // Give the welcome email a realistic verification token/link. + user.into_active_model() + .set_email_verification_sent(&boot.app_context.db) + .await + .expect("failed to set email verification token"); + let user = Model::find_by_email(&boot.app_context.db, &recipient) + .await + .expect("failed to reload test user"); + + // The assertion: the live SMTP server must accept the message. + AuthMailer::send_welcome(&boot.app_context, &user) + .await + .unwrap_or_else(|e| panic!("real SMTP send to {recipient} failed: {e:?}")); +} diff --git a/tests/mod.rs b/tests/mod.rs index b42f234..881612e 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -1,3 +1,4 @@ +mod mailer; mod models; mod requests; mod tasks; diff --git a/tests/models/users.rs b/tests/models/users.rs index 21bf08e..26cdbb8 100644 --- a/tests/models/users.rs +++ b/tests/models/users.rs @@ -50,6 +50,7 @@ async fn can_create_with_password() { email: "test@framework.com".to_string(), password: "1234".to_string(), name: "framework".to_string(), + account_type: Some("personal".to_string()), }; let res = Model::create_with_password(&boot.app_context.db, ¶ms).await; @@ -78,6 +79,7 @@ async fn handle_create_with_password_with_duplicate() { email: "user1@example.com".to_string(), password: "1234".to_string(), name: "framework".to_string(), + account_type: Some("personal".to_string()), }, ) .await;