diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6df1579..5ccf4a5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,7 @@ name: cargo test jobs: test: runs-on: ubuntu-latest - name: ${{ matrix.toolchain }} + name: greenmail/${{ matrix.toolchain }} strategy: matrix: toolchain: [stable, beta, nightly] @@ -32,3 +32,28 @@ jobs: - 3995:3995 env: GREENMAIL_OPTS: "-Dgreenmail.setup.test.all -Dgreenmail.hostname=0.0.0.0 -Dgreenmail.auth.disabled -Dgreenmail.verbose" + test_cyrus: + runs-on: ubuntu-latest + name: cyrus/${{ matrix.toolchain }} + strategy: + matrix: + toolchain: [stable, beta, nightly] + steps: + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.toolchain }} + - uses: actions/checkout@v2 + - name: cargo test + uses: actions-rs/cargo@v1 + with: + command: test + args: -- --ignored + services: + cyrus_imapd: + image: outoforder/cyrus-imapd-tester:latest + ports: + - 3025:25 + - 3143:143 + - 3465:465 + - 3993:993 diff --git a/README.md b/README.md index 3b2d745..0a0d30e 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,14 @@ easiest way to do that is with Docker: ```console $ docker pull greenmail/standalone:1.6.8 -$ docker run -t -i -e GREENMAIL_OPTS='-Dgreenmail.setup.test.all -Dgreenmail.hostname=0.0.0.0 -Dgreenmail.auth.disabled -Dgreenmail.verbose' -p 3025:3025 -p 3110:3110 -p 3143:3143 -p 3465:3465 -p 3993:3993 -p 3995:3995 greenmail/standalone:1.6.3 +$ docker run -it --rm -e GREENMAIL_OPTS='-Dgreenmail.setup.test.all -Dgreenmail.hostname=0.0.0.0 -Dgreenmail.auth.disabled -Dgreenmail.verbose' -p 3025:3025 -p 3110:3110 -p 3143:3143 -p 3465:3465 -p 3993:3993 -p 3995:3995 greenmail/standalone:1.6.3 +``` + +Another alternative is to test against cyrus imapd which is a more complete IMAP implementation that greenmail (supporting quotas and ACLs). + +``` +$ docker pull outoforder/cyrus-imapd-tester +$ docker run -it --rm -p 3025:25 -p 3110:110 -p 3143:143 -p 3465:465 -p 3993:993 outoforder/cyrus-imapd-tester:latest ``` ## License diff --git a/tests/imap_integration.rs b/tests/imap_integration.rs index 81a5efa..ec455b1 100644 --- a/tests/imap_integration.rs +++ b/tests/imap_integration.rs @@ -19,9 +19,61 @@ fn tls() -> native_tls::TlsConnector { .unwrap() } +fn test_host() -> String { + std::env::var("TEST_HOST").unwrap_or("127.0.0.1".to_string()) +} + +fn test_smtp_host() -> String { + std::env::var("TEST_SMTP_HOST") + .unwrap_or_else(|_| std::env::var("TEST_HOST").unwrap_or("127.0.0.1".to_string())) +} + +fn test_imap_port() -> u16 { + std::env::var("TEST_IMAP_PORT") + .unwrap_or("3143".to_string()) + .parse() + .unwrap_or(3143) +} + +fn test_imaps_port() -> u16 { + std::env::var("TEST_IMAPS_PORT") + .unwrap_or("3993".to_string()) + .parse() + .unwrap_or(3993) +} + +fn test_smtps_port() -> u16 { + std::env::var("TEST_SMTPS_PORT") + .unwrap_or("3465".to_string()) + .parse() + .unwrap_or(3465) +} + +fn clean_mailbox(session: &mut imap::Session>) { + session.select("INBOX").unwrap(); + let inbox = session.search("ALL").unwrap(); + if !inbox.is_empty() { + session + .store( + inbox + .iter() + .map(|d| d.to_string()) + .collect::>() + .join(","), + "+FLAGS (\\Deleted)", + ) + .unwrap(); + } + session.expunge().unwrap(); +} + +fn wait_for_delivery() { + std::thread::sleep(std::time::Duration::from_millis(500)); +} + fn session(user: &str) -> imap::Session> { - let host = std::env::var("TEST_HOST").unwrap_or("127.0.0.1".to_string()); - let mut s = imap::ClientBuilder::new(&host, 3993) + let host = test_host(); + let mut s = imap::ClientBuilder::new(&host, test_imaps_port()) .connect(|domain, tcp| { let ssl_conn = tls(); Ok(native_tls::TlsConnector::connect(&ssl_conn, domain, tcp).unwrap()) @@ -30,19 +82,17 @@ fn session(user: &str) -> imap::Session> { .login(user, user) .unwrap(); s.debug = true; + clean_mailbox(&mut s); s } fn smtp(user: &str) -> lettre::SmtpTransport { let creds = lettre::smtp::authentication::Credentials::new(user.to_string(), user.to_string()); lettre::SmtpClient::new( - &format!( - "{}:3465", - std::env::var("TEST_HOST").unwrap_or("127.0.0.1".to_string()) - ), + &format!("{}:{}", test_smtp_host(), test_smtps_port()), lettre::ClientSecurity::Wrapper(lettre::ClientTlsParameters { connector: tls(), - domain: "smpt.example.com".to_string(), + domain: "smtp.example.com".to_string(), }), ) .unwrap() @@ -53,9 +103,9 @@ fn smtp(user: &str) -> lettre::SmtpTransport { #[test] #[ignore] fn connect_insecure_then_secure() { - let host = std::env::var("TEST_HOST").unwrap_or("127.0.0.1".to_string()); + let host = test_host(); // ignored because of https://github.com/greenmail-mail-test/greenmail/issues/135 - imap::ClientBuilder::new(&host, 3143) + imap::ClientBuilder::new(&host, test_imap_port()) .starttls() .connect(|domain, tcp| { let ssl_conn = tls(); @@ -66,8 +116,8 @@ fn connect_insecure_then_secure() { #[test] fn connect_secure() { - let host = std::env::var("TEST_HOST").unwrap_or("127.0.0.1".to_string()); - imap::ClientBuilder::new(&host, 3993) + let host = test_host(); + imap::ClientBuilder::new(&host, test_imaps_port()) .connect(|domain, tcp| { let ssl_conn = tls(); Ok(native_tls::TlsConnector::connect(&ssl_conn, domain, tcp).unwrap()) @@ -124,6 +174,8 @@ fn inbox() { .unwrap(); s.send(e.into()).unwrap(); + wait_for_delivery(); + // now we should see the e-mails! let inbox = c.search("ALL").unwrap(); assert_eq!(inbox.len(), 2); @@ -145,12 +197,11 @@ fn inbox() { .any(|m| m == &imap::types::UnsolicitedResponse::Recent(2))); // let's see that we can also fetch the e-mails - let fetch = c.fetch("1", "(ALL UID)").unwrap(); + let fetch = c.fetch("1", "(ENVELOPE INTERNALDATE UID)").unwrap(); assert_eq!(fetch.len(), 1); let fetch = fetch.iter().next().unwrap(); assert_eq!(fetch.message, 1); assert_ne!(fetch.uid, None); - assert_eq!(fetch.size, Some(138)); let e = fetch.envelope().unwrap(); assert_eq!(e.subject, Some(b"My first e-mail"[..].into())); assert_ne!(e.from, None); @@ -240,6 +291,8 @@ fn inbox_uid() { .unwrap(); s.send(e.into()).unwrap(); + wait_for_delivery(); + // now we should see the e-mail! let inbox = c .uid_sort(&[SortCriterion::Subject], SortCharset::Utf8, "ALL") @@ -263,7 +316,9 @@ fn inbox_uid() { .any(|m| m == &imap::types::UnsolicitedResponse::Recent(1))); // let's see that we can also fetch the e-mail - let fetch = c.uid_fetch(format!("{}", uid), "(ALL UID)").unwrap(); + let fetch = c + .uid_fetch(format!("{}", uid), "(ENVELOPE INTERNALDATE FLAGS UID)") + .unwrap(); assert_eq!(fetch.len(), 1); let fetch = fetch.iter().next().unwrap(); assert_eq!(fetch.uid, Some(uid)); @@ -288,7 +343,7 @@ fn list() { let mut s = session("readonly-test@localhost"); s.select("INBOX").unwrap(); let subdirs = s.list(None, Some("%")).unwrap(); - assert_eq!(subdirs.len(), 0); + assert_eq!(subdirs.len(), 1); // TODO: make a subdir } @@ -323,7 +378,7 @@ fn append() { let uid = inbox.into_iter().next().unwrap(); // fetch the e-mail - let fetch = c.uid_fetch(format!("{}", uid), "(ALL UID)").unwrap(); + let fetch = c.uid_fetch(format!("{}", uid), "(ENVELOPE UID)").unwrap(); assert_eq!(fetch.len(), 1); let fetch = fetch.iter().next().unwrap(); assert_eq!(fetch.uid, Some(uid)); @@ -374,7 +429,9 @@ fn append_with_flags() { let uid = inbox.into_iter().next().unwrap(); // fetch the e-mail - let fetch = c.uid_fetch(format!("{}", uid), "(ALL UID)").unwrap(); + let fetch = c + .uid_fetch(format!("{}", uid), "(ENVELOPE FLAGS UID)") + .unwrap(); assert_eq!(fetch.len(), 1); let fetch = fetch.iter().next().unwrap(); assert_eq!(fetch.uid, Some(uid)); @@ -434,7 +491,9 @@ fn append_with_flags_and_date() { let uid = inbox.into_iter().next().unwrap(); // fetch the e-mail - let fetch = c.uid_fetch(format!("{}", uid), "(ALL UID)").unwrap(); + let fetch = c + .uid_fetch(format!("{}", uid), "(INTERNALDATE UID)") + .unwrap(); assert_eq!(fetch.len(), 1); let fetch = fetch.iter().next().unwrap(); assert_eq!(fetch.uid, Some(uid));