From b1761d5bbfe3f54eb8de3dcbc5e179704184c44f Mon Sep 17 00:00:00 2001 From: aiongg Date: Mon, 26 Oct 2026 18:18:25 +0000 Subject: [PATCH] refactor(engine): move links.rs's tests to links/tests.rs --- crates/engine/src/links.rs | 330 +------------------------------ crates/engine/src/links/tests.rs | 327 ++++++++++++++++++++++++++++++ 2 files changed, 328 insertions(+), 329 deletions(-) create mode 100644 crates/engine/src/links/tests.rs diff --git a/crates/engine/src/links.rs b/crates/engine/src/links.rs index 9b1c49729..e1d54d56c 100644 --- a/crates/engine/src/links.rs +++ b/crates/engine/src/links.rs @@ -446,332 +446,4 @@ fn mixes_scripts(label: &str) -> bool { } #[cfg(test)] -mod tests { - #[test] - fn remote_documents_must_be_regular_pdf_files() { - use super::{RemoteDocumentPathError as E, read_remote_document as read}; - let dir = std::env::temp_dir().join(format!("pdfcraft-gotor-read-{}", std::process::id())); - let _ = std::fs::remove_dir_all(&dir); - std::fs::create_dir_all(dir.join("folder.pdf")).unwrap(); - std::fs::write(dir.join("doc.PDF"), b"%PDF-1.7\n").unwrap(); - std::fs::write(dir.join("notes.txt"), b"text").unwrap(); - assert_eq!(read(&dir.join("doc.PDF")).as_deref(), Ok(&b"%PDF-1.7\n"[..])); - assert_eq!(read(&dir.join("folder.pdf")), Err(E::NotAFile), "a directory is refused"); - assert_eq!(read(&dir.join("notes.txt")), Err(E::NotPdf)); - assert_eq!(read(&dir), Err(E::NotPdf)); - #[cfg(unix)] - { - // Sparse: the size guard must reject this without allocating or reading a GiB. - let large = dir.join("large.pdf"); - std::fs::File::create(&large).unwrap().set_len(super::MAX_REMOTE_DOCUMENT_BYTES + 1).unwrap(); - assert_eq!(read(&large), Err(E::TooLarge)); - } - assert!(matches!(read(&dir.join("missing.pdf")), Err(E::Read(_)))); - #[cfg(unix)] - { - // A device (like /dev/zero, which never ends) and a FIFO (which blocks) are never opened. - std::os::unix::fs::symlink("/dev/zero", dir.join("zero.pdf")).unwrap(); - assert_eq!(read(&dir.join("zero.pdf")), Err(E::NotAFile)); - let fifo = dir.join("pipe.pdf"); - if std::process::Command::new("mkfifo").arg(&fifo).status().is_ok_and(|s| s.success()) { - assert_eq!(read(&fifo), Err(E::NotAFile)); - } - } - let _ = std::fs::remove_dir_all(&dir); - } - - #[test] - fn remote_document_absolute_classification_uses_the_original_platform_path() { - use super::remote_document_path_is_absolute as absolute; - for relative in ["target.pdf", "../appendix/target.pdf", "folder/target.pdf"] { - assert!(!absolute(relative), "{relative}"); - } - #[cfg(unix)] - { - assert!(absolute("/manual/target.pdf")); - assert!(!absolute(r"C:\manual\target.pdf")); - } - #[cfg(windows)] - { - assert!(absolute(r"C:\manual\target.pdf")); - assert!(absolute("C:/manual/target.pdf")); - assert!(!absolute(r"C:target.pdf"), "drive-relative is not absolute"); - assert!(!absolute(r"\manual\target.pdf"), "rooted without a drive is not absolute"); - } - } - - #[test] - fn remote_document_paths_are_local_and_source_relative() { - use super::{RemoteDocumentPathError as E, resolve_remote_document_path as resolve}; - use std::path::Path; - - let source = std::env::temp_dir().join("chapter").join("source.pdf"); - let source = source.as_path(); - let folder = source.parent().unwrap(); - assert_eq!(resolve(Some(source), "target.pdf"), Ok(folder.join("target.pdf"))); - assert_eq!(resolve(Some(source), "../target.pdf").as_deref(), Ok(folder.parent().unwrap().join("target.pdf").as_path())); - #[cfg(unix)] - assert_eq!(resolve(Some(source), "/manual/target.pdf").as_deref(), Ok(Path::new("/manual/target.pdf"))); - assert_eq!(resolve(None, "target.pdf"), Err(E::MissingSourcePath)); - assert_eq!(resolve(Some(Path::new("source.pdf")), "target.pdf"), Err(E::MissingSourcePath), "a relative source is not used"); - #[cfg(unix)] - assert_eq!(resolve(Some(source), "C:\\x.pdf"), Err(E::NotLocal(craft_docpath::Refused::Invalid)), "a Windows path on Unix needs no source"); - assert_eq!(resolve(Some(source), ""), Err(E::Empty)); - assert_eq!(resolve(Some(source), " "), Err(E::Empty)); - for url in - ["file:///tmp/a.pdf", "file://h/s/a.pdf", "https://example.org/a.pdf", "smb://host/share/a.pdf", "ftp://host/a.pdf", "custom:target.pdf"] - { - assert!(matches!(resolve(Some(source), url), Err(E::NotLocal(_))), "{url}"); - } - assert_eq!(resolve(Some(source), "folder/hidden\u{200B}.pdf"), Err(E::Hidden)); - #[cfg(windows)] - assert_eq!(resolve(Some(source), "folder/a:b.pdf"), Err(E::NotLocal(craft_docpath::Refused::Invalid))); - assert_eq!(resolve(Some(source), &"a".repeat(super::MAX_REMOTE_DOCUMENT_PATH_BYTES + 1)), Err(E::TooLong)); - } - - #[test] - fn remote_document_paths_to_other_computers_and_devices_are_refused() { - use super::{RemoteDocumentPathError as E, resolve_remote_document_path as resolve}; - - let source = std::env::temp_dir().join("chapter").join("source.pdf"); - let source = source.as_path(); - // Refused before any filesystem access: none of these exist, and a stat of a network path - // would contact its host. - for not_local in [ - "//server/share/a.pdf", - "\\\\server\\share\\a.pdf", - "\\\\h\\s\\a.pdf", - "\\\\?\\UNC\\h\\s\\a.pdf", - "\\??\\UNC\\h\\s\\a.pdf", - "/??/x", - "\\??\\C:\\a.pdf", - "\\\\.\\device\\a.pdf", - "/\\server/share/a.pdf", - "NUL.pdf", - ] { - assert!(matches!(resolve(Some(source), not_local), Err(E::NotLocal(_))), "{not_local:?}"); - } - // Linux reads `//tmp/x` as `/tmp/x`, but on Windows it names a share. - #[cfg(unix)] - { - let doubled = format!("/{}", source.with_file_name("a.pdf").display()); - assert!(matches!(resolve(Some(source), &doubled), Err(E::NotLocal(_))), "{doubled}"); - } - } - - #[test] - fn urls_follow_the_artcraft_scheme() { - assert_eq!(super::APP_PAGE, format!("{}/apps/{}", super::WEBSITE, super::APP)); - assert_eq!(super::GITHUB, format!("https://github.com/storytold/{}", super::APP)); - for l in super::LINKS { - assert!(l.url.starts_with("https://"), "{}", l.url); - assert!(crate::commands::command(l.command).is_some(), "{} is a registered command", l.command); - } - } - - #[test] - fn documents_may_offer_web_and_email_addresses() { - use super::document_url; - for ok in [ - "https://example.org", - "HTTPS://Example.org/a?b=c#d", - "http://example.org:8080/x", - "mailto:someone@example.org", - "https://[::1]:8443/", - // Plain spaces turn up in real documents' links and are visible in the dialog. - "mailto:orders@example.org?subject=Order form", - "https://example.org/My Report.pdf", - ] { - assert_eq!(document_url(ok).as_deref(), Ok(ok), "{ok}"); - } - let multibyte = format!("https://example.org/{}", "é".repeat(super::MAX_DOCUMENT_URL - 20)); - assert!(document_url(&multibyte).is_ok(), "the limit counts characters, not bytes"); - assert_eq!(document_url(" https://example.org/ \n").as_deref(), Ok("https://example.org/"), "surrounding whitespace is trimmed"); - } - - #[test] - fn documents_may_not_offer_other_kinds_of_address() { - use super::{BlockedLink as B, MAX_DOCUMENT_URL, document_url}; - let scheme = |s: &str| Err(B::Scheme(s.into())); - assert_eq!(document_url("file:///C:/Windows/System32/calc.exe"), scheme("file")); - assert_eq!(document_url("FILE://server/share/x.exe"), scheme("file")); - assert_eq!(document_url("javascript:alert(1)"), scheme("javascript")); - assert_eq!(document_url("data:text/html,"), scheme("data")); - assert_eq!(document_url("ms-settings:privacy"), scheme("ms-settings")); - assert_eq!(document_url("smb://server/share"), scheme("smb")); - assert_eq!(document_url(""), Err(B::Empty)); - assert_eq!(document_url(" "), Err(B::Empty)); - for bad in [ - "example.org", - "//example.org", - "1http://x", - "ht tp://x", - "https:", - "https://", - "https:///path", - "https://user@/x", - "mailto:", - "https://trusted.example /x", - ] { - assert_eq!(document_url(bad), Err(B::Malformed), "{bad}"); - } - for hidden in [ - "https://exa\u{202E}gro.elpmaxe", - "https://example.org\u{200B}.evil", - "https://a.org/\u{0007}", - "https://a.org/x\ty", - "https://a.org/x\ny", - "https://example.org/a\u{00A0}b", - ] { - assert_eq!(document_url(hidden), Err(B::Hidden), "{hidden:?}"); - } - let long = format!("https://example.org/?q={}", "a".repeat(MAX_DOCUMENT_URL)); - assert!(matches!(document_url(&long), Err(B::TooLong(_)))); - } - - #[test] - fn the_host_shown_is_the_one_the_address_goes_to() { - use super::host; - assert_eq!(host("https://example.org/path"), Some("example.org")); - assert_eq!(host("https://trusted.example@other.example/"), Some("other.example")); - assert_eq!(host("http://user:pw@other.example:8080?x"), Some("other.example")); - assert_eq!(host("https://[::1]:8443/"), Some("[::1]")); - // Browsers read `\` as `/`, so this goes to other.example, not trusted.example. - assert_eq!(host("https://other.example\\@trusted.example/"), Some("other.example")); - assert_eq!(host("https://other.example\\x@trusted.example/"), Some("other.example")); - assert_eq!(host("mailto:a@example.org"), None); - assert_eq!(host("https:///nohost"), None); - } - - #[test] - fn addresses_the_url_parser_rejects_are_refused() { - use super::{BlockedLink as B, document_url}; - // The webbrowser crate would open each of these as a local file path. - for bad in ["mailto://a^b/x", "mailto://a:99999/", "mailto://[x]/", "mailto://a\\b/", "mailto://a b/", "https://a^b/", "http://ax"), scheme("data")); + assert_eq!(document_url("ms-settings:privacy"), scheme("ms-settings")); + assert_eq!(document_url("smb://server/share"), scheme("smb")); + assert_eq!(document_url(""), Err(B::Empty)); + assert_eq!(document_url(" "), Err(B::Empty)); + for bad in [ + "example.org", + "//example.org", + "1http://x", + "ht tp://x", + "https:", + "https://", + "https:///path", + "https://user@/x", + "mailto:", + "https://trusted.example /x", + ] { + assert_eq!(document_url(bad), Err(B::Malformed), "{bad}"); + } + for hidden in [ + "https://exa\u{202E}gro.elpmaxe", + "https://example.org\u{200B}.evil", + "https://a.org/\u{0007}", + "https://a.org/x\ty", + "https://a.org/x\ny", + "https://example.org/a\u{00A0}b", + ] { + assert_eq!(document_url(hidden), Err(B::Hidden), "{hidden:?}"); + } + let long = format!("https://example.org/?q={}", "a".repeat(MAX_DOCUMENT_URL)); + assert!(matches!(document_url(&long), Err(B::TooLong(_)))); +} + +#[test] +fn the_host_shown_is_the_one_the_address_goes_to() { + use super::host; + assert_eq!(host("https://example.org/path"), Some("example.org")); + assert_eq!(host("https://trusted.example@other.example/"), Some("other.example")); + assert_eq!(host("http://user:pw@other.example:8080?x"), Some("other.example")); + assert_eq!(host("https://[::1]:8443/"), Some("[::1]")); + // Browsers read `\` as `/`, so this goes to other.example, not trusted.example. + assert_eq!(host("https://other.example\\@trusted.example/"), Some("other.example")); + assert_eq!(host("https://other.example\\x@trusted.example/"), Some("other.example")); + assert_eq!(host("mailto:a@example.org"), None); + assert_eq!(host("https:///nohost"), None); +} + +#[test] +fn addresses_the_url_parser_rejects_are_refused() { + use super::{BlockedLink as B, document_url}; + // The webbrowser crate would open each of these as a local file path. + for bad in ["mailto://a^b/x", "mailto://a:99999/", "mailto://[x]/", "mailto://a\\b/", "mailto://a b/", "https://a^b/", "http://a