add maildir backend flag tests

This commit is contained in:
Clément DOUIN 2022-03-01 13:34:24 +01:00
parent f631f63799
commit 4093d13765
No known key found for this signature in database
GPG key ID: 353E4A18EE0FAB72
2 changed files with 184 additions and 164 deletions

View file

@ -77,10 +77,8 @@ impl<'a> Backend<'a> for MaildirBackend<'a> {
let path = self.mdir.path().join(format!(".{}", subdir)); let path = self.mdir.path().join(format!(".{}", subdir));
trace!("subdir path: {:?}", path); trace!("subdir path: {:?}", path);
fs::create_dir(&path).context(format!( fs::create_dir(&path)
"cannot create maildir subdir {:?} at {:?}", .with_context(|| format!("cannot create maildir subdir {:?} at {:?}", subdir, path))?;
subdir, path
))?;
info!("<< add maildir subdir"); info!("<< add maildir subdir");
Ok(()) Ok(())
@ -89,10 +87,10 @@ impl<'a> Backend<'a> for MaildirBackend<'a> {
fn get_mboxes(&mut self) -> Result<Box<dyn Mboxes>> { fn get_mboxes(&mut self) -> Result<Box<dyn Mboxes>> {
info!(">> get maildir dirs"); info!(">> get maildir dirs");
let dirs: MaildirMboxes = self.mdir.list_subdirs().try_into().context(format!( let dirs: MaildirMboxes =
"cannot parse maildir dirs from {:?}", self.mdir.list_subdirs().try_into().with_context(|| {
self.mdir.path() format!("cannot parse maildir dirs from {:?}", self.mdir.path())
))?; })?;
trace!("dirs: {:?}", dirs); trace!("dirs: {:?}", dirs);
info!("<< get maildir dirs"); info!("<< get maildir dirs");
@ -107,7 +105,7 @@ impl<'a> Backend<'a> for MaildirBackend<'a> {
trace!("dir path: {:?}", path); trace!("dir path: {:?}", path);
fs::remove_dir_all(&path) fs::remove_dir_all(&path)
.context(format!("cannot delete maildir {:?} from {:?}", dir, path))?; .with_context(|| format!("cannot delete maildir {:?} from {:?}", dir, path))?;
info!("<< delete maildir dir"); info!("<< delete maildir dir");
Ok(()) Ok(())
@ -126,14 +124,13 @@ impl<'a> Backend<'a> for MaildirBackend<'a> {
let mdir = self let mdir = self
.get_mdir_from_dir(dir) .get_mdir_from_dir(dir)
.context(format!("cannot get maildir instance from {:?}", dir))?; .with_context(|| format!("cannot get maildir instance from {:?}", dir))?;
// Reads envelopes from the "cur" folder of the selected // Reads envelopes from the "cur" folder of the selected
// maildir. // maildir.
let mut envelopes: MaildirEnvelopes = mdir.list_cur().try_into().context(format!( let mut envelopes: MaildirEnvelopes = mdir.list_cur().try_into().with_context(|| {
"cannot parse maildir envelopes from {:?}", format!("cannot parse maildir envelopes from {:?}", self.mdir.path())
self.mdir.path() })?;
))?;
debug!("envelopes len: {:?}", envelopes.len()); debug!("envelopes len: {:?}", envelopes.len());
trace!("envelopes: {:?}", envelopes); trace!("envelopes: {:?}", envelopes);
@ -200,28 +197,28 @@ impl<'a> Backend<'a> for MaildirBackend<'a> {
let mdir = self let mdir = self
.get_mdir_from_dir(dir) .get_mdir_from_dir(dir)
.context(format!("cannot get maildir instance from {:?}", dir))?; .with_context(|| format!("cannot get maildir instance from {:?}", dir))?;
let flags: MaildirFlags = flags let flags: MaildirFlags = flags
.try_into() .try_into()
.context(format!("cannot parse maildir flags {:?}", flags))?; .with_context(|| format!("cannot parse maildir flags {:?}", flags))?;
let id = mdir let id = mdir
.store_cur_with_flags(msg, &flags.to_string()) .store_cur_with_flags(msg, &flags.to_string())
.context(format!("cannot add maildir message to {:?}", mdir.path()))?; .with_context(|| format!("cannot add maildir message to {:?}", mdir.path()))?;
debug!("id: {:?}", id); debug!("id: {:?}", id);
let hash = format!("{:x}", md5::compute(&id)); let hash = format!("{:x}", md5::compute(&id));
debug!("hash: {:?}", hash); debug!("hash: {:?}", hash);
// Appends hash entry to the id mapper cache file. // Appends hash entry to the id mapper cache file.
let mut mapper = IdMapper::new(mdir.path()).context(format!( let mut mapper = IdMapper::new(mdir.path())
"cannot create id mapper instance for {:?}", .with_context(|| format!("cannot create id mapper instance for {:?}", mdir.path()))?;
mdir.path()
))?;
mapper mapper
.append(vec![(hash.clone(), id.clone())]) .append(vec![(hash.clone(), id.clone())])
.context(format!( .with_context(|| {
"cannot append hash {:?} with id {:?} to id mapper", format!(
hash, id "cannot append hash {:?} with id {:?} to id mapper",
))?; hash, id
)
})?;
info!("<< add maildir message"); info!("<< add maildir message");
Ok(Box::new(hash)) Ok(Box::new(hash))
@ -234,14 +231,16 @@ impl<'a> Backend<'a> for MaildirBackend<'a> {
let mdir = self let mdir = self
.get_mdir_from_dir(dir) .get_mdir_from_dir(dir)
.context(format!("cannot get maildir instance from {:?}", dir))?; .with_context(|| format!("cannot get maildir instance from {:?}", dir))?;
let id = IdMapper::new(mdir.path())? let id = IdMapper::new(mdir.path())?
.find(short_hash) .find(short_hash)
.context(format!( .with_context(|| {
"cannot find maildir message by short hash {:?} at {:?}", format!(
short_hash, "cannot find maildir message by short hash {:?} at {:?}",
mdir.path() short_hash,
))?; mdir.path()
)
})?;
debug!("id: {:?}", id); debug!("id: {:?}", id);
let mut mail_entry = mdir.find(&id).ok_or_else(|| { let mut mail_entry = mdir.find(&id).ok_or_else(|| {
anyhow!( anyhow!(
@ -250,16 +249,12 @@ impl<'a> Backend<'a> for MaildirBackend<'a> {
mdir.path() mdir.path()
) )
})?; })?;
let parsed_mail = mail_entry.parsed().context(format!( let parsed_mail = mail_entry.parsed().with_context(|| {
"cannot parse maildir message {:?} at {:?}", format!("cannot parse maildir message {:?} at {:?}", id, mdir.path())
id, })?;
mdir.path() let msg = Msg::from_parsed_mail(parsed_mail, self.account_config).with_context(|| {
))?; format!("cannot parse maildir message {:?} at {:?}", id, mdir.path())
let msg = Msg::from_parsed_mail(parsed_mail, self.account_config).context(format!( })?;
"cannot parse maildir message {:?} at {:?}",
id,
mdir.path()
))?;
trace!("message: {:?}", msg); trace!("message: {:?}", msg);
info!("<< get maildir message"); info!("<< get maildir message");
@ -271,46 +266,46 @@ impl<'a> Backend<'a> for MaildirBackend<'a> {
debug!("source dir: {:?}", dir_src); debug!("source dir: {:?}", dir_src);
debug!("destination dir: {:?}", dir_dst); debug!("destination dir: {:?}", dir_dst);
let mdir_src = self.get_mdir_from_dir(dir_src).context(format!( let mdir_src = self
"cannot get source maildir instance from {:?}", .get_mdir_from_dir(dir_src)
dir_src .with_context(|| format!("cannot get source maildir instance from {:?}", dir_src))?;
))?; let mdir_dst = self.get_mdir_from_dir(dir_dst).with_context(|| {
let mdir_dst = self.get_mdir_from_dir(dir_dst).context(format!( format!("cannot get destination maildir instance from {:?}", dir_dst)
"cannot get destination maildir instance from {:?}", })?;
dir_dst
))?;
let id = IdMapper::new(mdir_src.path()) let id = IdMapper::new(mdir_src.path())
.context(format!( .with_context(|| format!("cannot create id mapper instance for {:?}", mdir_src.path()))?
"cannot create id mapper instance for {:?}",
mdir_src.path()
))?
.find(short_hash) .find(short_hash)
.context(format!( .with_context(|| {
"cannot find maildir message by short hash {:?} at {:?}", format!(
short_hash, "cannot find maildir message by short hash {:?} at {:?}",
mdir_src.path() short_hash,
))?; mdir_src.path()
)
})?;
debug!("id: {:?}", id); debug!("id: {:?}", id);
mdir_src.copy_to(&id, &mdir_dst).context(format!( mdir_src.copy_to(&id, &mdir_dst).with_context(|| {
"cannot copy message {:?} from maildir {:?} to maildir {:?}", format!(
id, "cannot copy message {:?} from maildir {:?} to maildir {:?}",
mdir_src.path(), id,
mdir_dst.path() mdir_src.path(),
))?; mdir_dst.path()
)
})?;
// Appends hash entry to the id mapper cache file. // Appends hash entry to the id mapper cache file.
let mut mapper = IdMapper::new(mdir_dst.path()).context(format!( let mut mapper = IdMapper::new(mdir_dst.path()).with_context(|| {
"cannot create id mapper instance for {:?}", format!("cannot create id mapper instance for {:?}", mdir_dst.path())
mdir_dst.path() })?;
))?;
let hash = format!("{:x}", md5::compute(&id)); let hash = format!("{:x}", md5::compute(&id));
mapper mapper
.append(vec![(hash.clone(), id.clone())]) .append(vec![(hash.clone(), id.clone())])
.context(format!( .with_context(|| {
"cannot append hash {:?} with id {:?} to id mapper", format!(
hash, id "cannot append hash {:?} with id {:?} to id mapper",
))?; hash, id
)
})?;
info!("<< copy maildir message"); info!("<< copy maildir message");
Ok(()) Ok(())
@ -321,46 +316,46 @@ impl<'a> Backend<'a> for MaildirBackend<'a> {
debug!("source dir: {:?}", dir_src); debug!("source dir: {:?}", dir_src);
debug!("destination dir: {:?}", dir_dst); debug!("destination dir: {:?}", dir_dst);
let mdir_src = self.get_mdir_from_dir(dir_src).context(format!( let mdir_src = self
"cannot get source maildir instance from {:?}", .get_mdir_from_dir(dir_src)
dir_src .with_context(|| format!("cannot get source maildir instance from {:?}", dir_src))?;
))?; let mdir_dst = self.get_mdir_from_dir(dir_dst).with_context(|| {
let mdir_dst = self.get_mdir_from_dir(dir_dst).context(format!( format!("cannot get destination maildir instance from {:?}", dir_dst)
"cannot get destination maildir instance from {:?}", })?;
dir_dst
))?;
let id = IdMapper::new(mdir_src.path()) let id = IdMapper::new(mdir_src.path())
.context(format!( .with_context(|| format!("cannot create id mapper instance for {:?}", mdir_src.path()))?
"cannot create id mapper instance for {:?}",
mdir_src.path()
))?
.find(short_hash) .find(short_hash)
.context(format!( .with_context(|| {
"cannot find maildir message by short hash {:?} at {:?}", format!(
short_hash, "cannot find maildir message by short hash {:?} at {:?}",
mdir_src.path() short_hash,
))?; mdir_src.path()
)
})?;
debug!("id: {:?}", id); debug!("id: {:?}", id);
mdir_src.move_to(&id, &mdir_dst).context(format!( mdir_src.move_to(&id, &mdir_dst).with_context(|| {
"cannot move message {:?} from maildir {:?} to maildir {:?}", format!(
id, "cannot move message {:?} from maildir {:?} to maildir {:?}",
mdir_src.path(), id,
mdir_dst.path() mdir_src.path(),
))?; mdir_dst.path()
)
})?;
// Appends hash entry to the id mapper cache file. // Appends hash entry to the id mapper cache file.
let mut mapper = IdMapper::new(mdir_dst.path()).context(format!( let mut mapper = IdMapper::new(mdir_dst.path()).with_context(|| {
"cannot create id mapper instance for {:?}", format!("cannot create id mapper instance for {:?}", mdir_dst.path())
mdir_dst.path() })?;
))?;
let hash = format!("{:x}", md5::compute(&id)); let hash = format!("{:x}", md5::compute(&id));
mapper mapper
.append(vec![(hash.clone(), id.clone())]) .append(vec![(hash.clone(), id.clone())])
.context(format!( .with_context(|| {
"cannot append hash {:?} with id {:?} to id mapper", format!(
hash, id "cannot append hash {:?} with id {:?} to id mapper",
))?; hash, id
)
})?;
info!("<< move maildir message"); info!("<< move maildir message");
Ok(()) Ok(())
@ -373,24 +368,25 @@ impl<'a> Backend<'a> for MaildirBackend<'a> {
let mdir = self let mdir = self
.get_mdir_from_dir(dir) .get_mdir_from_dir(dir)
.context(format!("cannot get maildir instance from {:?}", dir))?; .with_context(|| format!("cannot get maildir instance from {:?}", dir))?;
let id = IdMapper::new(mdir.path()) let id = IdMapper::new(mdir.path())
.context(format!( .with_context(|| format!("cannot create id mapper instance for {:?}", mdir.path()))?
"cannot create id mapper instance for {:?}",
mdir.path()
))?
.find(short_hash) .find(short_hash)
.context(format!( .with_context(|| {
"cannot find maildir message by short hash {:?} at {:?}", format!(
short_hash, "cannot find maildir message by short hash {:?} at {:?}",
mdir.path() short_hash,
))?; mdir.path()
)
})?;
debug!("id: {:?}", id); debug!("id: {:?}", id);
mdir.delete(&id).context(format!( mdir.delete(&id).with_context(|| {
"cannot delete message {:?} from maildir {:?}", format!(
id, "cannot delete message {:?} from maildir {:?}",
mdir.path() id,
))?; mdir.path()
)
})?;
info!("<< delete maildir message"); info!("<< delete maildir message");
Ok(()) Ok(())
@ -404,27 +400,24 @@ impl<'a> Backend<'a> for MaildirBackend<'a> {
let mdir = self let mdir = self
.get_mdir_from_dir(dir) .get_mdir_from_dir(dir)
.context(format!("cannot get maildir instance from {:?}", dir))?; .with_context(|| format!("cannot get maildir instance from {:?}", dir))?;
let flags: MaildirFlags = flags let flags: MaildirFlags = flags
.try_into() .try_into()
.context(format!("cannot parse maildir flags {:?}", flags))?; .with_context(|| format!("cannot parse maildir flags {:?}", flags))?;
debug!("flags: {:?}", flags); debug!("flags: {:?}", flags);
let id = IdMapper::new(mdir.path()) let id = IdMapper::new(mdir.path())
.context(format!( .with_context(|| format!("cannot create id mapper instance for {:?}", mdir.path()))?
"cannot create id mapper instance for {:?}",
mdir.path()
))?
.find(short_hash) .find(short_hash)
.context(format!( .with_context(|| {
"cannot find maildir message by short hash {:?} at {:?}", format!(
short_hash, "cannot find maildir message by short hash {:?} at {:?}",
mdir.path() short_hash,
))?; mdir.path()
)
})?;
debug!("id: {:?}", id); debug!("id: {:?}", id);
mdir.add_flags(&id, &flags.to_string()).context(format!( mdir.add_flags(&id, &flags.to_string())
"cannot add flags {:?} to maildir message {:?}", .with_context(|| format!("cannot add flags {:?} to maildir message {:?}", flags, id))?;
flags, id
))?;
info!("<< add maildir message flags"); info!("<< add maildir message flags");
Ok(()) Ok(())
@ -438,27 +431,24 @@ impl<'a> Backend<'a> for MaildirBackend<'a> {
let mdir = self let mdir = self
.get_mdir_from_dir(dir) .get_mdir_from_dir(dir)
.context(format!("cannot get maildir instance from {:?}", dir))?; .with_context(|| format!("cannot get maildir instance from {:?}", dir))?;
let flags: MaildirFlags = flags let flags: MaildirFlags = flags
.try_into() .try_into()
.context(format!("cannot parse maildir flags {:?}", flags))?; .with_context(|| format!("cannot parse maildir flags {:?}", flags))?;
debug!("flags: {:?}", flags); debug!("flags: {:?}", flags);
let id = IdMapper::new(mdir.path()) let id = IdMapper::new(mdir.path())
.context(format!( .with_context(|| format!("cannot create id mapper instance for {:?}", mdir.path()))?
"cannot create id mapper instance for {:?}",
mdir.path()
))?
.find(short_hash) .find(short_hash)
.context(format!( .with_context(|| {
"cannot find maildir message by short hash {:?} at {:?}", format!(
short_hash, "cannot find maildir message by short hash {:?} at {:?}",
mdir.path() short_hash,
))?; mdir.path()
)
})?;
debug!("id: {:?}", id); debug!("id: {:?}", id);
mdir.set_flags(&id, &flags.to_string()).context(format!( mdir.set_flags(&id, &flags.to_string())
"cannot set flags {:?} to maildir message {:?}", .with_context(|| format!("cannot set flags {:?} to maildir message {:?}", flags, id))?;
flags, id
))?;
info!("<< set maildir message flags"); info!("<< set maildir message flags");
Ok(()) Ok(())
@ -472,27 +462,29 @@ impl<'a> Backend<'a> for MaildirBackend<'a> {
let mdir = self let mdir = self
.get_mdir_from_dir(dir) .get_mdir_from_dir(dir)
.context(format!("cannot get maildir instance from {:?}", dir))?; .with_context(|| format!("cannot get maildir instance from {:?}", dir))?;
let flags: MaildirFlags = flags let flags: MaildirFlags = flags
.try_into() .try_into()
.context(format!("cannot parse maildir flags {:?}", flags))?; .with_context(|| format!("cannot parse maildir flags {:?}", flags))?;
debug!("flags: {:?}", flags); debug!("flags: {:?}", flags);
let id = IdMapper::new(mdir.path()) let id = IdMapper::new(mdir.path())
.context(format!( .with_context(|| format!("cannot create id mapper instance for {:?}", mdir.path()))?
"cannot create id mapper instance for {:?}",
mdir.path()
))?
.find(short_hash) .find(short_hash)
.context(format!( .with_context(|| {
"cannot find maildir message by short hash {:?} at {:?}", format!(
short_hash, "cannot find maildir message by short hash {:?} at {:?}",
mdir.path() short_hash,
))?; mdir.path()
)
})?;
debug!("id: {:?}", id); debug!("id: {:?}", id);
mdir.remove_flags(&id, &flags.to_string()).context(format!( mdir.remove_flags(&id, &flags.to_string())
"cannot delete flags {:?} to maildir message {:?}", .with_context(|| {
flags, id format!(
))?; "cannot delete flags {:?} to maildir message {:?}",
flags, id
)
})?;
info!("<< delete maildir message flags"); info!("<< delete maildir message flags");
Ok(()) Ok(())

View file

@ -2,7 +2,7 @@ use maildir::Maildir;
use std::{collections::HashMap, env, fs, iter::FromIterator}; use std::{collections::HashMap, env, fs, iter::FromIterator};
use himalaya::{ use himalaya::{
backends::{Backend, MaildirBackend, MaildirEnvelopes}, backends::{Backend, MaildirBackend, MaildirEnvelopes, MaildirFlag},
config::{AccountConfig, MaildirBackendConfig}, config::{AccountConfig, MaildirBackendConfig},
}; };
@ -49,6 +49,34 @@ fn test_maildir_backend() {
assert_eq!("alice@localhost", envelope.sender); assert_eq!("alice@localhost", envelope.sender);
assert_eq!("Plain message", envelope.subject); assert_eq!("Plain message", envelope.subject);
// check that a flag can be added to the message
mdir.add_flags("INBOX", &envelope.hash, "flagged passed")
.unwrap();
let envelopes = mdir.get_envelopes("INBOX", 1, 0).unwrap();
let envelopes: &MaildirEnvelopes = envelopes.as_any().downcast_ref().unwrap();
let envelope = envelopes.first().unwrap();
assert!(envelope.flags.contains(&MaildirFlag::Seen));
assert!(envelope.flags.contains(&MaildirFlag::Flagged));
assert!(envelope.flags.contains(&MaildirFlag::Passed));
// check that the message flags can be changed
mdir.set_flags("INBOX", &envelope.hash, "passed").unwrap();
let envelopes = mdir.get_envelopes("INBOX", 1, 0).unwrap();
let envelopes: &MaildirEnvelopes = envelopes.as_any().downcast_ref().unwrap();
let envelope = envelopes.first().unwrap();
assert!(!envelope.flags.contains(&MaildirFlag::Seen));
assert!(!envelope.flags.contains(&MaildirFlag::Flagged));
assert!(envelope.flags.contains(&MaildirFlag::Passed));
// check that a flag can be removed from the message
mdir.del_flags("INBOX", &envelope.hash, "passed").unwrap();
let envelopes = mdir.get_envelopes("INBOX", 1, 0).unwrap();
let envelopes: &MaildirEnvelopes = envelopes.as_any().downcast_ref().unwrap();
let envelope = envelopes.first().unwrap();
assert!(!envelope.flags.contains(&MaildirFlag::Seen));
assert!(!envelope.flags.contains(&MaildirFlag::Flagged));
assert!(!envelope.flags.contains(&MaildirFlag::Passed));
// check that the message can be copied // check that the message can be copied
mdir.copy_msg("INBOX", "Subdir", &envelope.hash).unwrap(); mdir.copy_msg("INBOX", "Subdir", &envelope.hash).unwrap();
assert!(mdir.get_msg("INBOX", &hash).is_ok()); assert!(mdir.get_msg("INBOX", &hash).is_ok());