diff --git a/Cargo.lock b/Cargo.lock index de95138..24c5795 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -467,6 +467,17 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", +] + [[package]] name = "glob" version = "0.3.3" @@ -834,6 +845,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "regex-automata" version = "0.4.14" @@ -869,6 +886,7 @@ dependencies = [ "sd-notify", "serde", "serde_json", + "tempfile", "tokio", "tokio-util", "toml", @@ -1048,6 +1066,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "terminal_size" version = "0.4.4" diff --git a/Cargo.toml b/Cargo.toml index 9e15930..cf3216f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,3 +91,4 @@ codegen-units = 1 [dev-dependencies] indoc = "2.0.7" +tempfile = "3.27.0" diff --git a/src/server/fingerd/local_email.rs b/src/server/fingerd/local_email.rs index 4c394a0..697839c 100644 --- a/src/server/fingerd/local_email.rs +++ b/src/server/fingerd/local_email.rs @@ -89,3 +89,120 @@ fn detect_new_mail_by_maildir(maildir_path: &Path) -> anyhow::Result )) } } + +#[cfg(test)] +mod tests { + use super::*; + use nix::{ + fcntl::AT_FDCWD, + sys::{stat::UtimensatFlags, time::TimeSpec}, + }; + use std::{fs, os::unix::fs::MetadataExt}; + use tempfile::TempDir; + + fn set_file_times(path: &Path, atime: i64, mtime: i64) { + nix::sys::stat::utimensat( + AT_FDCWD, + path, + &TimeSpec::new(atime, 0), + &TimeSpec::new(mtime, 0), + UtimensatFlags::FollowSymlink, + ) + .unwrap(); + } + + fn set_dir_mtime(path: &Path, mtime: i64) { + let metadata = fs::metadata(path).unwrap(); + set_file_times(path, metadata.atime(), mtime); + } + + #[test] + fn test_detect_new_mail_by_mailbox_new_mail_received() { + let tempdir = TempDir::new().unwrap(); + let mailbox = tempdir.path().join("Mailbox"); + fs::write(&mailbox, b"mail").unwrap(); + set_file_times(&mailbox, 1_700_000_000, 1_700_000_100); + + let status = detect_new_mail_by_mailbox(&mailbox).unwrap(); + + assert_eq!( + status, + MailStatus::NewMailReceived { + received_time: Utc.timestamp_opt(1_700_000_100, 0).single().unwrap(), + unread_since: Utc.timestamp_opt(1_700_000_000, 0).single().unwrap(), + } + ); + } + + #[test] + fn test_detect_new_mail_by_mailbox_mail_last_read() { + let tempdir = TempDir::new().unwrap(); + let mailbox = tempdir.path().join("Mailbox"); + fs::write(&mailbox, b"mail").unwrap(); + set_file_times(&mailbox, 1_700_000_100, 1_700_000_000); + + let status = detect_new_mail_by_mailbox(&mailbox).unwrap(); + + assert_eq!( + status, + MailStatus::MailLastRead(Utc.timestamp_opt(1_700_000_100, 0).single().unwrap()) + ); + } + + #[test] + fn test_detect_new_mail_by_maildir_no_mail() { + let tempdir = TempDir::new().unwrap(); + let maildir = tempdir.path().join("Maildir"); + let new_dir = maildir.join("new"); + let cur_dir = maildir.join("cur"); + fs::create_dir_all(&new_dir).unwrap(); + fs::create_dir_all(&cur_dir).unwrap(); + + let status = detect_new_mail_by_maildir(&maildir).unwrap(); + + assert_eq!(status, MailStatus::NoMail); + } + + #[test] + fn test_detect_new_mail_by_maildir_new_mail_received() { + let tempdir = TempDir::new().unwrap(); + let maildir = tempdir.path().join("Maildir"); + let new_dir = maildir.join("new"); + let cur_dir = maildir.join("cur"); + fs::create_dir_all(&new_dir).unwrap(); + fs::create_dir_all(&cur_dir).unwrap(); + fs::write(new_dir.join("msg"), b"mail").unwrap(); + set_dir_mtime(&cur_dir, 1_700_000_000); + set_dir_mtime(&new_dir, 1_700_000_100); + + let status = detect_new_mail_by_maildir(&maildir).unwrap(); + + assert_eq!( + status, + MailStatus::NewMailReceived { + received_time: Utc.timestamp_opt(1_700_000_100, 0).single().unwrap(), + unread_since: Utc.timestamp_opt(1_700_000_000, 0).single().unwrap(), + } + ); + } + + #[test] + fn test_detect_new_mail_by_maildir_mail_last_read() { + let tempdir = TempDir::new().unwrap(); + let maildir = tempdir.path().join("Maildir"); + let new_dir = maildir.join("new"); + let cur_dir = maildir.join("cur"); + fs::create_dir_all(&new_dir).unwrap(); + fs::create_dir_all(&cur_dir).unwrap(); + fs::write(cur_dir.join("msg"), b"mail").unwrap(); + set_dir_mtime(&new_dir, 1_700_000_000); + set_dir_mtime(&cur_dir, 1_700_000_100); + + let status = detect_new_mail_by_maildir(&maildir).unwrap(); + + assert_eq!( + status, + MailStatus::MailLastRead(Utc.timestamp_opt(1_700_000_100, 0).single().unwrap()) + ); + } +}