//! Privacy settings (#1062): `w:removePersonalInformation` and `w:removeDateAndTime` //! (ECMA-376 Part 1 §17.15.1.69–70) are kept on save and honoured by the writer. Synthetic //! fixtures written for these tests from the spec; the names are made up. use std::collections::BTreeMap; use std::io::{Cursor, Read, Write}; const W_NS: &str = r#"xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships""#; const RT: &str = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/"; fn zip(entries: &[(&str, String)]) -> Vec { let mut z = zip::ZipWriter::new(Cursor::new(Vec::new())); for (name, text) in entries { z.start_file(*name, zip::write::SimpleFileOptions::default()).unwrap(); z.write_all(text.as_bytes()).unwrap(); } z.finish().unwrap().into_inner() } fn unzip(bytes: &[u8]) -> BTreeMap { let mut z = zip::ZipArchive::new(Cursor::new(bytes)).unwrap(); let mut parts = BTreeMap::new(); for i in 0..z.len() { let mut f = z.by_index(i).unwrap(); let mut s = String::new(); if f.read_to_string(&mut s).is_ok() { parts.insert(f.name().to_string(), s); } } parts } /// A package with two named commenters, a tracked insertion and deletion, the document /// properties' creator and last-modified-by, and `settings` inside `w:settings`. fn fixture(settings: &str) -> Vec { let body = r#"Clause one. AddedRemoved"#; let comments = format!( r#"Check this.Agreed."# ); let core = r#"Draft termsQuill RamseyOna Vettel"#; let root_rels = format!( r#""# ); let doc_rels = format!( r#""# ); zip(&[ ("_rels/.rels", root_rels), ("word/document.xml", format!(r#"{body}"#)), ("word/_rels/document.xml.rels", doc_rels), ("word/comments.xml", comments), ("word/settings.xml", format!(r#"{settings}"#)), ("docProps/core.xml", core.to_string()), ]) } const NAMES: [&str; 4] = ["Quill Ramsey", "Ona Vettel", "\"QR\"", "\"OV\""]; /// Every part of the saved package, joined. fn saved(doc: &wordcraft_doc::Document) -> (BTreeMap, String) { let parts = unzip(&wordcraft_docx::write(doc).unwrap()); let all = parts.values().cloned().collect::>().join("\n"); (parts, all) } #[test] fn remove_personal_information_and_dates_on_save() { let doc = wordcraft_docx::read(&fixture("")).unwrap(); assert!(doc.settings.remove_personal_info && doc.settings.remove_date_time); let before = doc.clone(); let (parts, all) = saved(&doc); // The open document keeps its real names and dates. assert_eq!(doc, before); assert_eq!(doc.core.creator, "Quill Ramsey"); assert!(doc.comments.values().any(|c| c.author == "Ona Vettel" && !c.date.is_empty())); // No name, initials or annotation date anywhere in the file. for name in NAMES { assert!(!all.contains(name), "{name} saved"); } assert!(!all.contains("w:date="), "annotation date saved"); assert!(!all.contains("2026-03-0"), "annotation date saved"); let comments = &parts["word/comments.xml"]; assert_eq!(comments.matches(r#"w:author="Author""#).count(), 2, "{comments}"); assert_eq!(comments.matches(r#"w:initials="A""#).count(), 2, "{comments}"); let body = &parts["word/document.xml"]; assert!(body.contains("").unwrap(); let dates = settings.find("").unwrap(); assert!(zoom < personal && personal < dates, "{settings}"); let again = wordcraft_docx::read(&wordcraft_docx::write(&doc).unwrap()).unwrap(); assert!(again.settings.remove_personal_info && again.settings.remove_date_time); } #[test] fn each_privacy_setting_works_alone() { // Dates only: names stay. let doc = wordcraft_docx::read(&fixture("")).unwrap(); let (parts, all) = saved(&doc); assert!(!all.contains("w:date=")); assert!(all.contains("Quill Ramsey") && all.contains("Ona Vettel")); assert!(parts["docProps/core.xml"].contains("Quill Ramsey")); assert!(!parts["word/settings.xml"].contains("removePersonalInformation")); // Names only: dates stay. let doc = wordcraft_docx::read(&fixture(r#""#)).unwrap(); let (_, all) = saved(&doc); for name in NAMES { assert!(!all.contains(name), "{name} saved"); } assert_eq!(all.matches("w:date=").count(), 4, "two comments and two changes keep their dates"); // Off (explicitly or absent): everything is saved as it was. for settings in ["", r#""#] { let doc = wordcraft_docx::read(&fixture(settings)).unwrap(); assert!(!doc.settings.remove_personal_info && !doc.settings.remove_date_time); let (parts, all) = saved(&doc); assert!(all.contains("Quill Ramsey") && all.contains(r#"w:initials="OV""#)); assert_eq!(all.matches("w:date=").count(), 4); assert!(parts["docProps/core.xml"].contains("Ona Vettel")); assert!(!parts["word/settings.xml"].contains("removeDateAndTime")); } }