diff options
author | Alexandros Frantzis <alf82@freemail.gr> | 2019-10-01 23:06:38 +0300 |
---|---|---|
committer | Alexandros Frantzis <alf82@freemail.gr> | 2019-10-01 23:36:26 +0300 |
commit | a054789ddb60ed1fab26e6d4e6bd36ed926273f1 (patch) | |
tree | bd3caad78a0e377816b78276889301d9276344b9 /tests |
Initial public release
Diffstat (limited to 'tests')
-rw-r--r-- | tests/test_charset.rs | 104 | ||||
-rw-r--r-- | tests/test_deliver.rs | 86 | ||||
-rw-r--r-- | tests/test_encoded_words.rs | 78 | ||||
-rw-r--r-- | tests/test_encoding.rs | 136 | ||||
-rw-r--r-- | tests/test_fields.rs | 135 | ||||
-rw-r--r-- | tests/test_processing.rs | 39 | ||||
-rw-r--r-- | tests/test_regex.rs | 185 |
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())); +} |