//! 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. // Dial the name the TLS cert is actually valid for. `smtp.kompress.sk` is a // DNS alias for the same server (213.215.124.101) but the cert only lists // smtp.euronet.sk, so connecting via the alias fails certificate validation. const SMTP_HOST: &str = "smtp.euronet.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 = "filippriec@tutanota.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:?}")); }