diff options
Diffstat (limited to 'notmuch/tests')
| -rw-r--r-- | notmuch/tests/fixtures.rs | 198 | ||||
| -rw-r--r-- | notmuch/tests/lib.rs | 15 | ||||
| -rw-r--r-- | notmuch/tests/main.rs | 62 | ||||
| -rw-r--r-- | notmuch/tests/test_database.rs | 339 | ||||
| -rw-r--r-- | notmuch/tests/test_message.rs | 291 | ||||
| -rw-r--r-- | notmuch/tests/test_query.rs | 97 | ||||
| -rw-r--r-- | notmuch/tests/test_tags.rs | 142 | ||||
| -rw-r--r-- | notmuch/tests/test_thread.rs | 102 |
8 files changed, 1246 insertions, 0 deletions
diff --git a/notmuch/tests/fixtures.rs b/notmuch/tests/fixtures.rs new file mode 100644 index 0000000..7075905 --- /dev/null +++ b/notmuch/tests/fixtures.rs @@ -0,0 +1,198 @@ +extern crate dirs; +extern crate tempfile; +extern crate notmuch; +extern crate gethostname; +extern crate maildir; +extern crate lettre; +extern crate lettre_email; + +use std::ffi::OsStr; +use std::io::{Result, Write}; +use std::fs::{self, File}; +use std::path::PathBuf; +use tempfile::{tempdir, TempDir}; +use std::process::Command; +use maildir::Maildir; +use lettre_email::{EmailBuilder, Header}; +use lettre::SendableEmail; + + +// A basic test interface to a valid maildir directory. +// +// This creates a valid maildir and provides a simple mechanism to +// deliver test emails to it. It also writes a notmuch-config file +// in the top of the maildir. +pub struct MailBox { + root_dir: TempDir, + maildir: Maildir +} + +impl MailBox { + + // Creates a new maildir fixture. Since this is only used for tests, + // may just panic of something is wrong + pub fn new() -> Self { + + let root_dir = tempdir().unwrap(); + let root_path = root_dir.path().to_path_buf(); + + let tmp_path = root_path.join("tmp"); + fs::create_dir(&tmp_path).unwrap(); + + let cfg_fname = root_path.join("notmuch-config"); + let mut cfg_file = File::create(cfg_fname).unwrap(); + write!(cfg_file, r#" + [database] + path={tmppath} + [user] + name=Some Hacker + primary_email=dst@example.com + [new] + tags=unread;inbox; + ignore= + [search] + exclude_tags=deleted;spam; + [maildir] + synchronize_flags=true + [crypto] + gpg_path=gpg + "#, tmppath=root_path.to_string_lossy()).unwrap(); + + let maildir = Maildir::from(root_path.to_path_buf()); + maildir.create_dirs().unwrap(); + + Self { + root_dir, + maildir + } + } + + /// Return a new unique message ID + // fn next_msgid(&mut self) -> String{ + // let hostname = gethostname::gethostname(); + // let msgid = format!("{}@{}", self.idcount, hostname.to_string_lossy()); + // self.idcount += 1; + // msgid + // } + + pub fn path(&self) -> PathBuf { + self.root_dir.path().into() + } + + /// Deliver a new mail message in the mbox. + /// This does only adds the message to maildir, does not insert it + /// into the notmuch database. + /// returns a tuple of (msgid, pathname). + pub fn deliver(&self, + subject: Option<String>, + body: Option<String>, + to: Option<String>, + from: Option<String>, + headers: Vec<(String, String)>, + is_new: bool, // Move to new dir or cur dir? + _keywords: Option<Vec<String>>, // List of keywords or labels + seen: bool, // Seen flag (cur dir only) + replied: bool, // Replied flag (cur dir only) + flagged: bool) // Flagged flag (cur dir only) + -> Result<(String, PathBuf)> + { + + let mut builder = EmailBuilder::new() + .subject(subject.unwrap_or_else(|| "Test mail".to_string())); + + + if let Some(val) = body { + builder = builder.text(val); + } + + builder = builder.to(to.unwrap_or_else(|| "to@example.com".to_string())) + .from(from.unwrap_or_else(|| "src@example.com".to_string())); + + for h in headers.into_iter(){ + let hdr: Header = h.into(); + builder = builder.header(hdr); + } + + let msg:SendableEmail = builder.build().unwrap().into(); + + // not sure why lettre doesn't add the host suffix itself + let msg_id = msg.message_id().to_string() + ".lettre@localhost"; + let id = if is_new { + self.maildir.store_new(&msg.message_to_string().unwrap().as_bytes()).unwrap() + }else{ + let mut flags = String::from(""); + if flagged { + flags += "F"; + } + if replied { + flags += "R"; + } + if seen { + flags += "S"; + } + println!("flags: {:?}", flags); + let mid = self.maildir.store_cur_with_flags(&msg.message_to_string().unwrap().as_bytes(), flags.as_str()).unwrap(); + + // I have no idea what the reasoning for the :2 here is, but ok. + format!("{}:2,{}", mid, flags) + }; + + + let mut msgpath = self.path(); + msgpath = if is_new { + msgpath.join("new") + } else { + msgpath.join("cur") + }; + + msgpath = msgpath.join(&id); + + Ok((msg_id, msgpath)) + } +} + +impl Drop for MailBox { + fn drop(&mut self) { + } +} + + +#[derive(Clone, Debug)] +pub struct NotmuchCommand { + maildir_path: PathBuf +} + +impl NotmuchCommand { + + /// Return a function which runs notmuch commands on our test maildir. + /// + /// This uses the notmuch-config file created by the ``maildir`` + /// fixture. + pub fn new(maildir_path: &PathBuf) -> Self { + Self { + maildir_path: maildir_path.clone() + } + } + + /// Run a notmuch comand. + /// + /// This function runs with a timeout error as many notmuch + /// commands may block if multiple processes are trying to open + /// the database in write-mode. It is all too easy to + /// accidentally do this in the unittests. + pub fn run<I, S>(&self, args: I) -> Result<()> + where + I: IntoIterator<Item=S>, + S: AsRef<OsStr> + { + let cfg_fname = self.maildir_path.join("notmuch-config"); + + Command::new("notmuch").env("NOTMUCH_CONFIG", &cfg_fname) + .args(args) + .status()?; + Ok(()) + } + +} + + diff --git a/notmuch/tests/lib.rs b/notmuch/tests/lib.rs new file mode 100644 index 0000000..44ec82f --- /dev/null +++ b/notmuch/tests/lib.rs @@ -0,0 +1,15 @@ +extern crate dirs; +extern crate tempfile; +extern crate notmuch; +extern crate gethostname; +extern crate maildir; +extern crate lettre; +extern crate lettre_email; + +mod fixtures; +mod test_database; +mod test_query; +mod test_thread; +mod test_message; +mod test_tags; + diff --git a/notmuch/tests/main.rs b/notmuch/tests/main.rs new file mode 100644 index 0000000..17db2bc --- /dev/null +++ b/notmuch/tests/main.rs @@ -0,0 +1,62 @@ +extern crate dirs; +extern crate tempfile; +extern crate notmuch; +extern crate gethostname; +extern crate maildir; +extern crate lettre; +extern crate lettre_email; + +use std::sync::Arc; + +use notmuch::{Query, QueryExt}; + +mod fixtures; +use fixtures::{MailBox, NotmuchCommand}; + + + + +// fn main() { +// let mut mail_path = dirs::home_dir().unwrap(); +// mail_path.push(".mail"); + +// let md = MailBox::new(); +// let nmcmd = NotMuchCommand::new(md.path()); + +// match notmuch::Database::open( +// &mail_path.to_str().unwrap().to_string(), +// notmuch::DatabaseMode::ReadOnly, +// ) { +// Ok(db) => { +// #[cfg(feature = "v0_21")] +// { +// let rev = db.revision(); +// println!("db revision: {:?}", rev); +// } +// let query = { +// let dbr = Arc::new(db); + +// notmuch::Query::create(dbr.clone(), &"".to_string()).unwrap() +// }; + +// // let mut threads = query.search_threads().unwrap(); + +// // let mut threads = db.create_query(&"".to_string()).unwrap().search_threads().unwrap(); + +// let mut threads = Arc::new(<Query as QueryExt>::search_threads(query).unwrap()); + +// for thread in Arc::get_mut(&mut threads).unwrap() +// { +// println!("thread {:?} {:?}", thread.subject(), thread.authors()); +// } +// } +// Err(err) => { +// println!("Got error while trying to open db: {:?}", err); +// } +// } +// } + + + + + diff --git a/notmuch/tests/test_database.rs b/notmuch/tests/test_database.rs new file mode 100644 index 0000000..098e271 --- /dev/null +++ b/notmuch/tests/test_database.rs @@ -0,0 +1,339 @@ +use fixtures::{NotmuchCommand, MailBox}; + +// #[test] +// // fn test_config_pathname_default(){ + +// // monkeypatch.delenv('NOTMUCH_CONFIG', raising=False) +// // user = pathlib.Path('~/.notmuch-config').expanduser() +// // assert dbmod._config_pathname() == user + +// // } + +mod database { + + use super::*; + + #[test] + fn test_create(){ + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()); + assert!(db.is_ok()); + + assert!(mailbox.path().join(".notmuch/xapian").exists()); + } + + #[test] + fn test_create_already_open(){ + let mailbox = MailBox::new(); + let db1 = notmuch::Database::create(&mailbox.path()); + assert!(db1.is_ok()); + + let db2 = notmuch::Database::create(&mailbox.path()); + assert!(db2.is_err()); + } + + + #[test] + fn test_create_existing(){ + let mailbox = MailBox::new(); + notmuch::Database::create(&mailbox.path()).unwrap(); + + let db2 = notmuch::Database::create(&mailbox.path()); + assert!(db2.is_err()); + } + + + #[test] + fn test_close(){ + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()).unwrap(); + + assert!(db.close().is_ok()); + } + + #[test] + fn test_drop_noclose(){ + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()).unwrap(); + + drop(db); + } + + #[test] + fn test_close_drop(){ + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()).unwrap(); + db.close().unwrap(); + drop(db); + } + + #[test] + fn test_path(){ + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()).unwrap(); + assert_eq!(db.path(), mailbox.path()); + } + + #[test] + fn test_version(){ + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()).unwrap(); + assert!(db.version() > 0); + } + +} + + +mod atomic { + // use super::*; + + // TODO: how do I test this?? + +} + + +#[cfg(feature = "v0_21")] +mod revision { + use super::*; + + #[test] + fn test_single_rev(){ + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()).unwrap(); + + let rev0 = db.revision(); + let rev1 = db.revision(); + + assert!(rev0 == rev1); + assert!(rev0 <= rev1); + assert!(rev0 >= rev1); + + assert!(!(rev0 < rev1)); + assert!(!(rev0 > rev1)); + } + + #[test] + fn test_diff_db(){ + let mailbox0 = MailBox::new(); + let db0 = notmuch::Database::create(&mailbox0.path()).unwrap(); + let rev0 = db0.revision(); + + + let mailbox1 = MailBox::new(); + let db1 = notmuch::Database::create(&mailbox1.path()).unwrap(); + let rev1 = db1.revision(); + + assert_ne!(rev0, rev1); + assert_ne!(rev0.uuid, rev1.uuid); + } + + #[test] + fn test_cmp(){ + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()).unwrap(); + + let rev0 = db.revision(); + + let (_, filename) = mailbox.deliver(None, None, None, None, vec![], true, None, false, false, false).unwrap(); + + db.index_file(&filename, None).unwrap(); + + let rev1 = db.revision(); + + assert!(rev0 < rev1); + assert!(rev0 <= rev1); + assert!(!(rev0 > rev1)); + assert!(!(rev0 >= rev1)); + assert!(!(rev0 == rev1)); + assert!(rev0 != rev1); + + + } + + // TODO: add tests for revisions comparisons + +} + + +mod messages { + use super::*; + + #[test] + fn test_add_message() { + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()).unwrap(); + + let (msgid, filename) = mailbox.deliver(None, None, None, None, vec![], true, None, false, false, false).unwrap(); + let msg = db.index_file(&filename, None).unwrap(); + + assert_eq!(msg.filename(), filename); + assert_eq!(msg.id(), msgid); + + } + + #[test] + fn test_remove_message() { + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()).unwrap(); + + let (msgid, filename) = mailbox.deliver(None, None, None, None, vec![], true, None, false, false, false).unwrap(); + db.index_file(&filename, None).unwrap(); + assert!(db.find_message(&msgid).unwrap().is_some()); + + db.remove_message(&filename).unwrap(); + assert!(db.find_message(&msgid).unwrap().is_none()); + } + + #[test] + fn test_find_message() { + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()).unwrap(); + + let (msgid, filename) = mailbox.deliver(None, None, None, None, vec![], true, None, false, false, false).unwrap(); + let msg0 = db.index_file(&filename, None).unwrap(); + + let msg1 = db.find_message(&msgid).unwrap().unwrap(); + assert_eq!(msg0.id(), msgid); + assert_eq!(msg0.id(), msg1.id()); + + assert_eq!(msg0.filename(), filename); + assert_eq!(msg0.filename(), msg1.filename()); + } + + #[test] + fn test_find_message_notfound() { + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()).unwrap(); + + assert!(db.find_message(&"foo").unwrap().is_none()); + } + +} + +mod tags { + use super::*; + + #[test] + fn test_none() { + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()).unwrap(); + + let tags = db.all_tags().unwrap(); + + assert_eq!(tags.count(), 0); + } + + #[test] + fn test_some() { + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()).unwrap(); + let (_, filename) = mailbox.deliver(None, None, None, None, vec![], true, None, false, false, false).unwrap(); + let msg = db.index_file(&filename, None).unwrap(); + + msg.add_tag(&"hello").unwrap(); + let tags: Vec<String> = db.all_tags().unwrap().collect(); + + assert_eq!(tags.len(), 1); + assert!(tags.iter().any(|x| x == "hello")); + } + + #[test] + fn test_iters() { + let mailbox = MailBox::new(); + let db = notmuch::Database::create(&mailbox.path()).unwrap(); + + let t1: Vec<String> = db.all_tags().unwrap().collect(); + let t2: Vec<String> = db.all_tags().unwrap().collect(); + assert_eq!(t1, t2); + } + +} + +struct DatabaseFixture { + // Return a read-write Database. + // The database will have 3 messages, 2 threads. + + pub mailbox: MailBox, + pub database: notmuch::Database, +} + +impl DatabaseFixture { + pub fn new() -> Self{ + let mailbox = MailBox::new(); + + let (msgid, _) = mailbox.deliver(None, Some("foo".to_string()), None, None, vec![], true, None, false, false, false).unwrap(); + mailbox.deliver(None, Some("bar".to_string()), None, None, vec![], true, None, false, false, false).unwrap(); + mailbox.deliver(None, Some("baz".to_string()), None, None, vec![("In-Reply-To".to_string(), format!("<{}>", msgid))], true, None, false, false, false).unwrap(); + + let cmd = NotmuchCommand::new(&mailbox.path()); + cmd.run(vec!["new"]).unwrap(); + + let database = notmuch::Database::open(&mailbox.path(), notmuch::DatabaseMode::ReadWrite).unwrap(); + + Self { + mailbox, + database + } + } +} + +mod query { + use super::*; + + #[test] + fn test_count_messages() { + let db = DatabaseFixture::new(); + + let query = db.database.create_query("*").unwrap(); + assert_eq!(query.count_messages().unwrap(), 3); + } + + #[test] + fn test_message_no_results() { + let db = DatabaseFixture::new(); + + let query = db.database.create_query("not_a_matching_query").unwrap(); + let mut messages = query.search_messages().unwrap(); + let msg = messages.next(); + assert!(msg.is_none()); + } + + #[test] + fn test_message_match() { + let db = DatabaseFixture::new(); + + let query = db.database.create_query("*").unwrap(); + let mut messages = query.search_messages().unwrap(); + let msg = messages.next(); + assert!(msg.is_some()); + } + + #[test] + fn test_count_threads() { + let db = DatabaseFixture::new(); + + let query = db.database.create_query("*").unwrap(); + assert_eq!(query.count_threads().unwrap(), 2); + } + + #[test] + fn test_threads_no_results() { + let db = DatabaseFixture::new(); + + let query = db.database.create_query("not_a_matching_query").unwrap(); + let mut threads = query.search_threads().unwrap(); + let thrd = threads.next(); + assert!(thrd.is_none()); + } + + #[test] + fn test_threads_match() { + let db = DatabaseFixture::new(); + + let query = db.database.create_query("*").unwrap(); + let mut threads = query.search_threads().unwrap(); + let thrd = threads.next(); + assert!(thrd.is_some()); + } +} + diff --git a/notmuch/tests/test_message.rs b/notmuch/tests/test_message.rs new file mode 100644 index 0000000..7af6f2c --- /dev/null +++ b/notmuch/tests/test_message.rs @@ -0,0 +1,291 @@ +use std::sync::Arc; +use std::path::PathBuf; +use fixtures::MailBox; + +struct MessageFixture { + // Return a single thread with 2 messages + pub mailbox: MailBox, + pub database: Arc<notmuch::Database>, + pub maildir_msg: (String, PathBuf), + pub message: notmuch::Message<'static, notmuch::Database>, +} + +impl MessageFixture { + pub fn new() -> Self{ + let mailbox = MailBox::new(); + + let (msgid, filename) = mailbox.deliver(None, None, None, None, vec![], true, None, false, false, false).unwrap(); + + let database = Arc::new(notmuch::Database::create(&mailbox.path()).unwrap()); + let message = <notmuch::Database as notmuch::DatabaseExt>::index_file(database.clone(), &filename, None).unwrap(); + + Self { + mailbox, + database, + maildir_msg: (msgid, filename), + message + } + } +} + +mod message { + + use super::*; + + #[test] + fn test_messageid() { + let msg = MessageFixture::new(); + let copy = <notmuch::Database as notmuch::DatabaseExt>::find_message_by_filename(msg.database.clone(), &msg.message.filename()).unwrap().unwrap(); + assert_eq!(msg.message.id(), copy.id()) + } + + #[test] + fn test_messageid_find() { + let msg = MessageFixture::new(); + let copy = <notmuch::Database as notmuch::DatabaseExt>::find_message(msg.database.clone(), &msg.message.id()).unwrap().unwrap(); + assert_eq!(msg.message.id(), copy.id()) + } + + #[test] + fn test_path() { + let msg = MessageFixture::new(); + assert_eq!(msg.message.filename(), msg.maildir_msg.1) + } + + + #[test] + fn test_filenames() { + let msg = MessageFixture::new(); + let mut filenames = msg.message.filenames(); + let filename = filenames.next().unwrap(); + + assert_eq!(filename, msg.message.filename()); + + assert!(filenames.next().is_none()); + let names: Vec<PathBuf> = msg.message.filenames().collect(); + + assert_eq!(names, vec![msg.maildir_msg.1]); + } + + #[test] + fn test_header() { + let msg = MessageFixture::new(); + assert_eq!(msg.message.header(&"from").unwrap().unwrap().to_string(), "<src@example.com>"); + } + + #[test] + fn test_header_not_present() { + let msg = MessageFixture::new(); + assert_eq!(msg.message.header(&"foo").unwrap(), None); + } + + #[test] + fn test_freeze() { + let msg = MessageFixture::new(); + + msg.message.freeze().unwrap(); + msg.message.add_tag(&"foo").unwrap(); + msg.message.add_tag(&"bar").unwrap(); + msg.message.remove_tag(&"foo").unwrap(); + msg.message.thaw().unwrap(); + + assert!(msg.message.tags().all(|x| x != "foo")); + assert!(msg.message.tags().any(|x| x == "bar")); + } + + #[test] + fn test_freeze_context() { + let msg = MessageFixture::new(); + + { + let _frozen = notmuch::FrozenMessage::new(&msg.message).unwrap(); + msg.message.add_tag(&"foo").unwrap(); + msg.message.add_tag(&"bar").unwrap(); + msg.message.remove_tag(&"foo").unwrap(); + + } + assert!(msg.message.tags().all(|x| x != "foo")); + assert!(msg.message.tags().any(|x| x == "bar")); + } + + + #[test] + fn test_freeze_err() { + // not sure if this test is ok? + let msg = MessageFixture::new(); + + msg.message.add_tag(&"foo").unwrap(); + + msg.message.freeze().unwrap(); + assert!(msg.message.remove_all_tags().is_ok()); + + let copy = <notmuch::Database as notmuch::DatabaseExt>::find_message(msg.database.clone(), &msg.message.id()).unwrap().unwrap(); + assert!(copy.tags().any(|x| x == "foo")); + + msg.message.thaw().unwrap(); + + assert!(!msg.message.tags().any(|x| x == "foo")); + + let copy2 = <notmuch::Database as notmuch::DatabaseExt>::find_message(msg.database.clone(), &msg.message.id()).unwrap().unwrap(); + assert!(!copy2.tags().any(|x| x == "foo")); + } + + #[test] + fn test_freeze_context_err() { + // not sure if this test is ok? + let msg = MessageFixture::new(); + msg.message.add_tag(&"foo").unwrap(); + + { + let _frozen = notmuch::FrozenMessage::new(&msg.message).unwrap(); + assert!(msg.message.remove_all_tags().is_ok()); + assert!(!msg.message.tags().any(|x| x == "foo")); + + let copy = <notmuch::Database as notmuch::DatabaseExt>::find_message(msg.database.clone(), &msg.message.id()).unwrap().unwrap(); + assert!(copy.tags().any(|x| x == "foo")); + } + + let copy2 = <notmuch::Database as notmuch::DatabaseExt>::find_message(msg.database.clone(), &msg.message.id()).unwrap().unwrap(); + assert!(!copy2.tags().any(|x| x == "foo")); + assert!(!msg.message.tags().any(|x| x == "foo")); + } + + #[test] + fn test_replies() { + let msg = MessageFixture::new(); + assert_eq!(msg.message.replies().count(), 0); + } + +} + + +// def test_date(self, msg): +// # XXX Someone seems to treat things as local time instead of +// # UTC or the other way around. +// now = int(time.time()) +// assert abs(now - msg.date) < 3600*24 + +mod properties { + use super::*; + + #[test] + fn test_add_single() { + let msg = MessageFixture::new(); + msg.message.add_property(&"foo", &"bar").unwrap(); + assert_eq!(msg.message.property(&"foo").unwrap(), "bar"); + + msg.message.add_property(&"bar", &"baz").unwrap(); + assert_eq!(msg.message.property(&"bar").unwrap(), "baz"); + } + + #[test] + fn test_add_dup() { + let msg = MessageFixture::new(); + msg.message.add_property(&"foo", &"bar").unwrap(); + msg.message.add_property(&"foo", &"baz").unwrap(); + + assert_eq!(msg.message.property(&"foo").unwrap(), "bar"); + + let props = msg.message.properties(&"foo", true); + let expect = vec![("foo", "bar"), ("foo", "baz")]; + for (&(ek, ev), (pk, pv)) in expect.iter().zip(props) { + assert_eq!(ek, pk); + assert_eq!(ev, pv); + } + } + + #[test] + fn test_len() { + let msg = MessageFixture::new(); + msg.message.add_property(&"foo", &"a").unwrap(); + msg.message.add_property(&"foo", &"b").unwrap(); + msg.message.add_property(&"bar", &"a").unwrap(); + + let num_props = msg.message.properties(&"", false).count(); + assert_eq!(num_props, 3); + + let mut prop_keys: Vec<String> = msg.message.properties(&"", false).map(|x| x.0).collect(); + prop_keys.sort(); + prop_keys.dedup(); + assert_eq!(prop_keys.len(), 2); + + let mut prop_vals: Vec<String> = msg.message.properties(&"", false).map(|x| x.1).collect(); + prop_vals.sort(); + prop_vals.dedup(); + assert_eq!(prop_vals.len(), 2); + } + + #[test] + fn test_del() { + let msg = MessageFixture::new(); + msg.message.add_property(&"foo", &"a").unwrap(); + msg.message.add_property(&"foo", &"b").unwrap(); + + msg.message.remove_all_properties(Some(&"foo")).unwrap(); + assert!(msg.message.property(&"foo").is_err()); + } + + #[test] + fn test_remove() { + let msg = MessageFixture::new(); + msg.message.add_property(&"foo", &"a").unwrap(); + msg.message.add_property(&"foo", &"b").unwrap(); + + msg.message.remove_property(&"foo", &"a").unwrap(); + assert_eq!(msg.message.property(&"foo").unwrap(), "b"); + } + + #[test] + fn test_clear() { + let msg = MessageFixture::new(); + msg.message.add_property(&"foo", &"a").unwrap(); + + msg.message.remove_all_properties(None).unwrap(); + assert!(msg.message.property(&"foo").is_err()); + } + + #[test] + fn test_getall() { + let msg = MessageFixture::new(); + msg.message.add_property(&"foo", &"a").unwrap(); + + let prop_keys: Vec<String> = msg.message.properties(&"foo", false).map(|x| x.0).collect(); + assert_eq!(prop_keys.len(), 1); + assert_eq!(prop_keys, vec!["foo"]); + + let prop_vals: Vec<String> = msg.message.properties(&"foo", false).map(|x| x.1).collect(); + assert_eq!(prop_vals.len(), 1); + assert_eq!(prop_vals, vec!["a"]); + } + + #[test] + fn test_getall_prefix() { + let msg = MessageFixture::new(); + msg.message.add_property(&"foo", &"a").unwrap(); + msg.message.add_property(&"foobar", &"b").unwrap(); + + let prop_keys: Vec<String> = msg.message.properties(&"foo", false).map(|x| x.0).collect(); + assert_eq!(prop_keys.len(), 2); + assert_eq!(prop_keys, vec!["foo", "foobar"]); + + let prop_vals: Vec<String> = msg.message.properties(&"foo", false).map(|x| x.1).collect(); + assert_eq!(prop_vals.len(), 2); + assert_eq!(prop_vals, vec!["a", "b"]); + } + + #[test] + fn test_getall_exact() { + let msg = MessageFixture::new(); + msg.message.add_property(&"foo", &"a").unwrap(); + msg.message.add_property(&"foobar", &"b").unwrap(); + + let prop_keys: Vec<String> = msg.message.properties(&"foo", true).map(|x| x.0).collect(); + assert_eq!(prop_keys.len(), 1); + assert_eq!(prop_keys, vec!["foo"]); + + let prop_vals: Vec<String> = msg.message.properties(&"foo", true).map(|x| x.1).collect(); + assert_eq!(prop_vals.len(), 1); + assert_eq!(prop_vals, vec!["a"]); + } +} + diff --git a/notmuch/tests/test_query.rs b/notmuch/tests/test_query.rs new file mode 100644 index 0000000..ad8f299 --- /dev/null +++ b/notmuch/tests/test_query.rs @@ -0,0 +1,97 @@ +use std::sync::Arc; +use fixtures::{NotmuchCommand, MailBox}; + + +struct QueryFixture { + // Return a single thread with 2 messages + pub mailbox: MailBox, + pub query: notmuch::Query<'static>, +} + +impl QueryFixture { + pub fn new() -> Self{ + let mailbox = MailBox::new(); + + let (msgid, _) = mailbox.deliver(None, Some("foo".to_string()), None, None, vec![], true, None, false, false, false).unwrap(); + mailbox.deliver(None, Some("bar".to_string()), None, None, vec![], true, None, false, false, false).unwrap(); + mailbox.deliver(None, Some("baz".to_string()), None, None, vec![("In-Reply-To".to_string(), format!("<{}>", msgid))], true, None, false, false, false).unwrap(); + mailbox.deliver(None, Some("foo qux".to_string()), None, None, vec![], true, None, false, false, false).unwrap(); + mailbox.deliver(None, Some("foo quux".to_string()), None, None, vec![], true, None, false, false, false).unwrap(); + + let cmd = NotmuchCommand::new(&mailbox.path()); + cmd.run(vec!["new"]).unwrap(); + + let query = { + let database = Arc::new(notmuch::Database::open(&mailbox.path(), notmuch::DatabaseMode::ReadWrite).unwrap()); + + notmuch::Query::create(database, &"foo".to_string()).unwrap() + }; + + Self { + mailbox, + query + } + } +} + +#[test] +fn test_iter_threads() { + let q = QueryFixture::new(); + + let threads = q.query.search_threads().unwrap(); + + let mut num = 0; + for _thread in threads { + num += 1; + } + + assert_eq!(num, 3); + +} + +#[test] +fn test_iter_threads_ext() { + let q = QueryFixture::new(); + + let threads = <notmuch::Query as notmuch::QueryExt>::search_threads(q.query).unwrap(); + + let mut num = 0; + for _thread in threads { + num += 1; + } + + assert_eq!(num, 3); + +} + + +#[test] +fn test_iter_messages() { + let q = QueryFixture::new(); + + let messages = q.query.search_messages().unwrap(); + + let mut num = 0; + for _message in messages { + num += 1; + } + + assert_eq!(num, 3); + +} + +#[test] +fn test_iter_messages_ext() { + let q = QueryFixture::new(); + + let messages = <notmuch::Query as notmuch::QueryExt>::search_messages(q.query).unwrap(); + + let mut num = 0; + for _message in messages { + num += 1; + } + + assert_eq!(num, 3); + +} + diff --git a/notmuch/tests/test_tags.rs b/notmuch/tests/test_tags.rs new file mode 100644 index 0000000..3d39486 --- /dev/null +++ b/notmuch/tests/test_tags.rs @@ -0,0 +1,142 @@ +use std::sync::Arc; +use fixtures::{MailBox, NotmuchCommand}; + +struct TagSetFixture { + // An non-empty immutable tagset. + // This will have the default new mail tags: inbox, unread. + pub mailbox: MailBox, + pub cmd: NotmuchCommand, + pub database: Arc<notmuch::Database>, + pub message: notmuch::Message<'static, notmuch::Database> +} + +impl TagSetFixture { + pub fn new(mutable: bool, flagged: bool) -> Self{ + let mailbox = MailBox::new(); + let (_msg, filename) = mailbox.deliver(None, None, None, None, vec![], !flagged, None, false, false, flagged).unwrap(); + + let cmd = NotmuchCommand::new(&mailbox.path()); + cmd.run(vec!["new"]).unwrap(); + + let database = Arc::new(notmuch::Database::open(&mailbox.path(), if !mutable {notmuch::DatabaseMode::ReadOnly} else { notmuch::DatabaseMode::ReadWrite }).unwrap()); + let message = <notmuch::Database as notmuch::DatabaseExt>::find_message_by_filename(database.clone(), &filename).unwrap().unwrap(); + + Self { + mailbox, + database, + cmd, + message + } + } +} + +mod immutable { + + use super::*; + + #[test] + fn test_neg(){ + let tagset = TagSetFixture::new(false, false); + + let tags: Vec<String> = tagset.database.all_tags().unwrap().collect(); + tagset.cmd.run(vec!["tag", "+foo", "*"]).unwrap(); + + let database = notmuch::Database::open(&tagset.mailbox.path(), notmuch::DatabaseMode::ReadOnly).unwrap(); + let ntags: Vec<String> = database.all_tags().unwrap().collect(); + + assert_ne!(tags, ntags); + } + + #[test] + fn test_contains(){ + let tagset = TagSetFixture::new(false, false); + let tags: Vec<String> = tagset.database.all_tags().unwrap().collect(); + + assert!(tags.iter().any(|x| x == "unread")); + assert!(!tags.iter().any(|x| x == "foo")); + } + + + #[test] + fn test_len(){ + let tagset = TagSetFixture::new(false, false); + assert_eq!(tagset.database.all_tags().unwrap().count(), 2); + } + +} + +mod mutable { + + use super::*; + + #[test] + fn test_add(){ + let tagset = TagSetFixture::new(true, false); + assert!(!tagset.message.tags().any(|x| x == "foo")); + + tagset.message.add_tag("foo").unwrap(); + assert!(tagset.message.tags().any(|x| x == "foo")); + } + + #[test] + fn test_discard(){ + let tagset = TagSetFixture::new(true, false); + assert!(tagset.message.tags().any(|x| x == "inbox")); + + tagset.message.remove_tag("inbox").unwrap(); + assert!(!tagset.message.tags().any(|x| x == "unbox")); + } + + #[test] + fn test_discard_not_present(){ + let tagset = TagSetFixture::new(true, false); + assert!(!tagset.message.tags().any(|x| x == "foo")); + + tagset.message.remove_tag("foo").unwrap(); + } + + #[test] + fn test_clear(){ + let tagset = TagSetFixture::new(true, false); + assert!(tagset.message.tags().count() > 0); + tagset.message.remove_all_tags().unwrap(); + + assert!(tagset.message.tags().count() == 0); + } + + #[test] + fn test_from_maildir_flags(){ + let tagset = TagSetFixture::new(true, true); + + tagset.message.remove_tag(&"flagged").unwrap(); + tagset.message.maildir_flags_to_tags().unwrap(); + + assert!(tagset.message.tags().any(|x| x == "flagged")); + } + + + #[test] + fn test_to_maildir_flags(){ + + let tagset = TagSetFixture::new(true, true); + + let filename = tagset.message.filename(); + let filestr = filename.to_string_lossy(); + + let file_parts: Vec<&str> = filestr.split(',').collect(); + let flags = file_parts.last().unwrap(); + println!("Flags {:?}", flags); + + assert!(flags.contains('F')); + tagset.message.remove_tag(&"flagged").unwrap(); + tagset.message.tags_to_maildir_flags().unwrap(); + + let filename = tagset.message.filename(); + let filestr = filename.to_string_lossy(); + + let file_parts: Vec<&str> = filestr.split(',').collect(); + let flags = file_parts.last().unwrap(); + assert!(!flags.contains('F')); + } + +}
\ No newline at end of file diff --git a/notmuch/tests/test_thread.rs b/notmuch/tests/test_thread.rs new file mode 100644 index 0000000..89f1ea0 --- /dev/null +++ b/notmuch/tests/test_thread.rs @@ -0,0 +1,102 @@ +use std::sync::Arc; +use fixtures::{NotmuchCommand, MailBox}; + + +struct ThreadFixture { + // Return a single thread with 2 messages + pub mailbox: MailBox, + pub thread: notmuch::Thread<'static, 'static>, +} + +impl ThreadFixture { + pub fn new() -> Self{ + let mailbox = MailBox::new(); + + let (msgid, _) = mailbox.deliver(None, Some("foo".to_string()), None, None, vec![], true, None, false, false, false).unwrap(); + mailbox.deliver(None, Some("bar".to_string()), None, None, vec![("In-Reply-To".to_string(), format!("<{}>", msgid))], true, None, false, false, false).unwrap(); + + let cmd = NotmuchCommand::new(&mailbox.path()); + cmd.run(vec!["new"]).unwrap(); + + let mut threads = { + let database = Arc::new(notmuch::Database::open(&mailbox.path(), notmuch::DatabaseMode::ReadWrite).unwrap()); + + let query = notmuch::Query::create(database.clone(), &"foo".to_string()).unwrap(); + + <notmuch::Query as notmuch::QueryExt>::search_threads(query).unwrap() + }; + let thread = threads.next().unwrap(); + + Self { + mailbox, + thread + } + } +} + +#[test] +fn test_threadid() { + let thread = ThreadFixture::new(); + assert!(!thread.thread.id().is_empty()); +} + + +#[test] +fn test_toplevel() { + let thread = ThreadFixture::new(); + let msgs = thread.thread.toplevel_messages(); + + assert_eq!(msgs.count(), 1); +} + + +#[test] +fn test_toplevel_reply() { + let thread = ThreadFixture::new(); + let msg = thread.thread.toplevel_messages().next().unwrap(); + + assert_eq!(msg.replies().count(), 1); +} + +#[test] +fn test_iter() { + let thread = ThreadFixture::new(); + let msg_count0 = thread.thread.messages().count() as i32; + let msg_count1 = thread.thread.total_messages(); + + assert_eq!(msg_count0, msg_count1); +} + +#[test] +fn test_matched() { + let thread = ThreadFixture::new(); + assert_eq!(thread.thread.matched_messages(), 1); +} + + +#[test] +fn test_authors() { + let thread = ThreadFixture::new(); + + assert_eq!(thread.thread.authors(), vec!["src@example.com".to_string()]); +} + + +#[test] +fn test_subject() { + let thread = ThreadFixture::new(); + + println!("{:?}", thread.thread.subject()); + assert_eq!(thread.thread.subject(), "Test mail"); +} + + + +#[test] +fn test_tags() { + let thread = ThreadFixture::new(); + + let tags: Vec<String> = thread.thread.tags().collect(); + assert!(tags.iter().any(|x| x == "inbox")); +} +
\ No newline at end of file |
