//! A3 — the native SMTP ingress delivery core. Seeds an app-owned email trigger //! with an inbound `RCPT TO` address, then drives `SmtpIngress::deliver` //! directly (no socket — deterministic) and asserts it writes exactly one //! `Email` outbox row for the known mailbox and none for an unknown one. The //! dispatcher tail (outbox → script) is covered by the existing dispatcher //! tests; this pins the resolve-address → outbox step the SMTP path adds. use std::sync::Arc; use picloud::smtp::SmtpIngress; use picloud_manager_core::{PostgresOutboxRepo, PostgresTriggerRepo}; use sqlx::PgPool; use uuid::Uuid; mod common; async fn seed(pool: &PgPool, address: &str) -> Uuid { let group: (Uuid,) = sqlx::query_as("INSERT INTO groups (slug, name) VALUES ($1, $1) RETURNING id") .bind(format!("smtp-g-{}", Uuid::new_v4())) .fetch_one(pool) .await .expect("group"); let app: (Uuid,) = sqlx::query_as("INSERT INTO apps (slug, name, group_id) VALUES ($1, $1, $2) RETURNING id") .bind(format!("smtp-a-{}", Uuid::new_v4())) .bind(group.0) .fetch_one(pool) .await .expect("app"); let admin: (Uuid,) = sqlx::query_as( "INSERT INTO admin_users (username, password_hash) VALUES ($1, 'x') RETURNING id", ) .bind(format!("smtp-u-{}", Uuid::new_v4())) .fetch_one(pool) .await .expect("admin"); let script: (Uuid,) = sqlx::query_as( "INSERT INTO scripts (name, source, app_id) VALUES ($1, 'x', $2) RETURNING id", ) .bind(format!("smtp-s-{}", Uuid::new_v4())) .bind(app.0) .fetch_one(pool) .await .expect("script"); let trigger: (Uuid,) = sqlx::query_as( "INSERT INTO triggers \ (app_id, script_id, kind, enabled, dispatch_mode, retry_max_attempts, \ retry_backoff, retry_base_ms, registered_by_principal, name) \ VALUES ($1, $2, 'email', TRUE, 'async', 3, 'exponential', 1000, $3, $4) RETURNING id", ) .bind(app.0) .bind(script.0) .bind(admin.0) .bind(format!("smtp-t-{}", Uuid::new_v4())) .fetch_one(pool) .await .expect("trigger"); sqlx::query("INSERT INTO email_trigger_details (trigger_id, inbound_address) VALUES ($1, $2)") .bind(trigger.0) .bind(address) .execute(pool) .await .expect("email details"); app.0 } async fn email_outbox_count(pool: &PgPool, app_id: Uuid) -> i64 { let row: (i64,) = sqlx::query_as("SELECT count(*) FROM outbox WHERE app_id = $1 AND source_kind = 'email'") .bind(app_id) .fetch_one(pool) .await .expect("count"); row.0 } #[tokio::test] async fn deliver_writes_one_outbox_row_for_a_known_mailbox() { let Some(pool) = common::test_pool("smtp_ingress").await else { return; }; let address = "hooks@app.test"; let app_id = seed(&pool, address).await; let ingress = SmtpIngress::new( Arc::new(PostgresTriggerRepo::new(pool.clone())), Arc::new(PostgresOutboxRepo::new(pool.clone())), ); let raw = b"From: alice@example.com\r\nSubject: Hi there\r\n\r\nhello body\r\n"; // Known mailbox → one outbox row. let delivered = ingress.deliver(&[address.to_string()], raw).await; assert_eq!(delivered, 1, "a known mailbox must be delivered"); assert_eq!(email_outbox_count(&pool, app_id).await, 1); // Unknown mailbox → nothing (the resolution IS the boundary). let delivered = ingress.deliver(&["nobody@app.test".to_string()], raw).await; assert_eq!(delivered, 0, "an unknown mailbox must not deliver"); assert_eq!( email_outbox_count(&pool, app_id).await, 1, "no extra outbox row for an unknown mailbox" ); }