mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-05 09:05:13 +00:00
init notmuch backend e2e tests
This commit is contained in:
parent
e544536e01
commit
886b66a017
|
@ -9,7 +9,7 @@ use std::{convert::TryInto, fs, path::PathBuf};
|
|||
|
||||
use crate::{
|
||||
backends::{Backend, IdMapper, MaildirEnvelopes, MaildirFlags, MaildirMboxes},
|
||||
config::{AccountConfig, MaildirBackendConfig, DEFAULT_INBOX_FOLDER},
|
||||
config::{AccountConfig, MaildirBackendConfig},
|
||||
mbox::Mboxes,
|
||||
msg::{Envelopes, Msg},
|
||||
};
|
||||
|
@ -40,17 +40,10 @@ impl<'a> MaildirBackend<'a> {
|
|||
}
|
||||
|
||||
/// Creates a maildir instance from a string slice.
|
||||
fn get_mdir_from_dir(&self, dir: &str) -> Result<maildir::Maildir> {
|
||||
let inbox_folder = self
|
||||
.account_config
|
||||
.mailboxes
|
||||
.get("inbox")
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(DEFAULT_INBOX_FOLDER);
|
||||
|
||||
pub fn get_mdir_from_dir(&self, dir: &str) -> Result<maildir::Maildir> {
|
||||
// If the dir points to the inbox folder, creates a maildir
|
||||
// instance from the root folder.
|
||||
if dir == inbox_folder {
|
||||
if dir == "inbox" {
|
||||
self.validate_mdir_path(self.mdir.path().to_owned())
|
||||
.map(maildir::Maildir::from)
|
||||
} else {
|
||||
|
|
|
@ -4,17 +4,17 @@ use anyhow::{anyhow, Context, Result};
|
|||
use log::{debug, info, trace};
|
||||
|
||||
use crate::{
|
||||
backends::{Backend, IdMapper, NotmuchEnvelopes, NotmuchMbox, NotmuchMboxes},
|
||||
backends::{Backend, IdMapper, MaildirBackend, NotmuchEnvelopes, NotmuchMbox, NotmuchMboxes},
|
||||
config::{AccountConfig, NotmuchBackendConfig},
|
||||
mbox::Mboxes,
|
||||
msg::{Envelopes, Msg},
|
||||
};
|
||||
|
||||
/// Represents the Notmuch backend.
|
||||
#[derive(Debug)]
|
||||
pub struct NotmuchBackend<'a> {
|
||||
account_config: &'a AccountConfig,
|
||||
notmuch_config: &'a NotmuchBackendConfig,
|
||||
pub mdir: &'a mut MaildirBackend<'a>,
|
||||
db: notmuch::Database,
|
||||
}
|
||||
|
||||
|
@ -22,12 +22,14 @@ impl<'a> NotmuchBackend<'a> {
|
|||
pub fn new(
|
||||
account_config: &'a AccountConfig,
|
||||
notmuch_config: &'a NotmuchBackendConfig,
|
||||
) -> Result<Self> {
|
||||
mdir: &'a mut MaildirBackend<'a>,
|
||||
) -> Result<NotmuchBackend<'a>> {
|
||||
info!(">> create new notmuch backend");
|
||||
|
||||
let backend = Self {
|
||||
account_config,
|
||||
notmuch_config,
|
||||
mdir,
|
||||
db: notmuch::Database::open(
|
||||
notmuch_config.notmuch_database_dir.clone(),
|
||||
notmuch::DatabaseMode::ReadWrite,
|
||||
|
@ -39,7 +41,6 @@ impl<'a> NotmuchBackend<'a> {
|
|||
)
|
||||
})?,
|
||||
};
|
||||
trace!("backend: {:?}", backend);
|
||||
|
||||
info!("<< create new notmuch backend");
|
||||
Ok(backend)
|
||||
|
@ -68,10 +69,10 @@ impl<'a> NotmuchBackend<'a> {
|
|||
let page_begin = page * page_size;
|
||||
debug!("page begin: {:?}", page_begin);
|
||||
if page_begin > envelopes.len() {
|
||||
return Err(anyhow!(format!(
|
||||
return Err(anyhow!(
|
||||
"cannot get notmuch envelopes at page {:?} (out of bounds)",
|
||||
page_begin + 1,
|
||||
)));
|
||||
));
|
||||
}
|
||||
let page_end = envelopes.len().min(page_begin + page_size);
|
||||
debug!("page end: {:?}", page_end);
|
||||
|
@ -192,17 +193,75 @@ impl<'a> Backend<'a> for NotmuchBackend<'a> {
|
|||
Ok(envelopes)
|
||||
}
|
||||
|
||||
fn add_msg(
|
||||
&mut self,
|
||||
_virt_mbox: &str,
|
||||
_msg: &[u8],
|
||||
_flags: &str,
|
||||
) -> Result<Box<dyn ToString>> {
|
||||
fn add_msg(&mut self, dir: &str, msg: &[u8], tags: &str) -> Result<Box<dyn ToString>> {
|
||||
info!(">> add notmuch envelopes");
|
||||
debug!("dir: {:?}", dir);
|
||||
debug!("tags: {:?}", tags);
|
||||
|
||||
let mdir = self
|
||||
.mdir
|
||||
.get_mdir_from_dir(dir)
|
||||
.with_context(|| format!("cannot get maildir instance from {:?}", dir))?;
|
||||
let mdir_path_str = mdir
|
||||
.path()
|
||||
.to_str()
|
||||
.ok_or_else(|| anyhow!("cannot parse maildir path to string"))?;
|
||||
|
||||
// Adds the message to the maildir folder and gets its hash.
|
||||
let hash = self
|
||||
.mdir
|
||||
.add_msg(mdir_path_str, msg, "seen")
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"cannot add notmuch message to maildir {:?}",
|
||||
self.notmuch_config.notmuch_database_dir
|
||||
)
|
||||
})?
|
||||
.to_string();
|
||||
debug!("hash: {:?}", hash);
|
||||
|
||||
// Retrieves the file path of the added message by its maildir
|
||||
// identifier.
|
||||
let id = IdMapper::new(mdir.path())
|
||||
.with_context(|| format!("cannot create id mapper instance for {:?}", dir))?
|
||||
.find(&hash)
|
||||
.with_context(|| format!("cannot find notmuch message from short hash {:?}", hash))?;
|
||||
debug!("id: {:?}", id);
|
||||
let file_path = mdir.path().join("cur").join(format!("{}:2,S", id));
|
||||
debug!("file path: {:?}", file_path);
|
||||
|
||||
// Adds the message to the notmuch database by indexing it.
|
||||
let id = self
|
||||
.db
|
||||
.index_file(&file_path, None)
|
||||
.with_context(|| format!("cannot index notmuch message from file {:?}", file_path))?
|
||||
.id()
|
||||
.to_string();
|
||||
let hash = format!("{:x}", md5::compute(&id));
|
||||
|
||||
// Appends hash entry to the id mapper cache file.
|
||||
let mut mapper =
|
||||
IdMapper::new(&self.notmuch_config.notmuch_database_dir).with_context(|| {
|
||||
format!(
|
||||
"cannot create id mapper instance for {:?}",
|
||||
self.notmuch_config.notmuch_database_dir
|
||||
)
|
||||
})?;
|
||||
mapper
|
||||
.append(vec![(hash.clone(), id.clone())])
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"cannot append hash {:?} with id {:?} to id mapper",
|
||||
hash, id
|
||||
)
|
||||
})?;
|
||||
|
||||
// Attaches tags to the notmuch message.
|
||||
self.add_flags("", &hash, tags)
|
||||
.with_context(|| format!("cannot add flags to notmuch message {:?}", id))?;
|
||||
|
||||
info!("<< add notmuch envelopes");
|
||||
Err(anyhow!(
|
||||
"cannot add notmuch envelopes: feature not implemented"
|
||||
))
|
||||
Ok(Box::new(hash))
|
||||
}
|
||||
|
||||
fn get_msg(&mut self, _virt_mbox: &str, short_hash: &str) -> Result<Msg> {
|
||||
|
@ -288,34 +347,35 @@ impl<'a> Backend<'a> for NotmuchBackend<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn add_flags(&mut self, virt_mbox: &str, query: &str, tags: &str) -> Result<()> {
|
||||
fn add_flags(&mut self, _virt_mbox: &str, short_hash: &str, tags: &str) -> Result<()> {
|
||||
info!(">> add notmuch message flags");
|
||||
debug!("tags: {:?}", tags);
|
||||
debug!("query: {:?}", query);
|
||||
|
||||
let query = self
|
||||
.account_config
|
||||
.mailboxes
|
||||
.get(virt_mbox)
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(query);
|
||||
debug!("final query: {:?}", query);
|
||||
let dir = &self.notmuch_config.notmuch_database_dir;
|
||||
let id = IdMapper::new(dir)
|
||||
.with_context(|| format!("cannot create id mapper instance for {:?}", dir))?
|
||||
.find(short_hash)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"cannot find notmuch message from short hash {:?}",
|
||||
short_hash
|
||||
)
|
||||
})?;
|
||||
debug!("id: {:?}", id);
|
||||
let query = format!("id:{}", id);
|
||||
debug!("query: {:?}", query);
|
||||
let tags: Vec<_> = tags.split_whitespace().collect();
|
||||
let query_builder = self
|
||||
.db
|
||||
.create_query(query)
|
||||
.create_query(&query)
|
||||
.with_context(|| format!("cannot create notmuch query from {:?}", query))?;
|
||||
let envelopes = query_builder
|
||||
let msgs = query_builder
|
||||
.search_messages()
|
||||
.with_context(|| format!("cannot find notmuch envelopes from query {:?}", query))?;
|
||||
for envelope in envelopes {
|
||||
for msg in msgs {
|
||||
for tag in tags.iter() {
|
||||
envelope.add_tag(*tag).with_context(|| {
|
||||
format!(
|
||||
"cannot add tag {:?} to notmuch message {:?}",
|
||||
tag,
|
||||
envelope.id()
|
||||
)
|
||||
msg.add_tag(*tag).with_context(|| {
|
||||
format!("cannot add tag {:?} to notmuch message {:?}", tag, msg.id())
|
||||
})?
|
||||
}
|
||||
}
|
||||
|
@ -324,40 +384,38 @@ impl<'a> Backend<'a> for NotmuchBackend<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn set_flags(&mut self, virt_mbox: &str, query: &str, tags: &str) -> Result<()> {
|
||||
fn set_flags(&mut self, _virt_mbox: &str, short_hash: &str, tags: &str) -> Result<()> {
|
||||
info!(">> set notmuch message flags");
|
||||
debug!("tags: {:?}", tags);
|
||||
debug!("query: {:?}", query);
|
||||
|
||||
let query = self
|
||||
.account_config
|
||||
.mailboxes
|
||||
.get(virt_mbox)
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(query);
|
||||
debug!("final query: {:?}", query);
|
||||
let dir = &self.notmuch_config.notmuch_database_dir;
|
||||
let id = IdMapper::new(dir)
|
||||
.with_context(|| format!("cannot create id mapper instance for {:?}", dir))?
|
||||
.find(short_hash)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"cannot find notmuch message from short hash {:?}",
|
||||
short_hash
|
||||
)
|
||||
})?;
|
||||
debug!("id: {:?}", id);
|
||||
let query = format!("id:{}", id);
|
||||
debug!("query: {:?}", query);
|
||||
let tags: Vec<_> = tags.split_whitespace().collect();
|
||||
let query_builder = self
|
||||
.db
|
||||
.create_query(query)
|
||||
.create_query(&query)
|
||||
.with_context(|| format!("cannot create notmuch query from {:?}", query))?;
|
||||
let envelopes = query_builder
|
||||
let msgs = query_builder
|
||||
.search_messages()
|
||||
.with_context(|| format!("cannot find notmuch envelopes from query {:?}", query))?;
|
||||
for envelope in envelopes {
|
||||
envelope.remove_all_tags().with_context(|| {
|
||||
format!(
|
||||
"cannot remove all tags from notmuch message {:?}",
|
||||
envelope.id()
|
||||
)
|
||||
for msg in msgs {
|
||||
msg.remove_all_tags().with_context(|| {
|
||||
format!("cannot remove all tags from notmuch message {:?}", msg.id())
|
||||
})?;
|
||||
for tag in tags.iter() {
|
||||
envelope.add_tag(*tag).with_context(|| {
|
||||
format!(
|
||||
"cannot add tag {:?} to notmuch message {:?}",
|
||||
tag,
|
||||
envelope.id()
|
||||
)
|
||||
msg.add_tag(*tag).with_context(|| {
|
||||
format!("cannot add tag {:?} to notmuch message {:?}", tag, msg.id())
|
||||
})?
|
||||
}
|
||||
}
|
||||
|
@ -366,33 +424,38 @@ impl<'a> Backend<'a> for NotmuchBackend<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn del_flags(&mut self, virt_mbox: &str, query: &str, tags: &str) -> Result<()> {
|
||||
fn del_flags(&mut self, _virt_mbox: &str, short_hash: &str, tags: &str) -> Result<()> {
|
||||
info!(">> delete notmuch message flags");
|
||||
debug!("tags: {:?}", tags);
|
||||
debug!("query: {:?}", query);
|
||||
|
||||
let query = self
|
||||
.account_config
|
||||
.mailboxes
|
||||
.get(virt_mbox)
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(query);
|
||||
debug!("final query: {:?}", query);
|
||||
let dir = &self.notmuch_config.notmuch_database_dir;
|
||||
let id = IdMapper::new(dir)
|
||||
.with_context(|| format!("cannot create id mapper instance for {:?}", dir))?
|
||||
.find(short_hash)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"cannot find notmuch message from short hash {:?}",
|
||||
short_hash
|
||||
)
|
||||
})?;
|
||||
debug!("id: {:?}", id);
|
||||
let query = format!("id:{}", id);
|
||||
debug!("query: {:?}", query);
|
||||
let tags: Vec<_> = tags.split_whitespace().collect();
|
||||
let query_builder = self
|
||||
.db
|
||||
.create_query(query)
|
||||
.create_query(&query)
|
||||
.with_context(|| format!("cannot create notmuch query from {:?}", query))?;
|
||||
let envelopes = query_builder
|
||||
let msgs = query_builder
|
||||
.search_messages()
|
||||
.with_context(|| format!("cannot find notmuch envelopes from query {:?}", query))?;
|
||||
for envelope in envelopes {
|
||||
for msg in msgs {
|
||||
for tag in tags.iter() {
|
||||
envelope.remove_tag(*tag).with_context(|| {
|
||||
msg.remove_tag(*tag).with_context(|| {
|
||||
format!(
|
||||
"cannot delete tag {:?} from notmuch message {:?}",
|
||||
tag,
|
||||
envelope.id()
|
||||
msg.id()
|
||||
)
|
||||
})?
|
||||
}
|
||||
|
|
16
src/main.rs
16
src/main.rs
|
@ -7,7 +7,7 @@ use himalaya::{
|
|||
compl::{compl_arg, compl_handler},
|
||||
config::{
|
||||
account_args, config_args, AccountConfig, BackendConfig, DeserializedConfig,
|
||||
DEFAULT_INBOX_FOLDER,
|
||||
MaildirBackendConfig, DEFAULT_INBOX_FOLDER,
|
||||
},
|
||||
mbox::{mbox_arg, mbox_handler},
|
||||
msg::{flag_arg, flag_handler, msg_arg, msg_handler, tpl_arg, tpl_handler},
|
||||
|
@ -51,6 +51,7 @@ fn main() -> Result<()> {
|
|||
|
||||
let mut imap;
|
||||
let mut maildir;
|
||||
let maildir_config;
|
||||
#[cfg(feature = "notmuch")]
|
||||
let mut notmuch;
|
||||
let backend: Box<&mut dyn Backend> = match backend_config {
|
||||
|
@ -64,7 +65,11 @@ fn main() -> Result<()> {
|
|||
}
|
||||
#[cfg(feature = "notmuch")]
|
||||
BackendConfig::Notmuch(ref notmuch_config) => {
|
||||
notmuch = NotmuchBackend::new(&account_config, notmuch_config)?;
|
||||
maildir_config = MaildirBackendConfig {
|
||||
maildir_dir: notmuch_config.notmuch_database_dir.clone(),
|
||||
};
|
||||
maildir = MaildirBackend::new(&account_config, &maildir_config);
|
||||
notmuch = NotmuchBackend::new(&account_config, notmuch_config, &mut maildir)?;
|
||||
Box::new(&mut notmuch)
|
||||
}
|
||||
};
|
||||
|
@ -95,6 +100,7 @@ fn main() -> Result<()> {
|
|||
let mut printer = StdoutPrinter::try_from(m.value_of("output"))?;
|
||||
let mut imap;
|
||||
let mut maildir;
|
||||
let maildir_config;
|
||||
#[cfg(feature = "notmuch")]
|
||||
let mut notmuch;
|
||||
let backend: Box<&mut dyn Backend> = match backend_config {
|
||||
|
@ -108,7 +114,11 @@ fn main() -> Result<()> {
|
|||
}
|
||||
#[cfg(feature = "notmuch")]
|
||||
BackendConfig::Notmuch(ref notmuch_config) => {
|
||||
notmuch = NotmuchBackend::new(&account_config, notmuch_config)?;
|
||||
maildir_config = MaildirBackendConfig {
|
||||
maildir_dir: notmuch_config.notmuch_database_dir.clone(),
|
||||
};
|
||||
maildir = MaildirBackend::new(&account_config, &maildir_config);
|
||||
notmuch = NotmuchBackend::new(&account_config, notmuch_config, &mut maildir)?;
|
||||
Box::new(&mut notmuch)
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,5 +2,6 @@ From: alice@localhost
|
|||
To: patrick@localhost
|
||||
Subject: Plain message
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Date: Tue, 1 Mar 2022 12:00:00 +0000
|
||||
|
||||
Ceci est un message.
|
81
tests/test_notmuch_backend.rs
Normal file
81
tests/test_notmuch_backend.rs
Normal file
|
@ -0,0 +1,81 @@
|
|||
use std::{collections::HashMap, env, fs, iter::FromIterator};
|
||||
|
||||
use himalaya::{
|
||||
backends::{Backend, MaildirBackend, NotmuchBackend, NotmuchEnvelopes},
|
||||
config::{AccountConfig, MaildirBackendConfig, NotmuchBackendConfig},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_notmuch_backend() {
|
||||
// set up maildir folders and notmuch database
|
||||
let mdir: maildir::Maildir = env::temp_dir().join("himalaya-test-notmuch").into();
|
||||
if let Err(_) = fs::remove_dir_all(mdir.path()) {}
|
||||
mdir.create_dirs().unwrap();
|
||||
notmuch::Database::create(mdir.path()).unwrap();
|
||||
|
||||
// configure accounts
|
||||
let account_config = AccountConfig {
|
||||
mailboxes: HashMap::from_iter([("inbox".into(), "*".into())]),
|
||||
..AccountConfig::default()
|
||||
};
|
||||
let mdir_config = MaildirBackendConfig {
|
||||
maildir_dir: mdir.path().to_owned(),
|
||||
};
|
||||
let notmuch_config = NotmuchBackendConfig {
|
||||
notmuch_database_dir: mdir.path().to_owned(),
|
||||
};
|
||||
let mut mdir = MaildirBackend::new(&account_config, &mdir_config);
|
||||
let mut notmuch = NotmuchBackend::new(&account_config, ¬much_config, &mut mdir).unwrap();
|
||||
|
||||
// check that a message can be added
|
||||
let msg = include_bytes!("./emails/alice-to-patrick.eml");
|
||||
let hash = notmuch.add_msg("", msg, "inbox seen").unwrap().to_string();
|
||||
|
||||
// check that the added message exists
|
||||
let msg = notmuch.get_msg("", &hash).unwrap();
|
||||
assert_eq!("alice@localhost", msg.from.clone().unwrap().to_string());
|
||||
assert_eq!("patrick@localhost", msg.to.clone().unwrap().to_string());
|
||||
assert_eq!("Ceci est un message.", msg.fold_text_plain_parts());
|
||||
|
||||
// check that the envelope of the added message exists
|
||||
let envelopes = notmuch.get_envelopes("inbox", 10, 0).unwrap();
|
||||
let envelopes: &NotmuchEnvelopes = envelopes.as_any().downcast_ref().unwrap();
|
||||
let envelope = envelopes.first().unwrap();
|
||||
assert_eq!(1, envelopes.len());
|
||||
assert_eq!("alice@localhost", envelope.sender);
|
||||
assert_eq!("Plain message", envelope.subject);
|
||||
|
||||
// check that a flag can be added to the message
|
||||
notmuch
|
||||
.add_flags("", &envelope.hash, "flagged passed")
|
||||
.unwrap();
|
||||
let envelopes = notmuch.get_envelopes("inbox", 1, 0).unwrap();
|
||||
let envelopes: &NotmuchEnvelopes = envelopes.as_any().downcast_ref().unwrap();
|
||||
let envelope = envelopes.first().unwrap();
|
||||
assert!(envelope.flags.contains(&"inbox".into()));
|
||||
assert!(envelope.flags.contains(&"seen".into()));
|
||||
assert!(envelope.flags.contains(&"flagged".into()));
|
||||
assert!(envelope.flags.contains(&"passed".into()));
|
||||
|
||||
// check that the message flags can be changed
|
||||
notmuch
|
||||
.set_flags("", &envelope.hash, "inbox passed")
|
||||
.unwrap();
|
||||
let envelopes = notmuch.get_envelopes("inbox", 1, 0).unwrap();
|
||||
let envelopes: &NotmuchEnvelopes = envelopes.as_any().downcast_ref().unwrap();
|
||||
let envelope = envelopes.first().unwrap();
|
||||
assert!(envelope.flags.contains(&"inbox".into()));
|
||||
assert!(!envelope.flags.contains(&"seen".into()));
|
||||
assert!(!envelope.flags.contains(&"flagged".into()));
|
||||
assert!(envelope.flags.contains(&"passed".into()));
|
||||
|
||||
// check that a flag can be removed from the message
|
||||
notmuch.del_flags("", &envelope.hash, "passed").unwrap();
|
||||
let envelopes = notmuch.get_envelopes("inbox", 1, 0).unwrap();
|
||||
let envelopes: &NotmuchEnvelopes = envelopes.as_any().downcast_ref().unwrap();
|
||||
let envelope = envelopes.first().unwrap();
|
||||
assert!(envelope.flags.contains(&"inbox".into()));
|
||||
assert!(!envelope.flags.contains(&"seen".into()));
|
||||
assert!(!envelope.flags.contains(&"flagged".into()));
|
||||
assert!(!envelope.flags.contains(&"passed".into()));
|
||||
}
|
Loading…
Reference in a new issue