use anyhow::Context;
use log::info;
use crate::config::PostarConfig;
use crate::inbox::{Inbox, Message, imap_inbox::IMAPInbox};
use crate::test_helpers::{
find_folder_contains, find_folder_equals, get_container, get_host_container,
get_mock_email_dir, send_email,
};
async fn send_test_email(
container_data: &crate::test_helpers::IMAPContainerData,
from_name: &str,
from_email: &str,
to_name: &str,
to_email: &str,
subject: &str,
body: &str,
) -> anyhow::Result<()> {
container_data
.send_email(
mail_send::mail_builder::MessageBuilder::new()
.from((from_name, from_email))
.to((to_name, to_email))
.subject(subject)
.text_body(body),
)
.await?;
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_send_email() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_equals(&mut inbox, "INBOX")?;
let initial_emails = inbox.fetch_all_messages_in_folder(&folder)?;
send_test_email(
&container_data,
"foo",
"foo@example.com",
"bar",
"bar@example.com",
"This is a test.",
"This is the text body.",
)
.await?;
let after_emails = inbox.fetch_all_messages_in_folder(&folder)?;
assert_eq!(
initial_emails.len() + 1,
after_emails.len(),
"This after messages should be 1 more than the initial ones."
);
assert!(
after_emails
.iter()
.any(|x| x.subject.as_ref().map(|s| s.as_str()).unwrap_or("") == "This is a test."),
"The new email should exist."
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_new_tls_successful_connection() -> anyhow::Result<()> {
let container_data = get_container().await;
let _ = IMAPInbox::new_tls(
&container_data.host,
container_data.imap_port,
"bar",
"a",
true,
&PostarConfig::default(),
":memory:",
)?;
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_new_tls_invalid_host() {
let result = IMAPInbox::new_tls(
"invalid.host.example.com",
993,
"user",
"pass",
true,
&PostarConfig::default(),
":memory:",
);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("Failed to connect to IMAP server"));
}
#[tokio::test]
#[test_log::test]
async fn test_list_folders_returns_all_folders() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folders = inbox.list_folders()?;
assert!(!folders.is_empty(), "Expected at least 1 folder, got 0");
let has_inbox = folders.iter().any(|f| f.name == "INBOX");
assert!(has_inbox, "Expected to find INBOX folder");
let has_test1 = folders.iter().any(|f| f.name == "INBOX.tests1");
assert!(has_test1, "Expected to find INBOX.tests1 folder");
let has_test2 = folders.iter().any(|f| f.name == "INBOX.tests2");
assert!(has_test2, "Expected to find INBOX.tests2 folder");
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_list_folders_returns_folder_objects() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folders = inbox.list_folders()?;
for folder in &folders {
assert!(!folder.name.is_empty(), "Folder name should not be empty");
}
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_list_folders_can_be_called_multiple_times() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folders1 = inbox.list_folders()?;
let folders2 = inbox.list_folders()?;
assert_eq!(
folders1.len(),
folders2.len(),
"Folder count should be consistent"
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_fetch_all_messages_empty_folder() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests2")?;
let emails = inbox.fetch_all_messages_in_folder(&folder)?;
assert_eq!(emails.len(), 0);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_fetch_all_messages_contains_correct_count() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let emails = inbox.fetch_all_messages_in_folder(&folder)?;
assert_eq!(emails.len(), 11);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_fetch_all_messages_contains_specific_body_data() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let _emails = inbox.fetch_all_messages_in_folder(&folder)?;
let mut body_path = get_mock_email_dir();
body_path.push("bar@example.com/INBOX/tests1/0.eml");
let body_data = std::fs::read(body_path)?;
let _str1 = std::str::from_utf8(&body_data)
.unwrap()
.replace("\r\n", "\n");
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_move_message_to_another_folder() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let source_folder = find_folder_contains(&mut inbox, "tests1")?;
let dest_folder = find_folder_contains(&mut inbox, "tests2")?;
let mut messages = inbox.fetch_all_messages_in_folder(&source_folder)?;
let initial_count = messages.len();
assert!(
initial_count > 0,
"Source folder should have messages to move"
);
let mut message_to_move = messages.remove(0);
let original_body = message_to_move.body.clone();
inbox.move_message_to_folder(&mut message_to_move, &dest_folder)?;
assert!(
!message_to_move.valid,
"Message should be invalid after move"
);
let source_messages_after = inbox.fetch_all_messages_in_folder(&source_folder)?;
let dest_messages_after = inbox.fetch_all_messages_in_folder(&dest_folder)?;
assert_eq!(source_messages_after.len(), initial_count - 1);
assert_eq!(dest_messages_after.len(), 1);
let moved_message = dest_messages_after
.iter()
.find(|m| m.body == original_body)
.expect("Message should be found in destination folder by body content");
assert_eq!(
moved_message.body, original_body,
"Persisted body should match the original"
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_move_message_to_same_folder() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let mut messages = inbox.fetch_all_messages_in_folder(&folder)?;
let initial_count = messages.len();
assert!(initial_count > 0, "Folder should have messages");
let mut message_to_move = messages.remove(0);
let original_body = message_to_move.body.clone();
inbox.move_message_to_folder(&mut message_to_move, &folder)?;
assert!(
!message_to_move.valid,
"Message should be invalid after move"
);
let messages_after = inbox.fetch_all_messages_in_folder(&folder)?;
let moved_message = messages_after
.iter()
.find(|m| m.body == original_body)
.expect("Message should be found in destination folder by body content");
assert_eq!(
moved_message.body, original_body,
"Persisted body should match the original"
);
assert_eq!(messages_after.len(), initial_count);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_move_message_to_non_existing_folder() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let source_folder = find_folder_contains(&mut inbox, "tests1")?;
let non_existing_folder = crate::inbox::Folder {
name: "INBOX.NonExistingFolder".to_string(),
};
let mut messages = inbox.fetch_all_messages_in_folder(&source_folder)?;
assert!(messages.len() > 0, "Source folder should have messages");
let mut message_to_move = messages.remove(0);
let result = inbox.move_message_to_folder(&mut message_to_move, &non_existing_folder);
assert!(
result.is_err(),
"Moving to non-existing folder should fail."
);
let messages_after = inbox.fetch_all_messages_in_folder(&source_folder)?;
assert!(
messages_after
.iter()
.any(|x| x.uid() == message_to_move.uid()),
"Message should remain in source folder"
);
assert!(
*message_to_move.containing_folder().unwrap() == source_folder,
"Containing folder shouldn't change."
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_move_invalid_message_to_another_folder() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let source_folder = find_folder_contains(&mut inbox, "tests1")?;
let dest_folder = find_folder_contains(&mut inbox, "tests2")?;
let mut body_path = get_mock_email_dir();
body_path.push("bar@example.com/INBOX/tests1/0.eml");
let body_data = std::fs::read(body_path)?;
let mut message_to_move = Message::new(source_folder.clone(), 999999999, body_data).unwrap();
let result = inbox.move_message_to_folder(&mut message_to_move, &dest_folder);
assert!(
result.is_ok(),
"Moving invalid message shouldn't fail. It is just a no-op."
);
assert!(
!message_to_move.valid,
"Message should be invalid after move"
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_delete_valid_message() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let mut messages = inbox.fetch_all_messages_in_folder(&folder)?;
let initial_count = messages.len();
assert!(initial_count > 0, "Folder should have messages to delete");
let mut message_to_delete = messages.remove(0);
let original_uid = message_to_delete.uid().expect("Message should have UID");
inbox.delete_message(&mut message_to_delete)?;
assert!(
!message_to_delete.valid,
"Message should be invalid after deletion"
);
let messages_after = inbox.fetch_all_messages_in_folder(&folder)?;
assert_eq!(
messages_after.len(),
initial_count - 1,
"Message count should decrease by 1 after deletion"
);
assert!(
messages_after.iter().all(|m| m.uid() != Some(original_uid)),
"Deleted message should not be found in folder"
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_delete_invalid_message() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let body_path = get_mock_email_dir().join("bar@example.com/INBOX/tests1/0.eml");
let body_data = std::fs::read(body_path)?;
let mut invalid_message = Message::new(
folder.clone(),
999999999, body_data,
)
.unwrap();
let result = inbox.delete_message(&mut invalid_message);
assert!(
result.is_ok(),
"Deleting invalid message should not fail, it should be a no-op"
);
assert!(
!invalid_message.valid,
"Message should be invalid after deletion attempt"
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_delete_already_invalid_message() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let body_path = get_mock_email_dir().join("bar@example.com/INBOX/tests1/0.eml");
let body_data = std::fs::read(body_path)?;
let mut invalid_message = Message::new(folder.clone(), 1, body_data).unwrap();
invalid_message.set_invalid();
let result = inbox.delete_message(&mut invalid_message);
assert!(
result.is_err(),
"Deleting already invalid message should fail"
);
assert!(!invalid_message.valid, "Message should remain invalid");
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_delete_multiple_messages() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let mut messages = inbox.fetch_all_messages_in_folder(&folder)?;
let initial_count = messages.len();
assert!(initial_count >= 2, "Folder should have at least 2 messages");
let mut message1 = messages.remove(0);
let mut message2 = messages.remove(0);
let uid1 = message1.uid().expect("Message should have UID");
let uid2 = message2.uid().expect("Message should have UID");
inbox.delete_message(&mut message1)?;
inbox.delete_message(&mut message2)?;
assert!(!message1.valid, "First message should be invalid");
assert!(!message2.valid, "Second message should be invalid");
let messages_after = inbox.fetch_all_messages_in_folder(&folder)?;
assert_eq!(
messages_after.len(),
initial_count - 2,
"Message count should decrease by 2 after deletion"
);
assert!(
messages_after
.iter()
.all(|m| m.uid() != Some(uid1) && m.uid() != Some(uid2)),
"Both deleted messages should not be found in folder"
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_message_subject_returns_correct_value() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let messages = inbox.fetch_all_messages_in_folder(&folder)?;
let billing_message = messages
.iter()
.find(|x| {
x.subject
.as_ref()
.unwrap_or(&String::new())
.contains("Billing Issues")
})
.ok_or(anyhow::format_err!("Cannot find billing message"))?;
assert_eq!(
billing_message.subject.as_ref().unwrap(),
"Billing Issues",
"Subject should match expected value"
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_message_subject_returns_none_for_missing_subject() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let messages = inbox.fetch_all_messages_in_folder(&folder)?;
for message in &messages {
if message.subject.is_none() {
assert!(message.subject.is_none(), "Message should have no subject");
return Ok(());
}
}
assert!(
messages.iter().all(|m| m.subject.is_some()),
"All messages have subjects"
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_message_from_returns_correct_format() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let messages = inbox.fetch_all_messages_in_folder(&folder)?;
for message in &messages {
if let Some(from_field) = &message.from {
assert!(!from_field.is_empty(), "From field should not be empty");
assert!(
from_field.contains('<') && from_field.contains('>'),
"From field should contain address in <address> format"
);
}
}
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_message_from_returns_none_for_missing_from() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let messages = inbox.fetch_all_messages_in_folder(&folder)?;
for message in &messages {
if message.from.is_none() {
assert!(message.from.is_none(), "Message should have no from field");
return Ok(());
}
}
assert!(
messages.iter().all(|m| m.from.is_some()),
"All messages have from fields"
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_message_to_returns_correct_format() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let messages = inbox.fetch_all_messages_in_folder(&folder)?;
for message in &messages {
if let Some(to_field) = &message.to {
assert!(!to_field.is_empty(), "To field should not be empty");
assert!(
to_field.contains('<') && to_field.contains('>'),
"To field should contain address in <address> format"
);
}
}
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_message_to_returns_none_for_missing_to() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let messages = inbox.fetch_all_messages_in_folder(&folder)?;
for message in &messages {
if message.to.is_none() {
assert!(message.to.is_none(), "Message should have no to field");
return Ok(());
}
}
assert!(
messages.iter().all(|m| m.to.is_some()),
"All messages have to fields"
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_message_fields_consistency() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let messages = inbox.fetch_all_messages_in_folder(&folder)?;
for message in &messages {
let subject = &message.subject;
let from = &message.from;
let to = &message.to;
assert!(
subject.is_some() || from.is_some() || to.is_some(),
"Message should have at least one of subject, from, or to"
);
if let (Some(from_field), Some(to_field)) = (from, to) {
if from_field != to_field {
assert_ne!(
from_field, to_field,
"From and to fields should typically be different"
);
}
}
}
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_message_fields_handle_multiple_addresses() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let messages = inbox.fetch_all_messages_in_folder(&folder)?;
for message in &messages {
if let Some(from_field) = &message.from {
if from_field.contains(',') {
info!("Checking from: {}", from_field);
let addresses: Vec<&str> = from_field.split(',').collect();
assert!(
addresses.len() > 1,
"Should have multiple addresses when comma is present"
);
for addr in addresses {
info!("Checking address: {}", addr);
assert!(
addr.trim().contains('<') && addr.trim().contains('>'),
"Each address should be in <address> format"
);
}
}
}
if let Some(to_field) = &message.to {
if to_field.contains(',') {
let addresses: Vec<&str> = to_field.split(',').collect();
assert!(
addresses.len() > 1,
"Should have multiple addresses when comma is present"
);
for addr in addresses {
assert!(
addr.trim().contains('<') && addr.trim().contains('>'),
"Each address should be in <address> format"
);
}
}
}
}
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_message_body_returns_content() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let messages = inbox.fetch_all_messages_in_folder(&folder)?;
for message in &messages {
let body = &message.body;
info!(
"Message {} subject {} length {}",
message.uid().unwrap(),
message.subject.as_ref().unwrap(),
body.len()
);
assert!(!body.is_empty(), "Message body should not be empty");
assert!(
body.len() > 10,
"Message body should have substantial content"
);
}
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_message_body_contains_expected_html_content() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let messages = inbox.fetch_all_messages_in_folder(&folder)?;
let billing_message = messages
.iter()
.find(|x| {
x.subject
.as_ref()
.unwrap_or(&String::new())
.contains("Billing Issues")
})
.ok_or(anyhow::format_err!("Cannot find billing message"))?;
let body = &billing_message.body;
assert!(
body.contains("Dear valued"),
"Body should contain the greeting"
);
assert!(body.contains("eBay Inc."), "Body should contain eBay Inc.");
assert!(body.contains("<html>"), "Body should contain HTML tags");
assert!(
body.contains("</html>"),
"Body should contain closing HTML tag"
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_message_body_handles_different_content_types() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let messages = inbox.fetch_all_messages_in_folder(&folder)?;
for (i, message) in messages.iter().enumerate() {
let body = &message.body;
info!("Message {} body length: {}", i, body.len());
assert!(!body.is_empty(), "Message {} should have body content", i);
let has_html = body.contains("<html>") || body.contains("<p>") || body.contains("<br>");
let has_text = body.chars().any(|c| c.is_alphabetic()) && !body.contains("<html>");
assert!(
has_html || has_text,
"Message {} should have either HTML or text content",
i
);
}
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_message_body_concatenation_order() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let messages = inbox.fetch_all_messages_in_folder(&folder)?;
for message in &messages {
let body = &message.body;
let html_start = body.find("<html>");
let text_start = body.find("Dear");
if let (Some(html_pos), Some(text_pos)) = (html_start, text_start) {
assert!(
html_pos < text_pos,
"HTML content should appear before text content in body"
);
}
}
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_message_body_for_invalid_message() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_contains(&mut inbox, "tests1")?;
let messages = inbox.fetch_all_messages_in_folder(&folder)?;
let mut test_message = messages.into_iter().next().unwrap();
test_message.set_invalid();
let body = &test_message.body;
assert!(
!body.is_empty(),
"Invalid message should still return body content"
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_poll_new_messages_receives_sent_email() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_equals(&mut inbox, "INBOX")?;
let mut poll_inbox = container_data.create_inbox()?;
let poll_folder = folder.clone();
let handle = tokio::task::spawn_blocking(move || poll_inbox.poll_new_messages(&poll_folder));
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
send_test_email(
&container_data,
"sender",
"sender@example.com",
"bar",
"bar@example.com",
"Poll Test",
"Testing poll_new_messages",
)
.await?;
let result = tokio::time::timeout(tokio::time::Duration::from_secs(5), handle).await???;
assert!(
!result.is_empty(),
"Poll should have returned at least one message"
);
assert!(
result
.iter()
.any(|m| m.subject.as_ref().unwrap_or(&String::new()) == "Poll Test"),
"The polled email should have the correct subject"
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_poll_new_messages_blocks_until_arrival() -> anyhow::Result<()> {
let container_data = get_container().await;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_equals(&mut inbox, "INBOX")?;
let mut poll_inbox = container_data.create_inbox()?;
let poll_folder = folder.clone();
let handle = tokio::task::spawn_blocking(move || poll_inbox.poll_new_messages(&poll_folder));
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
assert!(
!handle.is_finished(),
"Poll should still be blocking on empty folder"
);
send_test_email(
&container_data,
"sender",
"sender@example.com",
"bar",
"bar@example.com",
"Delayed Arrival",
"Triggering poll",
)
.await?;
container_data.print_container_logs().await;
let result = tokio::time::timeout(tokio::time::Duration::from_secs(5), handle).await???;
assert!(
!result.is_empty(),
"Poll should have returned at least one message"
);
assert!(
result
.iter()
.any(|m| m.subject.as_ref().unwrap_or(&String::new()) == "Delayed Arrival"),
"The polled email should have the correct subject"
);
Ok(())
}
#[tokio::test]
#[test_log::test]
async fn test_poll_new_messages_multiple_poll_calls_sequential() -> anyhow::Result<()> {
let container_data = get_container().await;
let host = container_data.host.clone();
let smtp_port = container_data.smtp_port;
let mut inbox = container_data.create_inbox()?;
let folder = find_folder_equals(&mut inbox, "INBOX")?;
let host1 = host.clone();
let handle1 = tokio::spawn(async move {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
send_email(
&host1,
smtp_port,
"sender1",
"sender1@example.com",
"bar",
"bar@example.com",
"Sequential Poll 1",
"First message",
)
.await
});
let folder1 = folder.clone();
let (mut inbox, result1) = tokio::task::spawn_blocking(move || {
let poll_result = inbox
.poll_new_messages(&folder1)
.with_context(|| "First poll failed")?;
Ok::<
(
IMAPInbox<native_tls::TlsStream<std::net::TcpStream>>,
Vec<Message>,
),
anyhow::Error,
>((inbox, poll_result))
})
.await??;
handle1.await??;
assert_eq!(result1.len(), 1, "First poll should return 1 message");
assert_eq!(
result1[0].subject.as_ref().unwrap(),
"Sequential Poll 1",
"First message should have correct subject"
);
let host2 = host.clone();
let handle2 = tokio::spawn(async move {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
send_email(
&host2,
smtp_port,
"sender2",
"sender2@example.com",
"bar",
"bar@example.com",
"Sequential Poll 2",
"Second message",
)
.await
});
let (_inbox, result2) = tokio::task::spawn_blocking(move || {
let poll_result = inbox
.poll_new_messages(&folder)
.with_context(|| "Second poll failed")?;
Ok::<
(
IMAPInbox<native_tls::TlsStream<std::net::TcpStream>>,
Vec<Message>,
),
anyhow::Error,
>((inbox, poll_result))
})
.await??;
handle2.await??;
assert_eq!(result2.len(), 1, "Second poll should return 1 message");
assert_eq!(
result2[0].subject.as_ref().unwrap(),
"Sequential Poll 2",
"Second message should have correct subject"
);
Ok(())
}