Skip to content

Commit

Permalink
Add Travis integration testing
Browse files Browse the repository at this point in the history
This uses [GreenMail's Docker
image](http://www.icegreen.com/greenmail/#deploy_docker_standalone) to
spin up a real SMTP+IMAP server on Travis, and then runs a series of
integration tests against it by sending e-mails using
[`lettre`](https://crates.io/crates/lettre) and checking that we can
receive them correctly.

A start on #101.
  • Loading branch information
jonhoo committed Nov 23, 2018
1 parent 8299a6b commit c393fd7
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ addons:
packages:
- libssl-dev

services:
- docker

os:
- linux
- osx
Expand All @@ -23,6 +26,10 @@ script:
- cargo check --all-targets
- cargo test

before_install:
- docker pull greenmail/standalone:1.5.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.5.8

after_success:
- if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" ]]; then
bash <(curl https://raw.githubusercontent.com/xd009642/tarpaulin/master/travis-install.sh);
Expand Down
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,11 @@ bufstream = "0.1"
imap-proto = "0.6"
nom = "4.0"
base64 = "0.10"

[dev-dependencies]
lettre = "0.9"
lettre_email = "0.9"

[patch.crates-io]
lettre = { git = "https://github.com/lettre/lettre.git" }
lettre_email = { git = "https://github.com/lettre/lettre.git" }
215 changes: 215 additions & 0 deletions tests/integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
extern crate imap;
extern crate lettre;
extern crate lettre_email;
extern crate native_tls;

use lettre::Transport;
use std::net::TcpStream;

fn tls() -> native_tls::TlsConnector {
native_tls::TlsConnector::builder()
.danger_accept_invalid_certs(true)
.build()
.unwrap()
}

fn session(user: &str) -> imap::Session<native_tls::TlsStream<TcpStream>> {
let mut s = imap::connect("127.0.0.1:3993", "imap.example.com", &tls())
.unwrap()
.login(user, user)
.unwrap();
s.debug = true;
s
}

fn smtp(user: &str) -> lettre::SmtpTransport {
let creds = lettre::smtp::authentication::Credentials::new(user.to_string(), user.to_string());
lettre::SmtpClient::new(
"127.0.0.1:3465",
lettre::ClientSecurity::Wrapper(lettre::ClientTlsParameters {
connector: tls(),
domain: "smpt.example.com".to_string(),
}),
)
.unwrap()
.credentials(creds)
.transport()
}

#[test]
fn connect_insecure() {
imap::connect_insecure("127.0.0.1:3143").unwrap();
}

#[test]
#[ignore]
fn connect_insecure_then_secure() {
// ignored because of https://github.com/greenmail-mail-test/greenmail/issues/135
imap::connect_insecure("127.0.0.1:3143")
.unwrap()
.secure("imap.example.com", &tls())
.unwrap();
}

#[test]
fn connect_secure() {
imap::connect("127.0.0.1:3993", "imap.example.com", &tls()).unwrap();
}

#[test]
fn login() {
session("readonly-test@localhost");
}

#[test]
fn logout() {
let mut s = session("readonly-test@localhost");
s.logout().unwrap();
}

#[test]
#[ignore]
fn inbox_zero() {
// https://github.com/djc/tokio-imap/issues/34
let mut s = session("readonly-test@localhost");
s.select("INBOX").unwrap();
let inbox = s.search("ALL").unwrap();
assert_eq!(inbox.len(), 0);
}

#[test]
fn inbox() {
let to = "inbox@localhost";

// first log in so we'll see the unsolicited e-mails
let mut c = session(to);
c.select("INBOX").unwrap();

// then send the e-mail
let mut s = smtp(to);
let e = lettre_email::Email::builder()
.from("sender@localhost")
.to(to)
.subject("My first e-mail")
.text("Hello world from SMTP")
.build()
.unwrap();
s.send(e.into()).unwrap();

// now we should see the e-mail!
let inbox = c.search("ALL").unwrap();
// and the one message should have the first message sequence number
assert_eq!(inbox.len(), 1);
assert!(inbox.contains(&1));

// we should also get two unsolicited responses: Exists and Recent
c.noop().unwrap();
let mut unsolicited = Vec::new();
while let Ok(m) = c.unsolicited_responses.try_recv() {
unsolicited.push(m);
}
assert_eq!(unsolicited.len(), 2);
assert!(unsolicited
.iter()
.any(|m| m == &imap::types::UnsolicitedResponse::Exists(1)));
assert!(unsolicited
.iter()
.any(|m| m == &imap::types::UnsolicitedResponse::Recent(1)));

// let's see that we can also fetch the e-mail
let fetch = c.fetch("1", "(ALL UID)").unwrap();
assert_eq!(fetch.len(), 1);
let fetch = &fetch[0];
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("My first e-mail"));
assert_ne!(e.from, None);
assert_eq!(e.from.as_ref().unwrap().len(), 1);
let from = &e.from.as_ref().unwrap()[0];
assert_eq!(from.mailbox, Some("sender"));
assert_eq!(from.host, Some("localhost"));
assert_ne!(e.to, None);
assert_eq!(e.to.as_ref().unwrap().len(), 1);
let to = &e.to.as_ref().unwrap()[0];
assert_eq!(to.mailbox, Some("inbox"));
assert_eq!(to.host, Some("localhost"));

// and let's delete it to clean up
c.store("1", "+FLAGS (\\Deleted)").unwrap();
c.expunge().unwrap();

// the e-mail should be gone now
// TODO: https://github.com/djc/tokio-imap/issues/34
// let inbox = c.search("ALL").unwrap();
// assert_eq!(inbox.len(), 0);
}

#[test]
fn inbox_uid() {
let to = "inbox-uid@localhost";

// first log in so we'll see the unsolicited e-mails
let mut c = session(to);
c.select("INBOX").unwrap();

// then send the e-mail
let mut s = smtp(to);
let e = lettre_email::Email::builder()
.from("sender@localhost")
.to(to)
.subject("My first e-mail")
.text("Hello world from SMTP")
.build()
.unwrap();
s.send(e.into()).unwrap();

// now we should see the e-mail!
let inbox = c.uid_search("ALL").unwrap();
// and the one message should have the first message sequence number
assert_eq!(inbox.len(), 1);
let uid = inbox.into_iter().next().unwrap();

// we should also get two unsolicited responses: Exists and Recent
c.noop().unwrap();
let mut unsolicited = Vec::new();
while let Ok(m) = c.unsolicited_responses.try_recv() {
unsolicited.push(m);
}
assert_eq!(unsolicited.len(), 2);
assert!(unsolicited
.iter()
.any(|m| m == &imap::types::UnsolicitedResponse::Exists(1)));
assert!(unsolicited
.iter()
.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();
assert_eq!(fetch.len(), 1);
let fetch = &fetch[0];
assert_eq!(fetch.uid, Some(uid));
let e = fetch.envelope().unwrap();
assert_eq!(e.subject, Some("My first e-mail"));

// and let's delete it to clean up
c.uid_store(format!("{}", uid), "+FLAGS (\\Deleted)")
.unwrap();
c.expunge().unwrap();

// the e-mail should be gone now
// TODO: https://github.com/djc/tokio-imap/issues/34
// let inbox = c.search("ALL").unwrap();
// assert_eq!(inbox.len(), 0);
}

#[test]
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);

// TODO: make a subdir
}

0 comments on commit c393fd7

Please sign in to comment.