use bstr::BString;
use chrono::{TimeZone, Utc};
use jamjam::jam::pack::PackOptions;
use jamjam::jam::{JamMessage, JamMessageBase, attributes, raw};
use jamjam::util::echomail::EchomailAddress;
use pretty_assertions::assert_eq;
use tempfile::TempDir;
fn base(dir: &TempDir) -> JamMessageBase {
let mut base = JamMessageBase::create(dir.path().join("packing")).unwrap();
for text in ["one", "two", "three", "four"] {
let msg = JamMessage::new(&EchomailAddress::default())
.with_date_time(Utc.with_ymd_and_hms(1990, 1, 1, 0, 0, 0).unwrap())
.with_from(BString::from("sysop"))
.with_to(BString::from(text))
.with_subject(BString::from(text))
.with_text(BString::from(text));
base.write_message(&msg).unwrap();
}
base.write_jhr_header().unwrap();
base
}
fn texts(base: &JamMessageBase) -> Vec<String> {
(base.lowest_message_number()..=base.highest_message_number())
.filter_map(|no| base.read_header(no).ok())
.map(|header| base.read_message_text(&header).unwrap().to_string())
.collect()
}
#[test]
fn test_pack_removes_deleted_messages() {
let dir = TempDir::new().unwrap();
let mut base = base(&dir);
base.delete_message(2).unwrap();
let report = base.pack(&PackOptions::default()).unwrap();
assert_eq!(report.before, 4);
assert_eq!(report.removed, 1);
assert_eq!(report.kept, 3);
assert_eq!(report.low_message_number, 1);
assert_eq!(report.high_message_number, 4);
assert_eq!(texts(&base), vec!["one", "three", "four"]);
}
#[test]
fn test_pack_keeps_message_numbers() {
let dir = TempDir::new().unwrap();
let mut base = base(&dir);
base.delete_message(2).unwrap();
base.pack(&PackOptions::default()).unwrap();
assert_eq!(base.read_header(3).unwrap().message_number, 3);
assert!(base.read_header(2).is_err());
let msg = JamMessage::new(&EchomailAddress::default()).with_text(BString::from("five"));
assert_eq!(base.write_message(&msg).unwrap(), 5);
assert_eq!(base.read_header(5).unwrap().message_number, 5);
assert_eq!(
base.read_message_text(&base.read_header(1).unwrap())
.unwrap(),
"one"
);
}
#[test]
fn test_pack_renumbers() {
let dir = TempDir::new().unwrap();
let mut base = base(&dir);
base.delete_message(1).unwrap();
let report = base
.pack(&PackOptions::default().with_renumber_from(1))
.unwrap();
assert_eq!(report.before, 4);
assert_eq!(report.removed, 1);
assert_eq!(report.kept, 3);
assert_eq!(report.low_message_number, 1);
assert_eq!(report.high_message_number, 3);
assert_eq!(texts(&base), vec!["two", "three", "four"]);
assert_eq!(base.read_header(1).unwrap().message_number, 1);
let msg = JamMessage::new(&EchomailAddress::default()).with_text(BString::from("five"));
assert_eq!(base.write_message(&msg).unwrap(), 4);
}
#[test]
fn test_pack_renumbers_from_a_new_low_number() {
let dir = TempDir::new().unwrap();
let mut base = base(&dir);
base.pack(&PackOptions::default().with_renumber_from(500))
.unwrap();
assert_eq!(base.lowest_message_number(), 500);
assert_eq!(base.highest_message_number(), 503);
assert_eq!(base.active_messages(), 4);
assert_eq!(
base.read_message_text(&base.read_header(500).unwrap())
.unwrap(),
"one"
);
assert_eq!(base.read_header(503).unwrap().message_number, 503);
}
#[test]
fn test_pack_purges_by_date() {
let dir = TempDir::new().unwrap();
let mut base = base(&dir);
let msg = JamMessage::new(&EchomailAddress::default())
.with_date_time(Utc.with_ymd_and_hms(1995, 1, 1, 0, 0, 0).unwrap())
.with_text(BString::from("recent"));
base.write_message(&msg).unwrap();
let report = base
.pack(
&PackOptions::default()
.with_purge_before(Utc.with_ymd_and_hms(1992, 1, 1, 0, 0, 0).unwrap()),
)
.unwrap();
assert_eq!(report.removed, 4);
assert_eq!(texts(&base), vec!["recent"]);
}
#[test]
fn test_pack_purges_received_private_messages() {
let dir = TempDir::new().unwrap();
let mut base = base(&dir);
raw::set_attributes(
&mut base,
2,
attributes::MSG_PRIVATE | attributes::MSG_READ,
0,
)
.unwrap();
raw::set_attributes(&mut base, 3, attributes::MSG_PRIVATE, 0).unwrap();
let report = base
.pack(&PackOptions::default().with_purge_received_private(true))
.unwrap();
assert_eq!(report.removed, 1);
assert_eq!(texts(&base), vec!["one", "three", "four"]);
}
#[test]
fn test_pack_purges_messages_whose_packout_date_arrived() {
let dir = TempDir::new().unwrap();
let mut base = base(&dir);
let expired = JamMessage::new(&EchomailAddress::default())
.with_packout_date(Utc.with_ymd_and_hms(1991, 1, 1, 0, 0, 0).unwrap())
.with_text(BString::from("expired"));
base.write_message(&expired).unwrap();
let later = JamMessage::new(&EchomailAddress::default())
.with_packout_date(Utc.with_ymd_and_hms(2999, 1, 1, 0, 0, 0).unwrap())
.with_text(BString::from("later"));
base.write_message(&later).unwrap();
let report = base.pack(&PackOptions::default()).unwrap();
assert_eq!(report.removed, 1);
assert_eq!(texts(&base), vec!["one", "two", "three", "four", "later"]);
}
#[test]
fn test_pack_shrinks_the_files() {
let dir = TempDir::new().unwrap();
let mut base = base(&dir);
let before = std::fs::metadata(dir.path().join("packing.jhr"))
.unwrap()
.len();
base.delete_message(2).unwrap();
base.pack(&PackOptions::default()).unwrap();
assert!(
std::fs::metadata(dir.path().join("packing.jhr"))
.unwrap()
.len()
< before
);
assert_eq!(
std::fs::metadata(dir.path().join("packing.jdt"))
.unwrap()
.len(),
"onethreefour".len() as u64
);
}
#[test]
fn test_reindex_rebuilds_a_lost_index() {
let dir = TempDir::new().unwrap();
let mut base = base(&dir);
let mut header = base.read_header(2).unwrap();
header.set_subject(BString::from("a much longer subject than before"));
raw::update_header(&mut base, 2, &header).unwrap();
std::fs::write(dir.path().join("packing.jdx"), []).unwrap();
let report = base
.pack(&PackOptions::default().with_index_only(true))
.unwrap();
assert_eq!(report.kept, 4);
assert_eq!(report.removed, 0);
assert_eq!(base.active_messages(), 4);
assert_eq!(texts(&base), vec!["one", "two", "three", "four"]);
assert_eq!(
base.read_header(2).unwrap().subject().unwrap(),
"a much longer subject than before"
);
}
#[test]
fn test_reindex_keeps_deleted_messages() {
let dir = TempDir::new().unwrap();
let mut base = base(&dir);
base.delete_message(2).unwrap();
std::fs::write(dir.path().join("packing.jdx"), []).unwrap();
base.pack(&PackOptions::default().with_index_only(true))
.unwrap();
base.restore_message(2).unwrap();
assert_eq!(texts(&base), vec!["one", "two", "three", "four"]);
}