mod common; pub use common::*; use indymilter_test::*; use spamassassin_milter::*; /// ‘Happy path’ processing of a spam message. #[tokio::test] async fn spam_message() { let config = configure_spamc(Config::builder()) .spamc_args([format!("--port={SPAMD_PORT}")]) .build(); let server = spawn_mock_spamd_server(SPAMD_PORT, |spam| { let mut spam = spam .replacen("X-Spam-Checker-Version: BogusChecker 1.0.0\r\n", "", 1) .replacen("X-Spam-Report: Bogus report\r\n", "", 1) .replacen( "\r\n\r\n", "\r\n\ X-Spam-Checker-Version: MyChecker 1.0.0\r\n\ X-Spam-Flag: YES\r\n\ X-Spam-Custom: Custom-Value\r\n\ Content-Type: multipart/mixed; ...\r\n\ \r\n", 1, ) .replacen( "Subject: Test message\r\n", "Subject: [SPAM] Test message\r\n", 1, ); // Replace the message body with the SpamAssassin ‘report’. spam.replace_range( (spam.find("\r\n\r\n").unwrap() + 4).., "Spam detection software has identified ...", ); Err(spam) }) .await .unwrap(); let milter = SpamAssassinMilter::spawn(LOCALHOST, config).await.unwrap(); let mut conn = TestConnection::open(milter.addr()).await.unwrap(); let status = conn.connect("client.gluet.ch", [123, 123, 123, 123]).await.unwrap(); assert_eq!(status, Status::Continue); let status = conn.helo("mail.gluet.ch").await.unwrap(); assert_eq!(status, Status::Continue); let status = conn.mail([""]).await.unwrap(); assert_eq!(status, Status::Continue); let status = conn.rcpt([""]).await.unwrap(); assert_eq!(status, Status::Continue); conn.macros( MacroStage::Data, [ ("i", "1234567ABC"), ("j", "localhost"), ("_", "client.gluet.ch [123.123.123.123]"), ("{tls_version}", "TLSv1.2"), ("v", "Postfix 3.3.0"), ], ) .await .unwrap(); let status = conn.data().await.unwrap(); assert_eq!(status, Status::Continue); let status = conn.header("From", "from@gluet.ch").await.unwrap(); assert_eq!(status, Status::Continue); let status = conn.header("To", "to@gluet.ch").await.unwrap(); assert_eq!(status, Status::Continue); let status = conn.header("Subject", "Test message").await.unwrap(); assert_eq!(status, Status::Continue); let status = conn.header("Message-ID", rand_msg_id()).await.unwrap(); assert_eq!(status, Status::Continue); let status = conn.header("Date", current_date()).await.unwrap(); assert_eq!(status, Status::Continue); // Incoming foreign SpamAssassin headers, to be replaced or deleted. let status = conn.header("X-Spam-Checker-Version", "BogusChecker 1.0.0").await.unwrap(); assert_eq!(status, Status::Continue); let status = conn.header("X-Spam-Report", "Bogus report").await.unwrap(); assert_eq!(status, Status::Continue); let status = conn.eoh().await.unwrap(); assert_eq!(status, Status::Continue); let status = conn.body(&b"HI!!! You have won a BILLION dollars!!!!"[..]).await.unwrap(); assert_eq!(status, Status::Continue); let (actions, status) = conn.eom().await.unwrap(); assert_eq!(status, Status::Continue); assert!(actions.has_delete_header("X-Spam-Checker-Version", any())); assert!(actions.has_add_header("X-Spam-Checker-Version", " MyChecker 1.0.0")); assert!(actions.has_add_header("X-Spam-Flag", " YES")); assert!(actions.has_add_header("X-Spam-Custom", " Custom-Value")); assert!(actions.has_delete_header("X-Spam-Report", any())); assert!(actions.has_change_header("Subject", 1, " [SPAM] Test message")); assert!(actions.has_add_header("Content-Type", " multipart/mixed; ...")); assert!(actions.has_replaced_body(b"Spam detection software has identified ...")); conn.close().await.unwrap(); milter.shutdown().await.unwrap(); server.await.unwrap().unwrap(); }