#[cfg(test)]
mod tests {
use super::*;
use tokio::test;
use tokio::net::TcpStream;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use std::net::SocketAddr;
use std::str::FromStr;
use std::time::Duration;
async fn create_mock_smtp_protocol() -> Result<(SmtpProtocol, TcpStream), Box<dyn std::error::Error>> {
let (server_stream, client_stream) = tokio::net::TcpSocket::new_v4()
?
.bind(SocketAddr::from_str("127.0.0.1:0").expect("Failed to parse bind address"))?
.connect(SocketAddr::from_str("127.0.0.1:0").expect("Failed to parse connect address"))
.await
?;
let protocol = SmtpProtocol::new(server_stream);
Ok((protocol, client_stream))
}
#[test]
async fn test_smtp_protocol_greeting() -> Result<(), Box<dyn std::error::Error>> {
let (mut protocol, mut client) = create_mock_smtp_protocol().await;
protocol.send_greeting().await?;
let mut buffer = [0; 1024];
let n = client.read(&mut buffer).await?;
let greeting = String::from_utf8_lossy(&buffer[0..n]);
assert_eq!(greeting, "220 MailLaser SMTP Server\r\n"); Ok(())
}
#[test]
async fn test_smtp_protocol_ehlo() -> Result<(), Box<dyn std::error::Error>> {
let (mut protocol, mut client) = create_mock_smtp_protocol().await;
client.write_all(b"EHLO example.com\r\n").await?;
let line = protocol.read_line().await?;
let result = protocol.process_command(&line).await?;
match result {
SmtpCommandResult::Continue => {},
_ => panic!("Expected Continue, got {:?}", result),
}
let mut buffer = [0; 1024];
let n = client.read(&mut buffer).await?;
let response = String::from_utf8_lossy(&buffer[0..n]);
assert_eq!(response, "250 MailLaser\r\n");
assert_eq!(protocol.get_state(), SmtpState::Greeted);
Ok(())
}
#[test]
async fn test_smtp_protocol_mail_from() -> Result<(), Box<dyn std::error::Error>> {
let (mut protocol, mut client) = create_mock_smtp_protocol().await;
protocol.reset_state();
assert_eq!(protocol.get_state(), SmtpState::Greeted);
client.write_all(b"MAIL FROM:<sender@example.com>\r\n").await?;
let line = protocol.read_line().await?;
let result = protocol.process_command(&line).await?;
match result {
SmtpCommandResult::MailFrom(email) => {
assert_eq!(email, "sender@example.com");
},
_ => panic!("Expected MailFrom, got {:?}", result),
}
let mut buffer = [0; 1024];
let n = client.read(&mut buffer).await?;
let response = String::from_utf8_lossy(&buffer[0..n]);
assert_eq!(response, "250 OK\r\n");
assert_eq!(protocol.get_state(), SmtpState::MailFrom);
Ok(())
}
#[test]
async fn test_smtp_protocol_rcpt_to() -> Result<(), Box<dyn std::error::Error>> {
let (mut protocol, mut client) = create_mock_smtp_protocol().await;
protocol.reset_state();
protocol.state = SmtpState::MailFrom;
client.write_all(b"RCPT TO:<recipient@example.com>\r\n").await?;
let line = protocol.read_line().await?;
let result = protocol.process_command(&line).await?;
match result {
SmtpCommandResult::RcptTo(email) => {
assert_eq!(email, "recipient@example.com");
},
_ => panic!("Expected RcptTo, got {:?}", result),
}
assert_eq!(protocol.get_state(), SmtpState::RcptTo);
Ok(())
}
#[test]
async fn test_smtp_protocol_data() -> Result<(), Box<dyn std::error::Error>> {
let (mut protocol, mut client) = create_mock_smtp_protocol().await;
protocol.reset_state();
protocol.state = SmtpState::RcptTo;
client.write_all(b"DATA\r\n").await?;
let line = protocol.read_line().await?;
let result = protocol.process_command(&line).await?;
match result {
SmtpCommandResult::DataStart => {},
_ => panic!("Expected DataStart, got {:?}", result),
}
let mut buffer = [0; 1024];
let n = client.read(&mut buffer).await?;
let response = String::from_utf8_lossy(&buffer[0..n]);
assert_eq!(response, "354 End data with <CR><LF>.<CR><LF>\r\n");
assert_eq!(protocol.get_state(), SmtpState::Data);
Ok(())
}
#[test]
async fn test_smtp_protocol_data_content() -> Result<(), Box<dyn std::error::Error>> {
let (mut protocol, mut client) = create_mock_smtp_protocol().await;
protocol.reset_state();
protocol.state = SmtpState::Data;
client.write_all(b"Subject: Test Subject\r\n").await?;
client.write_all(b"\r\n").await?;
client.write_all(b"This is the body.\r\n").await?;
client.write_all(b".\r\n").await?;
let mut data_lines = Vec::new();
for _ in 0..4 {
let line = protocol.read_line().await?;
let result = protocol.process_command(&line).await?;
match result {
SmtpCommandResult::DataLine(content) => {
data_lines.push(content);
},
SmtpCommandResult::DataEnd => {
assert_eq!(line, ".");
},
_ => panic!("Unexpected result: {:?}", result),
}
}
assert_eq!(data_lines.len(), 3);
assert_eq!(data_lines[0], "Subject: Test Subject");
assert_eq!(data_lines[1], "");
assert_eq!(data_lines[2], "This is the body.");
let mut buffer = [0; 1024];
let n = client.read(&mut buffer).await?;
let response = String::from_utf8_lossy(&buffer[0..n]);
assert_eq!(response, "250 OK: Message accepted\r\n");
assert_eq!(protocol.get_state(), SmtpState::Greeted);
Ok(())
}
#[test]
async fn test_smtp_protocol_quit() -> Result<(), Box<dyn std::error::Error>> {
let (mut protocol, mut client) = create_mock_smtp_protocol().await;
client.write_all(b"QUIT\r\n").await?;
let line = protocol.read_line().await?;
let result = protocol.process_command(&line).await?;
match result {
SmtpCommandResult::Quit => {},
_ => panic!("Expected Quit, got {:?}", result),
}
let mut buffer = [0; 1024];
let n = client.read(&mut buffer).await?;
let response = String::from_utf8_lossy(&buffer[0..n]);
assert_eq!(response, "221 Bye\r\n");
Ok(())
}
}