elektromail 0.1.1

A minimal, Rust-based IMAP + SMTP mail server for local development and testing
Documentation
//! Integration tests for SQLite persistence.
//!
//! These tests verify that messages persist across server restarts when using SQLite storage.

use std::io;

use elektromail::{Server, ServerConfig, StorageBackend};
use elektromail_test_support as support;

fn temp_db_path() -> String {
    format!("/tmp/elektromail_test_{}.db", std::process::id())
}

#[tokio::test]
async fn sqlite_messages_persist_across_restart() -> io::Result<()> {
    let db_path = temp_db_path();

    // Cleanup any existing test database
    let _ = std::fs::remove_file(&db_path);

    // Start server with SQLite storage
    let config = ServerConfig {
        storage: StorageBackend::Sqlite(db_path.clone()),
        ..ServerConfig::default()
    };
    let server = Server::start(config).await?;
    let smtp_addr = server.smtp_addr();
    let imap_addr = server.imap_addr();

    // Send a message
    support::smtp_send(smtp_addr, "user@localhost").await?;

    // Verify message arrived
    let fetched = support::imap_fetch_all(imap_addr).await?;
    assert!(fetched.contains("Subject: Hello"));

    // Stop the server
    server.stop().await?;

    // Start a new server with the same database
    let config = ServerConfig {
        storage: StorageBackend::Sqlite(db_path.clone()),
        ..ServerConfig::default()
    };
    let server = Server::start(config).await?;
    let imap_addr = server.imap_addr();

    // Verify message still exists
    let fetched = support::imap_fetch_all(imap_addr).await?;
    assert!(fetched.contains("Subject: Hello"));

    server.stop().await?;

    // Cleanup
    let _ = std::fs::remove_file(&db_path);
    Ok(())
}

#[tokio::test]
async fn sqlite_flags_persist_across_restart() -> io::Result<()> {
    let db_path = temp_db_path() + "_flags";

    // Cleanup any existing test database
    let _ = std::fs::remove_file(&db_path);

    // Start server with SQLite storage
    let config = ServerConfig {
        storage: StorageBackend::Sqlite(db_path.clone()),
        ..ServerConfig::default()
    };
    let server = Server::start(config).await?;
    let smtp_addr = server.smtp_addr();
    let imap_addr = server.imap_addr();

    // Send a message and mark it as seen
    support::smtp_send(smtp_addr, "user@localhost").await?;
    support::imap_store_seen(imap_addr).await?;

    // Verify flag is set
    let flags = support::imap_fetch_flags(imap_addr).await?;
    assert!(flags.contains("\\Seen"));

    // Stop the server
    server.stop().await?;

    // Start a new server with the same database
    let config = ServerConfig {
        storage: StorageBackend::Sqlite(db_path.clone()),
        ..ServerConfig::default()
    };
    let server = Server::start(config).await?;
    let imap_addr = server.imap_addr();

    // Verify flag persisted
    let flags = support::imap_fetch_flags(imap_addr).await?;
    assert!(flags.contains("\\Seen"));

    server.stop().await?;

    // Cleanup
    let _ = std::fs::remove_file(&db_path);
    Ok(())
}

#[tokio::test]
async fn sqlite_mailboxes_persist_across_restart() -> io::Result<()> {
    let db_path = temp_db_path() + "_mailboxes";

    // Cleanup any existing test database
    let _ = std::fs::remove_file(&db_path);

    // Start server with SQLite storage
    let config = ServerConfig {
        storage: StorageBackend::Sqlite(db_path.clone()),
        ..ServerConfig::default()
    };
    let server = Server::start(config).await?;
    let imap_addr = server.imap_addr();

    // Create a mailbox
    let result = support::imap_create_mailbox(imap_addr, "TestFolder").await?;
    assert!(result.contains("OK"));

    // Verify mailbox exists
    let list = support::imap_list(imap_addr).await?;
    assert!(list.contains("TestFolder"));

    // Stop the server
    server.stop().await?;

    // Start a new server with the same database
    let config = ServerConfig {
        storage: StorageBackend::Sqlite(db_path.clone()),
        ..ServerConfig::default()
    };
    let server = Server::start(config).await?;
    let imap_addr = server.imap_addr();

    // Verify mailbox persisted
    let list = support::imap_list(imap_addr).await?;
    assert!(list.contains("TestFolder"));

    server.stop().await?;

    // Cleanup
    let _ = std::fs::remove_file(&db_path);
    Ok(())
}

#[tokio::test]
async fn in_memory_is_default_and_ephemeral() -> io::Result<()> {
    // Start server with default (in-memory) storage
    let server = Server::start(ServerConfig::default()).await?;
    let smtp_addr = server.smtp_addr();
    let imap_addr = server.imap_addr();

    // Send a message
    support::smtp_send(smtp_addr, "user@localhost").await?;

    // Verify message arrived
    let fetched = support::imap_fetch_all(imap_addr).await?;
    assert!(fetched.contains("Subject: Hello"));

    // Stop the server
    server.stop().await?;

    // Start a new server with default storage
    let server = Server::start(ServerConfig::default()).await?;
    let imap_addr = server.imap_addr();

    // Verify message is gone (in-memory doesn't persist)
    let fetched = support::imap_fetch_all(imap_addr).await?;
    assert!(!fetched.contains("Subject: Hello"));

    server.stop().await?;
    Ok(())
}