summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorAlexandros Frantzis <alf82@freemail.gr>2019-10-01 23:06:38 +0300
committerAlexandros Frantzis <alf82@freemail.gr>2019-10-01 23:36:26 +0300
commita054789ddb60ed1fab26e6d4e6bd36ed926273f1 (patch)
treebd3caad78a0e377816b78276889301d9276344b9 /tests
Initial public release
Diffstat (limited to 'tests')
-rw-r--r--tests/test_charset.rs104
-rw-r--r--tests/test_deliver.rs86
-rw-r--r--tests/test_encoded_words.rs78
-rw-r--r--tests/test_encoding.rs136
-rw-r--r--tests/test_fields.rs135
-rw-r--r--tests/test_processing.rs39
-rw-r--r--tests/test_regex.rs185
7 files changed, 763 insertions, 0 deletions
diff --git a/tests/test_charset.rs b/tests/test_charset.rs
new file mode 100644
index 0000000..3aa357b
--- /dev/null
+++ b/tests/test_charset.rs
@@ -0,0 +1,104 @@
+// Copyright 2019 Alexandros Frantzis
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// SPDX-License-Identifier: MPL-2.0
+
+use mda::{Email, EmailRegex};
+
+static TEST_EMAIL_ISO_BASE64: &'static str = r#"Return-Path: <me@source.com>
+To: Destination <someone.else@destination.com>
+Content-Type: text/plain; charset="iso-8859-7"
+Content-Transfer-Encoding: base64
+
+tuvr4SDm5/Tl3yDnIPj1994g8+/1LCDj6Scg3Ovr4SDq6+Hf5em3CvTv7SDd8OHp7e8g9O/1IMTe
+7O/1IOrh6SD0+e0g0+/26fP0/u0sCvThIOT98+rv6+Eg6uHpIPQnIOHt5er03+zn9OEgxf3j5bcK
+9OftIMHj7/HcLCD07yDI3eH08e8sIOrh6SD07/XyINP05fbc7e/18i4=
+"#;
+
+static TEST_EMAIL_ISO_8BIT: &'static [u8] = &[
+ b'C', b'o', b'n', b't', b'e', b'n', b't', b'-', b'T', b'y', b'p', b'e',
+ b':', b' ', b't', b'e', b'x', b't', b'/', b'p', b'l', b'a', b'i', b'n',
+ b';', b' ', b'c', b'h', b'a', b'r', b's', b'e', b't', b'=', b'"', b'i',
+ b's', b'o', b'-', b'8', b'8', b'5', b'9', b'-', b'7', b'"', b'\r', b'\n',
+ b'C', b'o', b'n', b't', b'e', b'n', b't', b'-', b'T', b'r', b'a', b'n',
+ b's', b'f', b'e', b'r', b'-', b'E', b'n', b'c', b'o', b'd', b'i', b'n',
+ b'g', b':', b' ', b'8', b'b', b'i', b't', b'\r', b'\n',
+ b'\r', b'\n',
+ 0xb6, 0xeb, 0xe1, 0x20, 0xe6, 0xe7, 0xf4, 0xe5, 0xdf, 0x20, 0xe7, 0x20,
+ 0xf8, 0xf5, 0xf7, 0xde, 0x20, 0xf3, 0xef, 0xf5, 0x2c, 0x20, 0xe3, 0xe9,
+ 0x27, 0x20, 0xdc, 0xeb, 0xe1, 0x20, 0xea, 0xeb, 0xe1, 0xdf, 0xe5, 0xe9,
+ 0xb7, 0x0a, 0xf4, 0xef, 0xed, 0x20, 0xdd, 0xf0, 0xe1, 0xe9, 0xed, 0xef,
+ 0x20, 0xf4, 0xef, 0xf5, 0x20, 0xc4, 0xde, 0xec, 0xef, 0xf5, 0x20, 0xea,
+ 0xe1, 0xe9, 0x20, 0xf4, 0xf9, 0xed, 0x20, 0xd3, 0xef, 0xf6, 0xe9, 0xf3,
+ 0xf4, 0xfe, 0xed, 0x2c, 0x0a, 0xf4, 0xe1, 0x20, 0xe4, 0xfd, 0xf3, 0xea,
+ 0xef, 0xeb, 0xe1, 0x20, 0xea, 0xe1, 0xe9, 0x20, 0xf4, 0x27, 0x20, 0xe1,
+ 0xed, 0xe5, 0xea, 0xf4, 0xdf, 0xec, 0xe7, 0xf4, 0xe1, 0x20, 0xc5, 0xfd,
+ 0xe3, 0xe5, 0xb7, 0x0a, 0xf4, 0xe7, 0xed, 0x20, 0xc1, 0xe3, 0xef, 0xf1,
+ 0xdc, 0x2c, 0x20, 0xf4, 0xef, 0x20, 0xc8, 0xdd, 0xe1, 0xf4, 0xf1, 0xef,
+ 0x2c, 0x20, 0xea, 0xe1, 0xe9, 0x20, 0xf4, 0xef, 0xf5, 0xf2, 0x20, 0xd3,
+ 0xf4, 0xe5, 0xf6, 0xdc, 0xed, 0xef, 0xf5, 0xf2, 0x2e
+];
+
+static TEST_EMAIL_MULTIPART_ISO: &'static str = r#"Return-Path: <me@source.com>
+To: Destination <someone.else@destination.com>
+Content-type: multipart/alternative; boundary="XtT01VFrJIenjlg+ZCXSSWq4"
+
+--XtT01VFrJIenjlg+ZCXSSWq4
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: base64
+
+Sample US-ASCII text.
+--XtT01VFrJIenjlg+ZCXSSWq4
+Content-type: multipart/alternative; boundary="2c+OeCbICgJrtINI5EFlsI6G"
+
+--2c+OeCbICgJrtINI5EFlsI6G
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+
+zprOuSDhvILOvSDPgM+Ez4nPh865zrrhvbQgz4ThvbTOvSDOss+B4b+Hz4IsIOG8oSDhvLjOuM6s
+zrrOtyDOtOG9ss69IM+D4b2yIM6zzq3Ou86xz4POtS4K4bycz4TPg865IM+Dzr/PhuG9uM+CIM+A
+zr/hvbog4byUzrPOuc69zrXPgiwgzrzhvbIgz4TPjM+Dzrcgz4DOtc6vz4HOsSwK4bykzrTOtyDO
+uOG9sCDPhOG9uCDOus6xz4TOrM67zrHOss61z4Ig4b6RIOG8uM64zqzOus61z4Igz4TOryDPg863
+zrzOsc6vzr3Ov8+Fzr0uCg==
+--2c+OeCbICgJrtINI5EFlsI6G
+Content-Type: image/jpeg;
+Content-Transfer-Encoding: base64
+
+SSBhbSBzb3JyeSBEYXZlLCBJbSBhZnJhaWQgSSBjYW50IGRvIHRoYXQK
+
+--2c+OeCbICgJrtINI5EFlsI6G--
+
+--XtT01VFrJIenjlg+ZCXSSWq4
+Content-Type: text/plain; charset="iso-8859-7"
+Content-Transfer-Encoding: base64
+
+tuvr4SDm5/Tl3yDnIPj1994g8+/1LCDj6Scg3Ovr4SDq6+Hf5em3CvTv7SDd8OHp7e8g9O/1IMTe
+7O/1IOrh6SD0+e0g0+/26fP0/u0sCvThIOT98+rv6+Eg6uHpIPQnIOHt5er03+zn9OEgxf3j5bcK
+9OftIMHj7/HcLCD07yDI3eH08e8sIOrh6SD07/XyINP05fbc7e/18i4=
+--XtT01VFrJIenjlg+ZCXSSWq4--
+"#;
+
+#[test]
+fn email_with_charset_is_decoded() {
+ let email = Email::from_vec(TEST_EMAIL_ISO_BASE64.to_string().into_bytes()).unwrap();
+
+ assert!(email.body().search(r"τα δύσκολα και τ' ανεκτίμητα Εύγε·").unwrap());
+}
+
+#[test]
+fn email_with_charset_8bit_is_decoded() {
+ let email = Email::from_vec(TEST_EMAIL_ISO_8BIT.to_vec()).unwrap();
+
+ assert!(email.body().search(r"τα δύσκολα και τ' ανεκτίμητα Εύγε·").unwrap());
+}
+
+#[test]
+fn email_part_with_charset_is_decoded() {
+ let email = Email::from_vec(TEST_EMAIL_MULTIPART_ISO.as_bytes().to_vec()).unwrap();
+
+ assert!(email.body().search(r"Sample US-ASCII text.").unwrap());
+ assert!(email.body().search(r"τα δύσκολα και τ' ανεκτίμητα Εύγε·").unwrap());
+}
diff --git a/tests/test_deliver.rs b/tests/test_deliver.rs
new file mode 100644
index 0000000..ac80664
--- /dev/null
+++ b/tests/test_deliver.rs
@@ -0,0 +1,86 @@
+// Copyright 2019 Alexandros Frantzis
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// SPDX-License-Identifier: MPL-2.0
+
+use mda::Email;
+use tempfile;
+use std::fs;
+use std::os::unix::fs as unix_fs;
+
+#[test]
+fn creates_maildir_dir_structure() {
+ let tmpdir = tempfile::tempdir().unwrap();
+
+ let email = Email::from_vec(Vec::new()).unwrap();
+ email.deliver_to_maildir(tmpdir.path()).unwrap();
+
+ let entries: Vec<_> = fs::read_dir(tmpdir.path()).unwrap().collect();
+
+ let dir_named = |x,e: &fs::DirEntry| e.path() == tmpdir.path().join(x) &&
+ e.metadata().unwrap().is_dir();
+
+ assert_eq!(entries.len(), 3);
+ assert_eq!(entries.iter().filter(|e| dir_named("new", e.as_ref().unwrap())).count(), 1);
+ assert_eq!(entries.iter().filter(|e| dir_named("tmp", e.as_ref().unwrap())).count(), 1);
+ assert_eq!(entries.iter().filter(|e| dir_named("cur", e.as_ref().unwrap())).count(), 1);
+}
+
+#[test]
+fn delivers_to_maildir_new() {
+ let tmpdir = tempfile::tempdir().unwrap();
+ let data = [1, 3, 5, 7, 11];
+
+ let email = Email::from_vec(data.to_vec()).unwrap();
+ email.deliver_to_maildir(tmpdir.path()).unwrap();
+
+ let new_entries: Vec<_> = fs::read_dir(tmpdir.path().join("new")).unwrap().collect();
+ let tmp_entries: Vec<_> = fs::read_dir(tmpdir.path().join("tmp")).unwrap().collect();
+ let cur_entries: Vec<_> = fs::read_dir(tmpdir.path().join("cur")).unwrap().collect();
+
+ assert_eq!(new_entries.len(), 1);
+
+ let file_contents = fs::read(new_entries[0].as_ref().unwrap().path()).unwrap();
+ assert_eq!(file_contents, &data);
+
+ assert_eq!(tmp_entries.len(), 0);
+ assert_eq!(cur_entries.len(), 0);
+}
+
+#[test]
+fn keeps_old_maildir_data() {
+ let tmpdir = tempfile::tempdir().unwrap();
+
+ let data1 = [1, 3, 5, 7, 11];
+ let email1 = Email::from_vec(data1.to_vec()).unwrap();
+ let path1 = email1.deliver_to_maildir(tmpdir.path()).unwrap();
+
+ let data2 = [2, 4, 6, 8, 12];
+ let email2 = Email::from_vec(data2.to_vec()).unwrap();
+ let path2 = email2.deliver_to_maildir(tmpdir.path()).unwrap();
+
+ let new_entries: Vec<_> = fs::read_dir(tmpdir.path().join("new")).unwrap().collect();
+
+ assert_eq!(new_entries.len(), 2);
+ assert_eq!(new_entries.iter().filter(|e| e.as_ref().unwrap().path() == path1).count(), 1);
+ assert_eq!(new_entries.iter().filter(|e| e.as_ref().unwrap().path() == path2).count(), 1);
+
+ assert_eq!(fs::read(path1).unwrap(), &data1);
+ assert_eq!(fs::read(path2).unwrap(), &data2);
+}
+
+#[test]
+fn deals_with_soft_link_path() {
+ let tmpdir = tempfile::tempdir().unwrap();
+ let subdir = tmpdir.path().join("subdir");
+ let symlink = tmpdir.path().join("symlink");
+
+ fs::create_dir(&subdir).unwrap();
+ unix_fs::symlink(&subdir, &symlink).unwrap();
+
+ let email = Email::from_vec(Vec::new()).unwrap();
+ email.deliver_to_maildir(&symlink).unwrap();
+}
diff --git a/tests/test_encoded_words.rs b/tests/test_encoded_words.rs
new file mode 100644
index 0000000..926cfea
--- /dev/null
+++ b/tests/test_encoded_words.rs
@@ -0,0 +1,78 @@
+// Copyright 2019 Alexandros Frantzis
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// SPDX-License-Identifier: MPL-2.0
+
+use mda::{Email, EmailRegex};
+
+static TEST_EMAIL_MULTIPART: &'static str = r#"Return-Path: <me@source.com>
+To: =?iso-8859-1?q?=C0a_b=DF?= <someone.else1@destination.com>,
+ =?utf-8?b?zqXOps6nzqjOqQo=?= <someone.else2@destination.com>,
+Cc: =?iso-8859-1?q?=C0 b?= <someone.else3@destination.com>
+Bcc: =?utf8?B?zpbOl86YCg=?= <someone.else4@destination.com>
+Content-type: multipart/alternative; boundary="XtT01VFrJIenjlg+ZCXSSWq4"
+
+--XtT01VFrJIenjlg+ZCXSSWq4
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: base64
+X-header-field: =?UTF-8?B?zpHOks6TCg==?=
+
+--XtT01VFrJIenjlg+ZCXSSWq4--
+"#;
+
+static TEST_EMAIL_INVALID_UTF8: &'static str =
+ r#"Subject: =?utf-8?B?zojOus60zr/Pg863IGUtzrvOv86zzrHPgc65zrHPg868zr/P?="#;
+
+static TEST_EMAIL_MULTI_ENC_WORD: &'static str = r#"Return-Path: <me@source.com>
+Subject: =?utf-8?b?TXkgbXVsdGkgZW5jb2RlZC0=?=
+ =?utf-8?b?d29yZCBzdWJqZWN0IGw=?=
+ =?utf-8?b?aW5l?=
+"#;
+
+#[test]
+fn encoded_word_is_decoded() {
+ let email = Email::from_vec(TEST_EMAIL_MULTIPART.to_string().into_bytes()).unwrap();
+
+ assert!(email.data().search(r"Àa bß").unwrap());
+ assert!(email.header_field("To").unwrap().contains(r"Àa bß"));
+ assert!(!email.data().search(r"=C0a_b=DF").unwrap());
+ assert!(!email.header_field("To").unwrap().contains(r"=C0a_b=DF"));
+
+ assert!(email.data().search(r"ΥΦΧΨΩ").unwrap());
+ assert!(email.header_field("To").unwrap().contains(r"ΥΦΧΨΩ"));
+ assert!(!email.data().search(r"zqXOps6nzqjOqQo=").unwrap());
+ assert!(!email.header_field("To").unwrap().contains(r"zqXOps6nzqjOqQo="));
+
+ assert!(email.data().search(r"ΑΒΓ").unwrap());
+ assert!(!email.data().search(r"zpHOks6TCg==").unwrap());
+}
+
+#[test]
+fn invalid_encoded_word_is_not_decoded() {
+ let email = Email::from_vec(TEST_EMAIL_MULTIPART.to_string().into_bytes()).unwrap();
+
+ assert!(!email.data().search(r"À b").unwrap());
+ assert!(email.data().search(r"=C0 b").unwrap());
+
+ assert!(!email.data().search(r"ΖΗΘ").unwrap());
+ assert!(email.data().search(r"zpbOl86YCg=").unwrap());
+}
+
+#[test]
+fn invalid_charset_encoding_in_encoded_word_is_partially_decoded() {
+ let email = Email::from_vec(TEST_EMAIL_INVALID_UTF8.to_string().into_bytes()).unwrap();
+
+ assert!(email.data().search("Έκδοση e-λογαριασμο\u{FFFD}").unwrap());
+ assert!(email.header_field("Subject").unwrap().contains("Έκδοση e-λογαριασμο\u{FFFD}"));
+}
+
+#[test]
+fn multpile_encoded_words_are_concatenated() {
+ let email = Email::from_vec(TEST_EMAIL_MULTI_ENC_WORD.to_string().into_bytes()).unwrap();
+
+ assert!(email.data().search("My multi encoded-word subject line").unwrap());
+ assert!(email.header_field("Subject").unwrap().contains("My multi encoded-word subject line"));
+}
diff --git a/tests/test_encoding.rs b/tests/test_encoding.rs
new file mode 100644
index 0000000..1579449
--- /dev/null
+++ b/tests/test_encoding.rs
@@ -0,0 +1,136 @@
+// Copyright 2019 Alexandros Frantzis
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// SPDX-License-Identifier: MPL-2.0
+
+use mda::{Email, EmailRegex};
+
+static TEST_EMAIL_BASE64: &'static str = r#"Return-Path: <me@source.com>
+To: Destination <someone.else@destination.com>
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+
+VGhlIGFudGVjaGFwZWwgd2hlcmUgdGhlIHN0YXR1ZSBzdG9vZApPZiBOZXd0b24gd2l0aCBoaXMg
+cHJpc20gYW5kIHNpbGVudCBmYWNlLApUaGUgbWFyYmxlIGluZGV4IG9mIGEgbWluZCBmb3IgZXZl
+cgpWb3lhZ2luZyB0aHJvdWdoIHN0cmFuZ2Ugc2VhcyBvZiBUaG91Z2h0LCBhbG9uZS4gCg==
+"#;
+
+static TEST_EMAIL_MULTIPART: &'static str = r#"Return-Path: <me@source.com>
+To: Destination <someone.else@destination.com>
+Content-type: multipart/alternative; boundary="XtT01VFrJIenjlg+ZCXSSWq4"
+
+--XtT01VFrJIenjlg+ZCXSSWq4
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+
+VGhlIGFudGVjaGFwZWwgd2hlcmUgdGhlIHN0YXR1ZSBzdG9vZApPZiBOZXd0b24gd2l0aCBoaXMg
+cHJpc20gYW5kIHNpbGVudCBmYWNlLApUaGUgbWFyYmxlIGluZGV4IG9mIGEgbWluZCBmb3IgZXZl
+cgpWb3lhZ2luZyB0aHJvdWdoIHN0cmFuZ2Ugc2VhcyBvZiBUaG91Z2h0LCBhbG9uZS4gCg==
+--XtT01VFrJIenjlg+ZCXSSWq4
+Content-type: multipart/alternative; boundary="2c+OeCbICgJrtINI5EFlsI6G"
+
+--2c+OeCbICgJrtINI5EFlsI6G
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+
+zprOuSDhvILOvSDPgM+Ez4nPh865zrrhvbQgz4ThvbTOvSDOss+B4b+Hz4IsIOG8oSDhvLjOuM6s
+zrrOtyDOtOG9ss69IM+D4b2yIM6zzq3Ou86xz4POtS4K4bycz4TPg865IM+Dzr/PhuG9uM+CIM+A
+zr/hvbog4byUzrPOuc69zrXPgiwgzrzhvbIgz4TPjM+Dzrcgz4DOtc6vz4HOsSwK4bykzrTOtyDO
+uOG9sCDPhOG9uCDOus6xz4TOrM67zrHOss61z4Ig4b6RIOG8uM64zqzOus61z4Igz4TOryDPg863
+zrzOsc6vzr3Ov8+Fzr0uCg==
+--2c+OeCbICgJrtINI5EFlsI6G
+Content-Type: image/jpeg;
+Content-Transfer-Encoding: base64
+
+SSBhbSBzb3JyeSBEYXZlLCBJbSBhZnJhaWQgSSBjYW50IGRvIHRoYXQK
+
+--2c+OeCbICgJrtINI5EFlsI6G--
+
+--XtT01VFrJIenjlg+ZCXSSWq4
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+
+T3VyIHBvc3R1cmluZ3MsIG91ciBpbWFnaW5lZCBzZWxmLWltcG9ydGFuY2UsIHRoZSBkZWx1c2lv
+biB0aGF0IHdlIGhhdmUgc29tZSBwcml2aWxlZ2VkIHBvc2l0aW9uIGluIHRoZSBVbml2ZXJzZSwg
+YXJlIGNoYWxsZW5nZWQgYnkgdGhpcyBwb2ludCBvZiBwYWxlIGxpZ2h0LiBPdXIgcGxhbmV0IGlz
+IGEgbG9uZWx5IHNwZWNrIGluIHRoZSBncmVhdCBlbnZlbG9waW5nIGNvc21pYyBkYXJrLg==
+--XtT01VFrJIenjlg+ZCXSSWq4--
+"#;
+
+static TEST_EMAIL_INVALID_BASE64: &'static str = r#"Return-Path: <me@source.com>
+To: Destination <someone.else@destination.com>
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+
+VGhlIGFudGVjaGFwZWwgd2hlcmUgdGhlIHN0YXR1ZSBzdG9vZApPZiBOZXd0b24gd2l0aCBoaXMg
+cHJpc20gYW5kIHNpbGVudCBmYWNlLApUaGUgbWFyYmxlIGluZGV4IG9mIGEgbWluZCBmb3IgZXZl
+cgpWb3lhZ2luZyB0aHJvdWdoIHN0cmFuZ2Ugc2VhcyBvZiBUaG91Z2h0LCBhbG9uZS4gCg====
+"#;
+
+static TEST_EMAIL_QP: &'static str = r#"Return-Path: <me@source.com>
+To: Destination <someone.else@destination.com>
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: quoted-printable
+
+=54=68=65=20=61=6E=74=65=63=68=61=70=65=6C=20=77=68=65=72=65=20=74=68=
+=65=20=73=74=61=74=75=65=20=73=74=6F=6F=64
+=4F=66=20=4E=65=77=74=6F=6E=20=77=69=74=68=20=68=69=73=20=70=72=69=73=
+=6D=20=61=6E=64=20=73=69=6C=65=6E=74=20=66=61=63=65=2C
+=54=68=65=20=6D=61=72=62=6C=65=20=69=6E=64=65=78=20=6F=66=20=61=20=6D=
+=69=6E=64=20=66=6F=72=20=65=76=65=72
+=56=6F=79=61=67=69=6E=67=20=74=68=72=6F=75=67=68=20=73=74=72=61=6E=67=
+=65=20=73=65=61=73=20=6F=66=20=54=68=6F=75=67=68=74=2C=20=61=6C=6F=6E=
+=65=2E=20
+"#;
+
+#[test]
+fn base64_email_is_decoded() {
+ let email = Email::from_vec(TEST_EMAIL_BASE64.to_string().into_bytes()).unwrap();
+
+ assert!(email.body().search(r"a\smind\sfor\sever\svoyaging").unwrap());
+}
+
+#[test]
+fn base64_parts_are_decoded() {
+ let email = Email::from_vec(TEST_EMAIL_MULTIPART.to_string().into_bytes()).unwrap();
+
+ // First level part.
+ assert!(email.body().search(r"a\smind\sfor\sever\svoyaging").unwrap());
+ // Second level nested part.
+ assert!(email.body().search(r"ἤδη θὰ τὸ κατάλαβες ᾑ Ἰθάκες τί σημαίνουν").unwrap());
+ // First level part after end of previous nested subparts.
+ assert!(email.body().search(r"are challenged by this point of pale light").unwrap());
+}
+
+#[test]
+fn non_text_base64_is_not_decoded() {
+ let email = Email::from_vec(TEST_EMAIL_MULTIPART.to_string().into_bytes()).unwrap();
+
+ assert!(!email.body().search(r"I am sorry Dave").unwrap());
+}
+
+#[test]
+fn invalid_base64_is_not_decoded() {
+ let email = Email::from_vec(TEST_EMAIL_INVALID_BASE64.to_string().into_bytes()).unwrap();
+
+ assert!(!email.body().search(r"a\smind\sfor\sever\svoyaging").unwrap());
+ assert!(email.body().search(r"4gCg=").unwrap());
+}
+
+#[test]
+fn qp_email_is_decoded() {
+ let email = Email::from_vec(TEST_EMAIL_QP.to_string().into_bytes()).unwrap();
+
+ assert!(email.body().search(r"a\smind\sfor\sever\svoyaging").unwrap());
+}
+
+#[test]
+fn raw_data_is_not_decoded() {
+ let email = Email::from_vec(TEST_EMAIL_MULTIPART.to_string().into_bytes()).unwrap();
+
+ assert!(email.raw_data().search(r"vZiBUaG91Z2h0LCBhbG9uZS4gCg==").unwrap());
+ assert!(!email.raw_data().search(r"ἤδη θὰ τὸ κατάλαβες ᾑ Ἰθάκες τί σημαίνουν").unwrap());
+}
diff --git a/tests/test_fields.rs b/tests/test_fields.rs
new file mode 100644
index 0000000..ab43241
--- /dev/null
+++ b/tests/test_fields.rs
@@ -0,0 +1,135 @@
+// Copyright 2019 Alexandros Frantzis
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// SPDX-License-Identifier: MPL-2.0
+
+use mda::Email;
+
+static TEST_EMAIL: &'static str = "Return-Path: <me@source.com>
+Multi: multi1
+To: Destination <someone.else@destination.com>
+Cc: firstcc <firstcc@destination.com>,
+ secondcc <secondcc@destination.com>,
+\tthirsdcc <secondcc@destination.com>
+Multi: multi2
+Multi: multi3
+ multi3.1
+
+To: Body <body@destination.com>
+Multi: multibody
+BodyField: body
+Body body body
+";
+
+static TEST_EMAIL_NO_BODY: &'static str = "Return-Path: <me@source.com>
+Multi: multi1
+To: Destination <someone.else@destination.com>
+Cc: firstcc <firstcc@destination.com>,
+ secondcc <secondcc@destination.com>,
+ thirsdcc <secondcc@destination.com>
+";
+
+static TEST_EMAIL_CRLF: &'static str = "Return-Path: <me@source.com>\r
+Multi: multi1\r
+To: Destination <someone.else@destination.com>\r
+Cc: firstcc <firstcc@destination.com>,\r
+ secondcc <secondcc@destination.com>,\r
+ thirsdcc <secondcc@destination.com>\r
+Multi: multi2\r
+Multi: multi3\r
+ multi3.1
+";
+
+#[test]
+fn parses_single_line_fields() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+ assert_eq!(
+ email.header_field("To").unwrap().trim(),
+ "Destination <someone.else@destination.com>"
+ );
+ assert_eq!(
+ email.header_field("Return-Path").unwrap().trim(),
+ "<me@source.com>"
+ );
+}
+
+#[test]
+fn parses_multi_line_fields() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+ assert_eq!(
+ email.header_field("Cc").unwrap().trim(),
+ "firstcc <firstcc@destination.com>, secondcc <secondcc@destination.com>,\t\
+ thirsdcc <secondcc@destination.com>"
+ );
+}
+
+#[test]
+fn field_names_are_case_insensitive() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ assert_eq!(
+ email.header_field("return-path").unwrap().trim(),
+ "<me@source.com>"
+ );
+ assert_eq!(
+ email.header_field("ReTuRn-PaTh").unwrap().trim(),
+ "<me@source.com>"
+ );
+}
+
+#[test]
+fn non_existent_field_is_none() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+ assert!(email.header_field("BodyField").is_none());
+}
+
+#[test]
+fn fields_with_multiple_occurrences_return_all() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ let multi = email.header_field_all_occurrences("Multi").unwrap();
+
+ assert_eq!(multi.len(), 3);
+ assert_eq!(multi.iter().filter(|e| e.trim() == "multi1").count(), 1);
+ assert_eq!(multi.iter().filter(|e| e.trim() == "multi2").count(), 1);
+ assert_eq!(multi.iter().filter(|e| e.trim() == "multi3 multi3.1").count(), 1);
+}
+
+#[test]
+fn field_with_multiple_occurrences_returns_first() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ assert_eq!(email.header_field("Multi").unwrap().trim(), "multi1");
+}
+
+#[test]
+fn all_occurrences_of_non_existent_field_is_none() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ assert!(email.header_field_all_occurrences("BodyField").is_none())
+}
+
+#[test]
+fn header_with_no_body_is_parsed_fully() {
+ let email = Email::from_vec(TEST_EMAIL_NO_BODY.to_string().into_bytes()).unwrap();
+
+ assert_eq!(
+ email.header_field("Cc").unwrap().trim(),
+ "firstcc <firstcc@destination.com>, secondcc <secondcc@destination.com>, \
+ thirsdcc <secondcc@destination.com>"
+ );
+}
+
+#[test]
+fn header_using_crlf() {
+ let email = Email::from_vec(TEST_EMAIL_CRLF.to_string().into_bytes()).unwrap();
+
+ assert_eq!(
+ email.header_field("Cc").unwrap().trim(),
+ "firstcc <firstcc@destination.com>, secondcc <secondcc@destination.com>, \
+ thirsdcc <secondcc@destination.com>"
+ );
+}
diff --git a/tests/test_processing.rs b/tests/test_processing.rs
new file mode 100644
index 0000000..7bdfdd1
--- /dev/null
+++ b/tests/test_processing.rs
@@ -0,0 +1,39 @@
+// Copyright 2019 Alexandros Frantzis
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// SPDX-License-Identifier: MPL-2.0
+
+use mda::Email;
+
+static TEST_EMAIL: &'static str = "Return-Path: <me@source.com>
+To: Destination <someone.else@destination.com>
+Cc: firstcc <firstcc@destination.com>,
+ secondcc <secondcc@destination.com>,
+ thirsdcc <secondcc@destination.com>
+
+To: Body <body@destination.com>
+Body body body
+";
+
+#[test]
+fn filtering_creates_new_email() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ let email = email.filter(&["sed", "s/destination.com/newdest.com/g"]).unwrap();
+
+ assert_eq!(email.header_field("To").unwrap().trim(), "Destination <someone.else@newdest.com>");
+}
+
+#[test]
+fn processing_returns_output() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ let output_dest = email.process(&["grep", "Destination"]).unwrap();
+ let output_some = email.process(&["grep", "SomeInexistentString"]).unwrap();
+
+ assert_eq!(output_dest.status.code().unwrap(), 0);
+ assert_eq!(output_some.status.code().unwrap(), 1);
+}
diff --git a/tests/test_regex.rs b/tests/test_regex.rs
new file mode 100644
index 0000000..16c879a
--- /dev/null
+++ b/tests/test_regex.rs
@@ -0,0 +1,185 @@
+// Copyright 2019 Alexandros Frantzis
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// SPDX-License-Identifier: MPL-2.0
+
+use mda::{Email, EmailRegex};
+
+static TEST_EMAIL: &'static str = "Return-Path: <me@source.com>
+To: Destination <someone.else@destination.com>
+Cc: firstcc <firstcc@destination.com>,
+ secondcc <secondcc@destination.com>,
+ thirsdcc <thirdcc@destination.com>
+X-Test-Field: name123=value456
+Content-Type: text/plain; charset=utf-8
+
+To: Body <body@destination.com>
+Body body body
+Σὰ βγεῖς στὸν πηγαιμὸ γιὰ τὴν Ἰθάκη,
+νὰ εὔχεσαι νἆναι μακρὺς ὁ δρόμος,
+γεμάτος περιπέτειες, γεμάτος γνώσεις.
+";
+
+#[test]
+fn header_search() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ assert!(email.header().search(r"^(Cc|To).*someone\.else@destination\.com").unwrap());
+ assert!(!email.header().search(r"^(Cc|To).*body@destination\.com") .unwrap());
+}
+
+#[test]
+fn header_search_multiline() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ assert!(email.header().search(r"^Cc.*secondcc@destination\.com").unwrap());
+ assert!(email.header().search(r"^Cc.*thirdcc@destination\.com").unwrap());
+}
+
+#[test]
+fn body_search() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ assert!(email.body().search(r"^(Cc|To).*body@destination\.com").unwrap());
+ assert!(!email.body().search(r"^(Cc|To).*someone\.else@destination\.com").unwrap());
+}
+
+#[test]
+fn data_search() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ assert!(email.data().search(r"^(Cc|To).*firstcc@destination\.com").unwrap());
+ assert!(email.data().search(r"^(Cc|To).*body@destination\.com").unwrap());
+ assert!(!email.data().search(r"^(Cc|To).*unknown@destination\.com").unwrap());
+}
+
+#[test]
+fn invalid_regex() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ assert!(email.body().search(r"^(Cc|To).*(body@destination\.com").is_err());
+}
+
+#[test]
+fn header_search_set() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ let search =
+ email.header().search_set(
+ &[
+ r"^(Cc|To).*someone\.else@destination\.com",
+ r"^(Cc|To).*body@destination\.com",
+ ]
+ ).unwrap();
+
+ let search: Vec<_> = search.into_iter().collect();
+ assert_eq!(search, vec![0]);
+}
+
+#[test]
+fn body_search_set() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ let search =
+ email.body().search_set(
+ &[
+ r"^(Cc|To).*someone\.else@destination\.com",
+ r"^(Cc|To).*body@destination\.com",
+ ]
+ ).unwrap();
+
+ let search: Vec<_> = search.into_iter().collect();
+ assert_eq!(search, vec![1]);
+}
+
+#[test]
+fn data_search_set() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ let search =
+ email.data().search_set(
+ &[
+ r"^(Cc|To).*someone\.else@destination\.com",
+ r"^(Cc|To).*body@destination\.com",
+ ]
+ ).unwrap();
+
+ let search: Vec<_> = search.into_iter().collect();
+ assert_eq!(search, vec![0, 1]);
+}
+
+#[test]
+fn search_set_invalid() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ let search =
+ email.data().search_set(
+ &[
+ r"^((Cc|To).*someone\.else@destination\.com",
+ r"^(Cc|To).*body@destination\.com",
+ ]
+ );
+
+ assert!(search.is_err());
+}
+
+#[test]
+fn unicode_support() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ assert!(email.body().search(r"νἆναι μακρὺς ὁ δρόμος").unwrap());
+ assert!(email.body().search(r"νἆναι μακρὺς ὁ δρόμος").unwrap());
+
+ assert_eq!(
+ email.body().search_set(
+ &[
+ r"Τοὺς Λαιστρυγόνας καὶ τοὺς Κύκλωπας",
+ r"νἆναι μακρὺς ὁ δρόμος",
+ ]
+ ).unwrap().into_iter().collect::<Vec<_>>(),
+ vec![1]
+ );
+
+ assert_eq!(
+ email.data().search_set(
+ &[
+ r"Τοὺς Λαιστρυγόνας καὶ τοὺς Κύκλωπας",
+ r"νἆναι μακρὺς ὁ δρόμος",
+ ]
+ ).unwrap().into_iter().collect::<Vec<_>>(),
+ vec![1]
+ );
+}
+
+#[test]
+fn captures() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ let header = email.header();
+ let captures =
+ header
+ .search_with_captures(r"^X-Test-Field: *(?P<name>\w+)=(?P<value>\w+)")
+ .unwrap()
+ .unwrap();
+
+ assert_eq!(captures.name("name").map(|m| m.as_bytes()), Some("name123".as_bytes()));
+ assert_eq!(captures.name("value").map(|m| m.as_bytes()), Some("value456".as_bytes()));
+}
+
+#[test]
+fn multiline_headers() {
+ let email = Email::from_vec(TEST_EMAIL.to_string().into_bytes()).unwrap();
+
+ let header = email.header();
+ let captures =
+ header
+ .search_with_captures(r"^X-Test-Field: *(?P<name>\w+)=(?P<value>\w+)")
+ .unwrap()
+ .unwrap();
+
+ assert_eq!(captures.name("name").map(|m| m.as_bytes()), Some("name123".as_bytes()));
+ assert_eq!(captures.name("value").map(|m| m.as_bytes()), Some("value456".as_bytes()));
+}