use super::harness::{MockTransport, block_on, flatten};
use crate::client::SmtpClient;
use mail_builder::MessageBuilder;
#[test]
fn send_message_produces_same_wire_as_manual_send_mail() {
let server_script = flatten(&[
b"220 mail.example.com ESMTP\r\n",
b"250-mail.example.com\r\n",
b"250 8BITMIME\r\n",
b"250 OK\r\n",
b"250 OK\r\n",
b"354 End data with <CR><LF>.<CR><LF>\r\n",
b"250 Queued\r\n",
]);
let (transport, written, _closed) = MockTransport::new(&[&server_script[..]]);
let mut client = block_on(SmtpClient::connect(transport, "client.example")).expect("connect");
let message = MessageBuilder::new()
.from(("Notifications", "notify@example.com"))
.to(("Alice", "alice@example.org"))
.subject("Status update")
.text_body("Hello.\r\n");
block_on(client.send_message("notify@example.com", &["alice@example.org"], message))
.expect("send_message");
let written = written.borrow();
let written_str = std::str::from_utf8(&written).expect("UTF-8 wire bytes");
assert!(
written_str.starts_with("EHLO client.example\r\nMAIL FROM:<notify@example.com>\r\n"),
"envelope sender wrong: {written_str:?}",
);
assert!(
written_str.contains("RCPT TO:<alice@example.org>\r\n"),
"envelope recipient wrong: {written_str:?}",
);
assert!(
written_str.contains("DATA\r\n"),
"DATA verb missing: {written_str:?}",
);
assert!(
written_str.contains("From:"),
"From header missing: {written_str:?}",
);
assert!(
written_str.contains("Subject: Status update"),
"Subject header missing: {written_str:?}",
);
assert!(
written_str.ends_with("\r\n.\r\n"),
"DATA terminator missing or wrong: {written_str:?}",
);
}
#[test]
fn send_message_rejects_empty_recipient_list() {
use crate::error::SmtpError;
let server_script = flatten(&[
b"220 mail.example.com ESMTP\r\n",
b"250-mail.example.com\r\n",
b"250 8BITMIME\r\n",
]);
let (transport, written, _closed) = MockTransport::new(&[&server_script[..]]);
let mut client = block_on(SmtpClient::connect(transport, "client.example")).expect("connect");
let pre_writes = written.borrow().len();
let message = MessageBuilder::new()
.from("notify@example.com")
.to("alice@example.org")
.subject("test")
.text_body("body");
let err = block_on(client.send_message("notify@example.com", &[], message))
.expect_err("empty recipients must fail");
assert!(
matches!(err, SmtpError::InvalidInput(_)),
"expected InvalidInput, got {err:?}",
);
assert_eq!(written.borrow().len(), pre_writes);
}
#[test]
fn send_message_envelope_can_differ_from_headers() {
let server_script = flatten(&[
b"220 mail.example.com ESMTP\r\n",
b"250-mail.example.com\r\n",
b"250 8BITMIME\r\n",
b"250 OK\r\n", b"250 OK\r\n", b"250 OK\r\n", b"354 End data with <CR><LF>.<CR><LF>\r\n",
b"250 Queued\r\n",
]);
let (transport, written, _closed) = MockTransport::new(&[&server_script[..]]);
let mut client = block_on(SmtpClient::connect(transport, "client.example")).expect("connect");
let message = MessageBuilder::new()
.from("notify@example.com")
.to("alice@example.org") .subject("Update")
.text_body("Hi.");
block_on(client.send_message(
"notify@example.com",
&["alice@example.org", "auditor@example.com"],
message,
))
.expect("send_message with bcc-style envelope");
let written = written.borrow();
let written_str = std::str::from_utf8(&written).expect("UTF-8");
assert!(written_str.contains("RCPT TO:<alice@example.org>"));
assert!(written_str.contains("RCPT TO:<auditor@example.com>"));
}