From 7df646afde647d7841ea5584d292ff827e2d0be3 Mon Sep 17 00:00:00 2001 From: Chris Guidry Date: Fri, 31 Jul 2026 13:00:49 -0400 Subject: [PATCH] Replace hand-rolled temp directories with tempfile::TempDir Five test modules each defined their own unique_temp_dir(), created a directory under std::env::temp_dir(), and never removed it: cli.rs, config_tests.rs, srd/verify/fixtures.rs, srd/fetch.rs, and play/history.rs. Every test run left its directories behind, and the pre-commit coverage hook runs the full suite on every commit. Today this exhausted the machine's /tmp inodes. tempfile::TempDir removes its directory on drop, so each guard now lives as long as the test that needs it and cleans up automatically, including on panic. srd/verify/fixtures.rs keeps a thin unique_temp_dir() helper for the many tests that share its shape, but it now returns a TempDir guard instead of a bare path. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CUXjWo1zGFhdJUig1hcQGf --- Cargo.lock | 20 ++++++ Cargo.toml | 1 + src/cli.rs | 60 +++++++++--------- src/config_tests.rs | 34 ++++------ src/play/history.rs | 50 +++++++-------- src/srd/fetch.rs | 110 +++++++++++++++++++-------------- src/srd/verify/coverage.rs | 41 +++++++----- src/srd/verify/fidelity.rs | 26 ++++---- src/srd/verify/fixtures.rs | 27 +++----- src/srd/verify/integration.rs | 4 +- src/srd/verify/layer.rs | 38 ++++++------ src/srd/verify/mod.rs | 46 +++++++------- src/srd/verify/readme_meta.rs | 74 +++++++++++----------- src/srd/verify/schema_tests.rs | 102 +++++++++++++++--------------- 14 files changed, 331 insertions(+), 302 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 046b629..b536711 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -561,6 +561,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + [[package]] name = "filedescriptor" version = "0.8.3" @@ -1714,6 +1720,7 @@ dependencies = [ "serde_yaml_ng", "sha2 0.11.0", "tar", + "tempfile", "toml", "ureq", ] @@ -1794,6 +1801,19 @@ dependencies = [ "libc", ] +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "termina" version = "0.3.3" diff --git a/Cargo.toml b/Cargo.toml index e85a5be..d903206 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ ureq = "3.3.0" [dev-dependencies] assert_cmd = "2.2.2" +tempfile = "3.27.0" [lints.rust] # cargo-llvm-cov sets cfg(coverage). The play module uses it to leave the diff --git a/src/cli.rs b/src/cli.rs index 6844e29..55b7afa 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -171,22 +171,8 @@ mod tests { use std::io::{Read, Write}; use std::net::TcpListener; use std::path::PathBuf; - use std::sync::atomic::{AtomicU64, Ordering}; use std::thread::JoinHandle; - use std::time::{SystemTime, UNIX_EPOCH}; - - static TEMP_DIR_COUNTER: AtomicU64 = AtomicU64::new(0); - - fn unique_temp_dir() -> PathBuf { - let id = TEMP_DIR_COUNTER.fetch_add(1, Ordering::Relaxed); - let nanos = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_nanos(); - let dir = std::env::temp_dir().join(format!("storied-cli-test-{nanos}-{id}")); - fs::create_dir_all(&dir).unwrap(); - dir - } + use tempfile::TempDir; fn fetch_cli() -> Cli { Cli { @@ -325,21 +311,26 @@ mod tests { text: never_dialed_text_config(), meta: fixture_meta(), }; + let layer_dir = TempDir::new().unwrap(); - let result = run(Cli { command: None }, &sources, &unique_temp_dir()); + let result = run(Cli { command: None }, &sources, layer_dir.path()); assert_eq!(result, Ok(())); } #[test] fn run_srd_fetch_reports_an_already_present_file() { + let pdf_dir = TempDir::new().unwrap(); + let text_dir = TempDir::new().unwrap(); + let meta_dir = TempDir::new().unwrap(); + let layer_dir = TempDir::new().unwrap(); let pdf_body = b"already present".to_vec(); let pdf_expected_sha256 = sha256_hex(&pdf_body); - let pdf_destination = unique_temp_dir().join("fixture.pdf"); + let pdf_destination = pdf_dir.path().join("fixture.pdf"); fs::write(&pdf_destination, &pdf_body).unwrap(); - let text_destination = unique_temp_dir().join("dnd.srd.5.2.1"); + let text_destination = text_dir.path().join("dnd.srd.5.2.1"); fs::create_dir_all(&text_destination).unwrap(); - let meta_path = unique_temp_dir().join("meta.yaml"); + let meta_path = meta_dir.path().join("meta.yaml"); fs::write( &meta_path, "sources:\n - path: sources/dnd.srd.5.2.1/\n commit: deadbeef\n", @@ -361,7 +352,7 @@ mod tests { meta: fixture_meta(), }; - let result = run(fetch_cli(), &sources, &unique_temp_dir()); + let result = run(fetch_cli(), &sources, layer_dir.path()); assert_eq!(result, Ok(())); } @@ -374,23 +365,27 @@ mod tests { let text_body = tiny_tar_gz("dnd.srd.5.2.1-deadbeef"); let text_expected_sha256 = sha256_hex(&text_body); let (text_url, text_server) = serve_once(text_body); + let pdf_dir = TempDir::new().unwrap(); + let text_dir = TempDir::new().unwrap(); + let meta_dir = TempDir::new().unwrap(); + let layer_dir = TempDir::new().unwrap(); let sources = SrdSources { pdf: FetchConfig { url: pdf_url, - destination: unique_temp_dir().join("fixture.pdf"), + destination: pdf_dir.path().join("fixture.pdf"), expected_sha256: pdf_expected_sha256, }, text: TextFetchConfig { url: text_url, - destination: unique_temp_dir().join("dnd.srd.5.2.1"), + destination: text_dir.path().join("dnd.srd.5.2.1"), expected_tarball_sha256: text_expected_sha256, commit: "deadbeef".to_string(), - meta_path: unique_temp_dir().join("meta.yaml"), + meta_path: meta_dir.path().join("meta.yaml"), }, meta: fixture_meta(), }; - let result = run(fetch_cli(), &sources, &unique_temp_dir()); + let result = run(fetch_cli(), &sources, layer_dir.path()); assert_eq!(result, Ok(())); pdf_server.join().unwrap(); @@ -402,17 +397,19 @@ mod tests { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = listener.local_addr().unwrap(); drop(listener); + let pdf_dir = TempDir::new().unwrap(); + let layer_dir = TempDir::new().unwrap(); let sources = SrdSources { pdf: FetchConfig { url: format!("http://{addr}/fixture.pdf"), - destination: unique_temp_dir().join("fixture.pdf"), + destination: pdf_dir.path().join("fixture.pdf"), expected_sha256: "0".repeat(64), }, text: never_dialed_text_config(), meta: fixture_meta(), }; - let result = run(fetch_cli(), &sources, &unique_temp_dir()); + let result = run(fetch_cli(), &sources, layer_dir.path()); assert!(result.is_err()); } @@ -428,22 +425,27 @@ mod tests { text: never_dialed_text_config(), meta: fixture_meta(), }; + let layer_dir = TempDir::new().unwrap(); - let result = run(verify_cli(), &sources, &unique_temp_dir()); + let result = run(verify_cli(), &sources, layer_dir.path()); assert!(result.is_err()); } #[test] fn run_roll_prints_the_story_for_valid_notation() { - let result = run(roll_cli("4d6kh3"), &unused_sources(), &unique_temp_dir()); + let layer_dir = TempDir::new().unwrap(); + + let result = run(roll_cli("4d6kh3"), &unused_sources(), layer_dir.path()); assert_eq!(result, Ok(())); } #[test] fn run_roll_reports_a_parse_error_for_invalid_notation() { - let result = run(roll_cli("banana"), &unused_sources(), &unique_temp_dir()); + let layer_dir = TempDir::new().unwrap(); + + let result = run(roll_cli("banana"), &unused_sources(), layer_dir.path()); assert!(result.is_err()); } diff --git a/src/config_tests.rs b/src/config_tests.rs index 488c56b..761361a 100644 --- a/src/config_tests.rs +++ b/src/config_tests.rs @@ -3,26 +3,12 @@ use super::*; use std::sync::Mutex; -use std::sync::atomic::{AtomicU64, Ordering}; -use std::time::{SystemTime, UNIX_EPOCH}; +use tempfile::TempDir; /// Serializes the one test that touches real process environment /// variables, since `std::env` is shared by every thread in the process. static ENV_LOCK: Mutex<()> = Mutex::new(()); -static TEMP_DIR_COUNTER: AtomicU64 = AtomicU64::new(0); - -fn unique_temp_dir() -> PathBuf { - let id = TEMP_DIR_COUNTER.fetch_add(1, Ordering::Relaxed); - let nanos = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_nanos(); - let dir = env::temp_dir().join(format!("storied-config-test-{nanos}-{id}")); - fs::create_dir_all(&dir).unwrap(); - dir -} - fn no_overrides() -> Overrides { Overrides { api_base: None, @@ -92,7 +78,8 @@ fn config_path_falls_back_to_home_config_when_xdg_is_empty() { #[test] fn read_config_file_returns_the_contents_of_an_existing_file() { - let path = unique_temp_dir().join("config.toml"); + let temp_dir = TempDir::new().unwrap(); + let path = temp_dir.path().join("config.toml"); fs::write(&path, "api_base = \"https://example.test\"\n").unwrap(); let contents = read_config_file(&path).unwrap(); @@ -102,7 +89,8 @@ fn read_config_file_returns_the_contents_of_an_existing_file() { #[test] fn read_config_file_reports_a_missing_file() { - let path = unique_temp_dir().join("does-not-exist.toml"); + let temp_dir = TempDir::new().unwrap(); + let path = temp_dir.path().join("does-not-exist.toml"); let error = read_config_file(&path).unwrap_err(); @@ -111,9 +99,9 @@ fn read_config_file_reports_a_missing_file() { #[test] fn read_config_file_reports_an_unreadable_path() { - let path = unique_temp_dir(); + let temp_dir = TempDir::new().unwrap(); - let error = read_config_file(&path).unwrap_err(); + let error = read_config_file(temp_dir.path()).unwrap_err(); assert!(matches!(error, ConfigError::Unreadable { .. })); } @@ -302,17 +290,17 @@ It comes from api_key_env in /config/storied/config.toml" #[test] fn load_reads_the_config_file_and_the_api_key_from_the_environment() { let _lock = ENV_LOCK.lock().unwrap(); - let config_dir = unique_temp_dir(); - fs::create_dir_all(config_dir.join("storied")).unwrap(); + let config_dir = TempDir::new().unwrap(); + fs::create_dir_all(config_dir.path().join("storied")).unwrap(); fs::write( - config_dir.join("storied").join("config.toml"), + config_dir.path().join("storied").join("config.toml"), "api_base = \"https://example.test\"\n\ model = \"test-model\"\n\ api_key_env = \"STORIED_CONFIG_TEST_KEY\"\n", ) .unwrap(); unsafe { - env::set_var("XDG_CONFIG_HOME", &config_dir); + env::set_var("XDG_CONFIG_HOME", config_dir.path()); env::set_var("STORIED_CONFIG_TEST_KEY", "sk-test"); } diff --git a/src/play/history.rs b/src/play/history.rs index c7ddca5..7f4d0b8 100644 --- a/src/play/history.rs +++ b/src/play/history.rs @@ -146,21 +146,7 @@ fn unescape(line: &str) -> String { #[cfg(test)] mod tests { use super::*; - use std::sync::atomic::{AtomicU64, Ordering}; - use std::time::{SystemTime, UNIX_EPOCH}; - - static TEMP_DIR_COUNTER: AtomicU64 = AtomicU64::new(0); - - fn unique_temp_dir() -> PathBuf { - let id = TEMP_DIR_COUNTER.fetch_add(1, Ordering::Relaxed); - let nanos = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_nanos(); - let dir = std::env::temp_dir().join(format!("storied-history-test-{nanos}-{id}")); - fs::create_dir_all(&dir).unwrap(); - dir - } + use tempfile::TempDir; fn filled(entries: &[&str]) -> History { let mut history = History::in_memory(); @@ -172,7 +158,8 @@ mod tests { #[test] fn load_reads_entries_from_an_existing_file() { - let path = unique_temp_dir().join("terminal_history"); + let temp_dir = TempDir::new().unwrap(); + let path = temp_dir.path().join("terminal_history"); fs::write(&path, "look\ntake sword\n").unwrap(); let history = History::load(path); @@ -182,7 +169,8 @@ mod tests { #[test] fn load_with_a_missing_file_is_empty() { - let path = unique_temp_dir().join("does-not-exist"); + let temp_dir = TempDir::new().unwrap(); + let path = temp_dir.path().join("does-not-exist"); let history = History::load(path); @@ -191,7 +179,8 @@ mod tests { #[test] fn record_appends_to_the_file() { - let path = unique_temp_dir().join("terminal_history"); + let temp_dir = TempDir::new().unwrap(); + let path = temp_dir.path().join("terminal_history"); let mut history = History::load(path.clone()); history.record("look"); @@ -201,7 +190,8 @@ mod tests { #[test] fn record_skips_a_consecutive_duplicate() { - let path = unique_temp_dir().join("terminal_history"); + let temp_dir = TempDir::new().unwrap(); + let path = temp_dir.path().join("terminal_history"); let mut history = History::load(path.clone()); history.record("look"); @@ -212,7 +202,9 @@ mod tests { #[test] fn record_creates_parent_directories() { - let path = unique_temp_dir() + let temp_dir = TempDir::new().unwrap(); + let path = temp_dir + .path() .join("worlds") .join("default") .join("terminal_history"); @@ -225,7 +217,8 @@ mod tests { #[test] fn record_ignores_a_file_that_cannot_be_opened() { - let path = unique_temp_dir().join("terminal_history"); + let temp_dir = TempDir::new().unwrap(); + let path = temp_dir.path().join("terminal_history"); fs::create_dir_all(&path).unwrap(); let mut history = History::load(path); @@ -288,7 +281,8 @@ mod tests { #[test] fn a_multiline_entry_stays_one_line_in_the_file() { - let path = unique_temp_dir().join("terminal_history"); + let temp_dir = TempDir::new().unwrap(); + let path = temp_dir.path().join("terminal_history"); let mut history = History::load(path.clone()); history.record("look\naround"); @@ -298,7 +292,8 @@ mod tests { #[test] fn a_multiline_entry_round_trips_through_the_file() { - let path = unique_temp_dir().join("terminal_history"); + let temp_dir = TempDir::new().unwrap(); + let path = temp_dir.path().join("terminal_history"); let mut history = History::load(path.clone()); history.record("look\naround"); @@ -309,7 +304,8 @@ mod tests { #[test] fn a_backslash_not_followed_by_a_known_escape_passes_through() { - let path = unique_temp_dir().join("terminal_history"); + let temp_dir = TempDir::new().unwrap(); + let path = temp_dir.path().join("terminal_history"); fs::write(&path, "a\\xb\n").unwrap(); let history = History::load(path); @@ -319,7 +315,8 @@ mod tests { #[test] fn a_trailing_backslash_with_nothing_after_it_passes_through() { - let path = unique_temp_dir().join("terminal_history"); + let temp_dir = TempDir::new().unwrap(); + let path = temp_dir.path().join("terminal_history"); fs::write(&path, "a\\\n").unwrap(); let history = History::load(path); @@ -329,7 +326,8 @@ mod tests { #[test] fn a_literal_backslash_round_trips_through_the_file() { - let path = unique_temp_dir().join("terminal_history"); + let temp_dir = TempDir::new().unwrap(); + let path = temp_dir.path().join("terminal_history"); let mut history = History::load(path.clone()); history.record(r"a\path"); diff --git a/src/srd/fetch.rs b/src/srd/fetch.rs index 17a5d82..65eae1c 100644 --- a/src/srd/fetch.rs +++ b/src/srd/fetch.rs @@ -382,22 +382,8 @@ mod tests { use super::*; use std::io::{Read, Write}; use std::net::TcpListener; - use std::sync::atomic::{AtomicU64, Ordering}; use std::thread::JoinHandle; - use std::time::{SystemTime, UNIX_EPOCH}; - - static TEMP_DIR_COUNTER: AtomicU64 = AtomicU64::new(0); - - fn unique_temp_dir() -> PathBuf { - let id = TEMP_DIR_COUNTER.fetch_add(1, Ordering::Relaxed); - let nanos = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_nanos(); - let dir = std::env::temp_dir().join(format!("storied-fetch-test-{nanos}-{id}")); - fs::create_dir_all(&dir).unwrap(); - dir - } + use tempfile::TempDir; /// Serves `body` once to the first connection it accepts, as a complete /// HTTP/1.1 response, then returns the URL to fetch it from. @@ -732,7 +718,8 @@ mod tests { let body = b"fixture pdf bytes".to_vec(); let expected = sha256_hex(&body); let (url, server) = serve_once(body.clone()); - let destination = unique_temp_dir().join("fixture.pdf"); + let pdf_dir = TempDir::new().unwrap(); + let destination = pdf_dir.path().join("fixture.pdf"); let config = FetchConfig { url, destination: destination.clone(), @@ -750,7 +737,8 @@ mod tests { fn fetch_skips_download_when_the_existing_hash_matches() { let body = b"already present".to_vec(); let expected = sha256_hex(&body); - let destination = unique_temp_dir().join("fixture.pdf"); + let pdf_dir = TempDir::new().unwrap(); + let destination = pdf_dir.path().join("fixture.pdf"); fs::write(&destination, &body).unwrap(); let never_dialed_url = "http://127.0.0.1:0".to_string(); let config = FetchConfig { @@ -769,7 +757,8 @@ mod tests { let body = b"good bytes".to_vec(); let expected = sha256_hex(&body); let (url, server) = serve_once(body.clone()); - let destination = unique_temp_dir().join("fixture.pdf"); + let pdf_dir = TempDir::new().unwrap(); + let destination = pdf_dir.path().join("fixture.pdf"); fs::write(&destination, b"stale garbage").unwrap(); let config = FetchConfig { url, @@ -788,7 +777,8 @@ mod tests { fn fetch_rejects_a_hash_mismatch_and_leaves_no_file_behind() { let body = b"wrong bytes".to_vec(); let (url, server) = serve_once(body); - let destination = unique_temp_dir().join("fixture.pdf"); + let pdf_dir = TempDir::new().unwrap(); + let destination = pdf_dir.path().join("fixture.pdf"); let config = FetchConfig { url, destination: destination.clone(), @@ -809,7 +799,8 @@ mod tests { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = listener.local_addr().unwrap(); drop(listener); - let destination = unique_temp_dir().join("fixture.pdf"); + let pdf_dir = TempDir::new().unwrap(); + let destination = pdf_dir.path().join("fixture.pdf"); let config = FetchConfig { url: format!("http://{addr}/fixture.pdf"), destination, @@ -827,8 +818,8 @@ mod tests { let body = b"any bytes".to_vec(); let expected = sha256_hex(&body); let (url, server) = serve_once(body); - let dir = unique_temp_dir(); - let blocking_file = dir.join("blocker"); + let dir = TempDir::new().unwrap(); + let blocking_file = dir.path().join("blocker"); fs::write(&blocking_file, b"not a directory").unwrap(); let destination = blocking_file.join("nested").join("fixture.pdf"); let config = FetchConfig { @@ -846,9 +837,11 @@ mod tests { #[test] fn fetch_text_skips_download_when_the_recorded_pin_matches() { - let destination = unique_temp_dir().join("dnd.srd.5.2.1"); + let destination_dir = TempDir::new().unwrap(); + let destination = destination_dir.path().join("dnd.srd.5.2.1"); fs::create_dir_all(&destination).unwrap(); - let meta_path = unique_temp_dir().join("meta.yaml"); + let meta_dir = TempDir::new().unwrap(); + let meta_path = meta_dir.path().join("meta.yaml"); fs::write( &meta_path, "sources:\n - path: sources/dnd.srd.5.2.1/\n commit: deadbeef\n", @@ -872,8 +865,10 @@ mod tests { let body = tiny_tar_gz("dnd.srd.5.2.1-deadbeef"); let expected = sha256_hex(&body); let (url, server) = serve_once(body); - let destination = unique_temp_dir().join("dnd.srd.5.2.1"); - let meta_path = unique_temp_dir().join("meta.yaml"); + let destination_dir = TempDir::new().unwrap(); + let destination = destination_dir.path().join("dnd.srd.5.2.1"); + let meta_dir = TempDir::new().unwrap(); + let meta_path = meta_dir.path().join("meta.yaml"); let config = TextFetchConfig { url, destination: destination.clone(), @@ -906,10 +901,12 @@ mod tests { let body = tiny_tar_gz("dnd.srd.5.2.1-deadbeef"); let expected = sha256_hex(&body); let (url, server) = serve_once(body); - let destination = unique_temp_dir().join("dnd.srd.5.2.1"); + let destination_dir = TempDir::new().unwrap(); + let destination = destination_dir.path().join("dnd.srd.5.2.1"); fs::create_dir_all(&destination).unwrap(); fs::write(destination.join("stale.md"), b"stale").unwrap(); - let meta_path = unique_temp_dir().join("meta.yaml"); + let meta_dir = TempDir::new().unwrap(); + let meta_path = meta_dir.path().join("meta.yaml"); fs::write( &meta_path, "sources:\n - path: sources/dnd.srd.5.2.1/\n commit: old-sha\n", @@ -941,14 +938,16 @@ mod tests { let body = tiny_tar_gz("dnd.srd.5.2.1-deadbeef"); let expected = sha256_hex(&body); let (url, server) = serve_once(body); - let destination = unique_temp_dir().join("dnd.srd.5.2.1"); + let destination_dir = TempDir::new().unwrap(); + let destination = destination_dir.path().join("dnd.srd.5.2.1"); fs::create_dir_all(&destination).unwrap(); + let meta_dir = TempDir::new().unwrap(); let config = TextFetchConfig { url, destination, expected_tarball_sha256: expected, commit: "deadbeef".to_string(), - meta_path: unique_temp_dir().join("meta.yaml"), + meta_path: meta_dir.path().join("meta.yaml"), }; let outcome = fetch_text(&config, &fixture_meta()).unwrap(); @@ -962,11 +961,13 @@ mod tests { let body = tiny_tar_gz("dnd.srd.5.2.1-deadbeef"); let expected = sha256_hex(&body); let (url, server) = serve_once(body); - let blocking_file = unique_temp_dir().join("blocker"); + let blocker_dir = TempDir::new().unwrap(); + let blocking_file = blocker_dir.path().join("blocker"); fs::write(&blocking_file, b"not a directory").unwrap(); + let destination_dir = TempDir::new().unwrap(); let config = TextFetchConfig { url, - destination: unique_temp_dir().join("dnd.srd.5.2.1"), + destination: destination_dir.path().join("dnd.srd.5.2.1"), expected_tarball_sha256: expected, commit: "deadbeef".to_string(), meta_path: blocking_file.join("nested").join("meta.yaml"), @@ -983,12 +984,14 @@ mod tests { let body = tar_gz_with_a_file_parent_collision("dnd.srd.5.2.1-deadbeef"); let expected = sha256_hex(&body); let (url, server) = serve_once(body); + let destination_dir = TempDir::new().unwrap(); + let meta_dir = TempDir::new().unwrap(); let config = TextFetchConfig { url, - destination: unique_temp_dir().join("dnd.srd.5.2.1"), + destination: destination_dir.path().join("dnd.srd.5.2.1"), expected_tarball_sha256: expected, commit: "deadbeef".to_string(), - meta_path: unique_temp_dir().join("meta.yaml"), + meta_path: meta_dir.path().join("meta.yaml"), }; let error = fetch_text(&config, &fixture_meta()).unwrap_err(); @@ -1002,19 +1005,21 @@ mod tests { let body = tar_gz_with_a_parent_dir_traversal("dnd.srd.5.2.1-deadbeef"); let expected = sha256_hex(&body); let (url, server) = serve_once(body); - let destination = unique_temp_dir().join("dnd.srd.5.2.1"); + let destination_dir = TempDir::new().unwrap(); + let destination = destination_dir.path().join("dnd.srd.5.2.1"); let escape_target = destination .parent() .unwrap() .parent() .unwrap() .join("escape.txt"); + let meta_dir = TempDir::new().unwrap(); let config = TextFetchConfig { url, destination: destination.clone(), expected_tarball_sha256: expected, commit: "deadbeef".to_string(), - meta_path: unique_temp_dir().join("meta.yaml"), + meta_path: meta_dir.path().join("meta.yaml"), }; let error = fetch_text(&config, &fixture_meta()).unwrap_err(); @@ -1030,12 +1035,14 @@ mod tests { let body = tar_gz_with_an_absolute_path_entry("dnd.srd.5.2.1-deadbeef"); let expected = sha256_hex(&body); let (url, server) = serve_once(body); + let destination_dir = TempDir::new().unwrap(); + let meta_dir = TempDir::new().unwrap(); let config = TextFetchConfig { url, - destination: unique_temp_dir().join("dnd.srd.5.2.1"), + destination: destination_dir.path().join("dnd.srd.5.2.1"), expected_tarball_sha256: expected, commit: "deadbeef".to_string(), - meta_path: unique_temp_dir().join("meta.yaml"), + meta_path: meta_dir.path().join("meta.yaml"), }; let error = fetch_text(&config, &fixture_meta()).unwrap_err(); @@ -1050,12 +1057,14 @@ mod tests { let body = tar_gz_with_a_symlink_escape("dnd.srd.5.2.1-deadbeef"); let expected = sha256_hex(&body); let (url, server) = serve_once(body); + let destination_dir = TempDir::new().unwrap(); + let meta_dir = TempDir::new().unwrap(); let config = TextFetchConfig { url, - destination: unique_temp_dir().join("dnd.srd.5.2.1"), + destination: destination_dir.path().join("dnd.srd.5.2.1"), expected_tarball_sha256: expected, commit: "deadbeef".to_string(), - meta_path: unique_temp_dir().join("meta.yaml"), + meta_path: meta_dir.path().join("meta.yaml"), }; let error = fetch_text(&config, &fixture_meta()).unwrap_err(); @@ -1071,13 +1080,15 @@ mod tests { fn fetch_text_rejects_a_tarball_hash_mismatch() { let body = tiny_tar_gz("dnd.srd.5.2.1-deadbeef"); let (url, server) = serve_once(body); - let destination = unique_temp_dir().join("dnd.srd.5.2.1"); + let destination_dir = TempDir::new().unwrap(); + let destination = destination_dir.path().join("dnd.srd.5.2.1"); + let meta_dir = TempDir::new().unwrap(); let config = TextFetchConfig { url, destination: destination.clone(), expected_tarball_sha256: "0".repeat(64), commit: "deadbeef".to_string(), - meta_path: unique_temp_dir().join("meta.yaml"), + meta_path: meta_dir.path().join("meta.yaml"), }; let error = fetch_text(&config, &fixture_meta()).unwrap_err(); @@ -1092,12 +1103,14 @@ mod tests { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = listener.local_addr().unwrap(); drop(listener); + let destination_dir = TempDir::new().unwrap(); + let meta_dir = TempDir::new().unwrap(); let config = TextFetchConfig { url: format!("http://{addr}/dnd.srd.5.2.1.tar.gz"), - destination: unique_temp_dir().join("dnd.srd.5.2.1"), + destination: destination_dir.path().join("dnd.srd.5.2.1"), expected_tarball_sha256: "0".repeat(64), commit: "deadbeef".to_string(), - meta_path: unique_temp_dir().join("meta.yaml"), + meta_path: meta_dir.path().join("meta.yaml"), }; let error = fetch_text(&config, &fixture_meta()).unwrap_err(); @@ -1113,18 +1126,21 @@ mod tests { let text_body = tiny_tar_gz("dnd.srd.5.2.1-deadbeef"); let text_expected = sha256_hex(&text_body); let (text_url, text_server) = serve_once(text_body); + let pdf_dir = TempDir::new().unwrap(); + let destination_dir = TempDir::new().unwrap(); + let meta_dir = TempDir::new().unwrap(); let sources = SrdSources { pdf: FetchConfig { url: pdf_url, - destination: unique_temp_dir().join("fixture.pdf"), + destination: pdf_dir.path().join("fixture.pdf"), expected_sha256: pdf_expected, }, text: TextFetchConfig { url: text_url, - destination: unique_temp_dir().join("dnd.srd.5.2.1"), + destination: destination_dir.path().join("dnd.srd.5.2.1"), expected_tarball_sha256: text_expected, commit: "deadbeef".to_string(), - meta_path: unique_temp_dir().join("meta.yaml"), + meta_path: meta_dir.path().join("meta.yaml"), }, meta: fixture_meta(), }; diff --git a/src/srd/verify/coverage.rs b/src/srd/verify/coverage.rs index f988022..1f8ddc2 100644 --- a/src/srd/verify/coverage.rs +++ b/src/srd/verify/coverage.rs @@ -151,9 +151,9 @@ mod tests { #[test] fn check_claims_passes_when_every_each_file_is_claimed_once() { let layer_root = unique_temp_dir(); - let source = each_file(&layer_root, "07_Spells/Spells_Each/Fireball.md"); + let source = each_file(layer_root.path(), "07_Spells/Spells_Each/Fireball.md"); - let failures = check_claims(&layer_root, &[source]); + let failures = check_claims(layer_root.path(), &[source]); assert_eq!(failures, vec![]); } @@ -161,9 +161,9 @@ mod tests { #[test] fn check_claims_reports_an_unclaimed_each_file() { let layer_root = unique_temp_dir(); - each_file(&layer_root, "07_Spells/Spells_Each/Fireball.md"); + each_file(layer_root.path(), "07_Spells/Spells_Each/Fireball.md"); - let failures = check_claims(&layer_root, &[]); + let failures = check_claims(layer_root.path(), &[]); assert_eq!(failures.len(), 1); assert!(failures[0].message.contains("not claimed")); @@ -172,9 +172,9 @@ mod tests { #[test] fn check_claims_reports_a_double_claimed_each_file() { let layer_root = unique_temp_dir(); - let source = each_file(&layer_root, "07_Spells/Spells_Each/Fireball.md"); + let source = each_file(layer_root.path(), "07_Spells/Spells_Each/Fireball.md"); - let failures = check_claims(&layer_root, &[source.clone(), source]); + let failures = check_claims(layer_root.path(), &[source.clone(), source]); assert_eq!(failures.len(), 1); assert!(failures[0].message.contains("2 corpus entries")); @@ -184,10 +184,10 @@ mod tests { #[test] fn check_claims_sorts_multiple_failures_by_path() { let layer_root = unique_temp_dir(); - each_file(&layer_root, "07_Spells/Spells_Each/Wish.md"); - each_file(&layer_root, "07_Spells/Spells_Each/Fireball.md"); + each_file(layer_root.path(), "07_Spells/Spells_Each/Wish.md"); + each_file(layer_root.path(), "07_Spells/Spells_Each/Fireball.md"); - let failures = check_claims(&layer_root, &[]); + let failures = check_claims(layer_root.path(), &[]); let paths: Vec<_> = failures.iter().map(|f| f.path.clone()).collect(); let mut sorted_paths = paths.clone(); @@ -200,7 +200,7 @@ mod tests { fn check_claims_tolerates_a_missing_vendored_tree() { let layer_root = unique_temp_dir(); - let failures = check_claims(&layer_root, &[]); + let failures = check_claims(layer_root.path(), &[]); assert_eq!(failures, vec![]); } @@ -208,9 +208,12 @@ mod tests { #[test] fn check_claims_allows_an_unclaimed_non_each_file() { let layer_root = unique_temp_dir(); - write_file(&layer_root.join(VENDORED_ROOT).join("Legal.md"), "content"); + write_file( + &layer_root.path().join(VENDORED_ROOT).join("Legal.md"), + "content", + ); - let failures = check_claims(&layer_root, &[]); + let failures = check_claims(layer_root.path(), &[]); assert_eq!(failures, vec![]); } @@ -218,10 +221,13 @@ mod tests { #[test] fn check_claims_allows_a_non_each_file_claimed_once() { let layer_root = unique_temp_dir(); - write_file(&layer_root.join(VENDORED_ROOT).join("Legal.md"), "content"); + write_file( + &layer_root.path().join(VENDORED_ROOT).join("Legal.md"), + "content", + ); let source = PathBuf::from(VENDORED_ROOT).join("Legal.md"); - let failures = check_claims(&layer_root, &[source]); + let failures = check_claims(layer_root.path(), &[source]); assert_eq!(failures, vec![]); } @@ -229,10 +235,13 @@ mod tests { #[test] fn check_claims_rejects_a_non_each_file_claimed_twice() { let layer_root = unique_temp_dir(); - write_file(&layer_root.join(VENDORED_ROOT).join("Legal.md"), "content"); + write_file( + &layer_root.path().join(VENDORED_ROOT).join("Legal.md"), + "content", + ); let source = PathBuf::from(VENDORED_ROOT).join("Legal.md"); - let failures = check_claims(&layer_root, &[source.clone(), source]); + let failures = check_claims(layer_root.path(), &[source.clone(), source]); assert_eq!(failures.len(), 1); assert!(failures[0].message.contains("at most one")); diff --git a/src/srd/verify/fidelity.rs b/src/srd/verify/fidelity.rs index 5db3b45..629f2fd 100644 --- a/src/srd/verify/fidelity.rs +++ b/src/srd/verify/fidelity.rs @@ -129,7 +129,7 @@ mod tests { let layer_root = unique_temp_dir(); let source = PathBuf::from("sources/dnd.srd.5.2.1/01_Playing_The_Game/Playing_The_Game.md"); write_file( - &layer_root.join(&source), + &layer_root.path().join(&source), "# Playing the Game\n\nRules text.\n", ); let file = corpus_file(Kind::Core, "# Playing the Game\n\nRules text.\n"); @@ -138,7 +138,7 @@ mod tests { check( &file, &source, - &layer_root, + layer_root.path(), &KindFields::Other, &mut failures, ); @@ -151,7 +151,7 @@ mod tests { let layer_root = unique_temp_dir(); let source = PathBuf::from("sources/dnd.srd.5.2.1/01_Playing_The_Game/Playing_The_Game.md"); write_file( - &layer_root.join(&source), + &layer_root.path().join(&source), "# Playing the Game\n\nRules text extra here.\n", ); let file = corpus_file(Kind::Core, "# Playing the Game\n\nRules text here.\n"); @@ -160,7 +160,7 @@ mod tests { check( &file, &source, - &layer_root, + layer_root.path(), &KindFields::Other, &mut failures, ); @@ -177,14 +177,14 @@ mod tests { // regardless of what the source looks like. let layer_root = unique_temp_dir(); let source = PathBuf::from("sources/dnd.srd.5.2.1/01_Playing_The_Game/Playing_The_Game.md"); - write_file(&layer_root.join(&source), " \n\n \n"); + write_file(&layer_root.path().join(&source), " \n\n \n"); let file = corpus_file(Kind::Core, " \n\n"); let mut failures = Vec::new(); check( &file, &source, - &layer_root, + layer_root.path(), &KindFields::Other, &mut failures, ); @@ -203,7 +203,7 @@ mod tests { check( &file, &source, - &layer_root, + layer_root.path(), &KindFields::Other, &mut failures, ); @@ -341,7 +341,7 @@ mod tests { let layer_root = unique_temp_dir(); let source = PathBuf::from("sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md"); let source_text = "# Fireball\n\n**Casting Time:** Action\n"; - write_file(&layer_root.join(&source), source_text); + write_file(&layer_root.path().join(&source), source_text); let file = corpus_file(Kind::Spell, source_text); let fields = KindFields::Spell(SpellFields { level: 3, @@ -354,7 +354,7 @@ mod tests { }); let mut failures = Vec::new(); - check(&file, &source, &layer_root, &fields, &mut failures); + check(&file, &source, layer_root.path(), &fields, &mut failures); assert!(failures.iter().any(|f| f.message.contains("Bonus Action"))); } @@ -365,7 +365,7 @@ mod tests { let source = PathBuf::from("sources/dnd.srd.5.2.1/10_Magic_Items/Magic_Items_Each/Amulet.md"); let source_text = "# Amulet\n\n*Wondrous Item, Rare*\n"; - write_file(&layer_root.join(&source), source_text); + write_file(&layer_root.path().join(&source), source_text); let body = "# Amulet\n\n*Wondrous Item, Rare*\n\n**Category:** Wondrous Item\n\n**Rarity:** Rare\n\n**Attunement:** None\n"; let file = corpus_file(Kind::MagicItem, body); let fields = KindFields::MagicItem(MagicItemFields { @@ -376,7 +376,7 @@ mod tests { }); let mut failures = Vec::new(); - check(&file, &source, &layer_root, &fields, &mut failures); + check(&file, &source, layer_root.path(), &fields, &mut failures); assert_eq!(failures, vec![]); } @@ -386,7 +386,7 @@ mod tests { let layer_root = unique_temp_dir(); let source = PathBuf::from("sources/dnd.srd.5.2.1/11_Monsters/Monsters_Each/Rat.md"); let source_text = "# Rat\n\n*Small Beast, Unaligned*\n\n**AC** 10\n\n**HP** 1 (1d4)\n\n**Speed** 5 ft.\n\n| | MOD | SAVE | | MOD | SAVE | | MOD | SAVE |\n| :- | :- | :- | :- | :- | :- | :- | :- | :- |\n| **Str 1** | +0 | +0 | **Dex 1** | +0 | +0 | **Con 1** | +0 | +0 |\n| **Int 1** | +0 | +0 | **Wis 1** | +0 | +0 | **Cha 1** | +0 | +0 |\n\n**CR** 0\n\n## Actions\n\nBite.\n"; - write_file(&layer_root.join(&source), source_text); + write_file(&layer_root.path().join(&source), source_text); let body = stat_block::source_outside_region(source_text); let file = corpus_file(Kind::Monster, &body); fn ability() -> crate::srd::verify::monster::Ability { @@ -422,7 +422,7 @@ mod tests { })); let mut failures = Vec::new(); - check(&file, &source, &layer_root, &fields, &mut failures); + check(&file, &source, layer_root.path(), &fields, &mut failures); assert_eq!(failures, vec![]); } diff --git a/src/srd/verify/fixtures.rs b/src/srd/verify/fixtures.rs index 941804e..cc3ac85 100644 --- a/src/srd/verify/fixtures.rs +++ b/src/srd/verify/fixtures.rs @@ -4,21 +4,14 @@ use std::fs; use std::path::{Path, PathBuf}; -use std::sync::atomic::{AtomicU64, Ordering}; -use std::time::{SystemTime, UNIX_EPOCH}; - -static TEMP_DIR_COUNTER: AtomicU64 = AtomicU64::new(0); - -/// A fresh, empty directory that no other test is using. -pub fn unique_temp_dir() -> PathBuf { - let id = TEMP_DIR_COUNTER.fetch_add(1, Ordering::Relaxed); - let nanos = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_nanos(); - let dir = std::env::temp_dir().join(format!("storied-verify-test-{nanos}-{id}")); - fs::create_dir_all(&dir).unwrap(); - dir + +use tempfile::TempDir; + +/// A fresh, empty directory that no other test is using. The caller must +/// hold the returned guard for as long as the directory needs to exist; +/// it removes the directory when it drops. +pub fn unique_temp_dir() -> TempDir { + TempDir::new().expect("failed to create a temp directory") } /// Writes `contents` to `path`, creating its parent directories first. @@ -73,7 +66,7 @@ const ANIMAL_SOURCE_TEXT: &str = "# Wolf\n\n*Medium Beast, Unaligned*\n\n**AC** /// tests that care about a different, single failure should filter those /// out rather than assert an empty failure list. pub struct Layer { - pub root: PathBuf, + pub root: TempDir, } impl Layer { @@ -185,7 +178,7 @@ impl Layer { } pub fn path(&self, relative: &str) -> PathBuf { - self.root.join(relative) + self.root.path().join(relative) } pub fn write(&self, relative: &str, contents: &str) { diff --git a/src/srd/verify/integration.rs b/src/srd/verify/integration.rs index 6044115..2858156 100644 --- a/src/srd/verify/integration.rs +++ b/src/srd/verify/integration.rs @@ -14,7 +14,7 @@ use super::fixtures::{FIREBALL_SOURCE, Layer}; use super::{Failure, verify}; fn extra_failures(layer: &Layer) -> Vec { - verify(&layer.root) + verify(layer.root.path()) .failures .into_iter() .filter(|failure| { @@ -28,7 +28,7 @@ fn extra_failures(layer: &Layer) -> Vec { fn a_valid_layer_only_reports_count_floor_breaches() { let layer = Layer::valid(); - let report = verify(&layer.root); + let report = verify(layer.root.path()); assert_eq!(extra_failures(&layer), vec![]); assert_eq!(report.failures.len(), 11); diff --git a/src/srd/verify/layer.rs b/src/srd/verify/layer.rs index 80fdf6e..da9d472 100644 --- a/src/srd/verify/layer.rs +++ b/src/srd/verify/layer.rs @@ -150,7 +150,7 @@ mod tests { #[test] fn discover_finds_md_files_in_each_kind_directory() { let layer_root = unique_temp_dir(); - let spells_dir = layer_root.join("spells"); + let spells_dir = layer_root.path().join("spells"); fs::create_dir_all(&spells_dir).unwrap(); fs::write( spells_dir.join("fireball.md"), @@ -159,7 +159,7 @@ mod tests { .unwrap(); fs::write(spells_dir.join("notes.txt"), "ignored").unwrap(); - let (files, failures) = discover(&layer_root); + let (files, failures) = discover(layer_root.path()); assert_eq!(failures, vec![]); assert_eq!(files.len(), 1); @@ -169,7 +169,7 @@ mod tests { #[test] fn discover_reports_a_parse_failure_without_stopping() { let layer_root = unique_temp_dir(); - let spells_dir = layer_root.join("spells"); + let spells_dir = layer_root.path().join("spells"); fs::create_dir_all(&spells_dir).unwrap(); fs::write(spells_dir.join("broken.md"), "not frontmatter at all").unwrap(); fs::write( @@ -178,7 +178,7 @@ mod tests { ) .unwrap(); - let (files, failures) = discover(&layer_root); + let (files, failures) = discover(layer_root.path()); assert_eq!(files.len(), 1); assert_eq!(failures.len(), 1); @@ -188,13 +188,13 @@ mod tests { #[test] fn discover_reports_a_file_it_cannot_read() { let layer_root = unique_temp_dir(); - let spells_dir = layer_root.join("spells"); + let spells_dir = layer_root.path().join("spells"); fs::create_dir_all(&spells_dir).unwrap(); // Invalid UTF-8 fails `fs::read_to_string`, standing in for a // permissions problem without needing to change any file's mode. fs::write(spells_dir.join("bad-utf8.md"), [0xFF, 0xFE, 0x00]).unwrap(); - let (files, failures) = discover(&layer_root); + let (files, failures) = discover(layer_root.path()); assert!(files.is_empty()); assert_eq!(failures.len(), 1); @@ -204,7 +204,7 @@ mod tests { #[test] fn discover_reports_invalid_frontmatter_yaml() { let layer_root = unique_temp_dir(); - let spells_dir = layer_root.join("spells"); + let spells_dir = layer_root.path().join("spells"); fs::create_dir_all(&spells_dir).unwrap(); fs::write( spells_dir.join("bad-yaml.md"), @@ -212,7 +212,7 @@ mod tests { ) .unwrap(); - let (files, failures) = discover(&layer_root); + let (files, failures) = discover(layer_root.path()); assert!(files.is_empty()); assert_eq!(failures.len(), 1); @@ -222,11 +222,11 @@ mod tests { #[test] fn discover_ignores_a_non_md_file_in_a_subdirectory() { let layer_root = unique_temp_dir(); - let nested = layer_root.join("spells/extra/notes.txt"); + let nested = layer_root.path().join("spells/extra/notes.txt"); fs::create_dir_all(nested.parent().unwrap()).unwrap(); fs::write(&nested, "fine, not markdown").unwrap(); - let (files, failures) = discover(&layer_root); + let (files, failures) = discover(layer_root.path()); assert!(files.is_empty()); assert_eq!(failures, vec![]); @@ -235,11 +235,11 @@ mod tests { #[test] fn discover_reports_an_unexpected_file_in_a_subdirectory() { let layer_root = unique_temp_dir(); - let nested = layer_root.join("spells/extra/notes.md"); + let nested = layer_root.path().join("spells/extra/notes.md"); fs::create_dir_all(nested.parent().unwrap()).unwrap(); fs::write(&nested, "not a real entry").unwrap(); - let (files, failures) = discover(&layer_root); + let (files, failures) = discover(layer_root.path()); assert!(files.is_empty()); assert_eq!(failures.len(), 1); @@ -249,11 +249,11 @@ mod tests { #[test] fn discover_reports_an_unexpected_file_nested_two_directories_deep() { let layer_root = unique_temp_dir(); - let nested = layer_root.join("spells/extra/deeper/notes.md"); + let nested = layer_root.path().join("spells/extra/deeper/notes.md"); fs::create_dir_all(nested.parent().unwrap()).unwrap(); fs::write(&nested, "not a real entry").unwrap(); - let (files, failures) = discover(&layer_root); + let (files, failures) = discover(layer_root.path()); assert!(files.is_empty()); assert_eq!(failures.len(), 1); @@ -262,7 +262,8 @@ mod tests { #[test] fn find_md_files_returns_nothing_for_an_unreadable_directory() { - let missing = unique_temp_dir().join("does-not-exist"); + let temp_dir = unique_temp_dir(); + let missing = temp_dir.path().join("does-not-exist"); assert_eq!(find_md_files(&missing), Vec::::new()); } @@ -270,7 +271,7 @@ mod tests { fn discover_tolerates_a_missing_kind_directory() { let layer_root = unique_temp_dir(); - let (files, failures) = discover(&layer_root); + let (files, failures) = discover(layer_root.path()); assert!(files.is_empty()); assert_eq!(failures, vec![]); @@ -279,7 +280,7 @@ mod tests { #[test] fn read_frontmatter_parses_a_single_entry_by_path() { let layer_root = unique_temp_dir(); - let path = layer_root.join("spells/fireball.md"); + let path = layer_root.path().join("spells/fireball.md"); write_file(&path, "---\nname: Fireball\ntype: spell\n---\nbody\n"); let frontmatter = read_frontmatter(&path).unwrap(); @@ -293,7 +294,8 @@ mod tests { #[test] fn read_frontmatter_reports_a_file_it_cannot_read() { - let missing = unique_temp_dir().join("does-not-exist.md"); + let temp_dir = unique_temp_dir(); + let missing = temp_dir.path().join("does-not-exist.md"); let error = read_frontmatter(&missing).unwrap_err(); diff --git a/src/srd/verify/mod.rs b/src/srd/verify/mod.rs index 6fa8969..42bb9af 100644 --- a/src/srd/verify/mod.rs +++ b/src/srd/verify/mod.rs @@ -166,7 +166,7 @@ mod tests { fn verify_on_an_empty_layer_finds_no_entries_but_reports_shape_failures() { let layer_root = unique_temp_dir(); - let report = verify(&layer_root); + let report = verify(layer_root.path()); assert!(report.counts.is_empty()); assert!( @@ -187,11 +187,11 @@ mod tests { fn verify_counts_files_per_kind_even_when_universal_validation_fails() { let layer_root = unique_temp_dir(); write_file( - &layer_root.join("spells/fireball.md"), + &layer_root.path().join("spells/fireball.md"), "---\ntype: spell\n---\nbody\n", ); - let report = verify(&layer_root); + let report = verify(layer_root.path()); assert_eq!(report.counts.get(&Kind::Spell), Some(&1)); assert!(report.failures.iter().any(|f| f.message.contains("'name'"))); @@ -201,15 +201,15 @@ mod tests { fn verify_dispatches_spell_schema_validation() { let layer_root = unique_temp_dir(); let source = "sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md"; - write_file(&layer_root.join(source), "content"); + write_file(&layer_root.path().join(source), "content"); write_file( - &layer_root.join("spells/fireball.md"), + &layer_root.path().join("spells/fireball.md"), &format!( "---\nname: Fireball\ntype: spell\nsource: {source}\nschool: astrology\n---\nbody\n" ), ); - let report = verify(&layer_root); + let report = verify(layer_root.path()); assert!(report.failures.iter().any(|f| f.message.contains("school"))); } @@ -218,15 +218,15 @@ mod tests { fn verify_dispatches_magic_item_schema_validation() { let layer_root = unique_temp_dir(); let source = "sources/dnd.srd.5.2.1/10_Magic_Items/Magic_Items_Each/Amulet.md"; - write_file(&layer_root.join(source), "content"); + write_file(&layer_root.path().join(source), "content"); write_file( - &layer_root.join("magic-items/amulet.md"), + &layer_root.path().join("magic-items/amulet.md"), &format!( "---\nname: Amulet\ntype: magic-item\nsource: {source}\nrarity: mythical\n---\nbody\n" ), ); - let report = verify(&layer_root); + let report = verify(layer_root.path()); assert!(report.failures.iter().any(|f| f.message.contains("rarity"))); } @@ -235,13 +235,13 @@ mod tests { fn verify_dispatches_monster_schema_validation() { let layer_root = unique_temp_dir(); let source = "sources/dnd.srd.5.2.1/11_Monsters/Monsters_Each/Goblin.md"; - write_file(&layer_root.join(source), "content"); + write_file(&layer_root.path().join(source), "content"); write_file( - &layer_root.join("monsters/goblin.md"), + &layer_root.path().join("monsters/goblin.md"), &format!("---\nname: Goblin\ntype: monster\nsource: {source}\n---\nbody\n"), ); - let report = verify(&layer_root); + let report = verify(layer_root.path()); assert!( report @@ -255,15 +255,15 @@ mod tests { fn verify_requires_nothing_extra_for_core_class_and_feat() { let layer_root = unique_temp_dir(); let source = "sources/dnd.srd.5.2.1/05_Feats/Feats_Each/Alert.md"; - write_file(&layer_root.join(source), "# Alert\n\nBody text.\n"); + write_file(&layer_root.path().join(source), "# Alert\n\nBody text.\n"); write_file( - &layer_root.join("feats/alert.md"), + &layer_root.path().join("feats/alert.md"), &format!( "---\nname: Alert\ntype: feat\nsource: {source}\n---\n# Alert\n\nBody text.\n" ), ); - let report = verify(&layer_root); + let report = verify(layer_root.path()); // The layer is otherwise empty, so shape checks (count floors, // README, meta.yaml) still fail; this only confirms the feat @@ -280,15 +280,15 @@ mod tests { fn verify_reports_a_fidelity_mismatch() { let layer_root = unique_temp_dir(); let source = "sources/dnd.srd.5.2.1/05_Feats/Feats_Each/Alert.md"; - write_file(&layer_root.join(source), "# Alert\n\nSource text.\n"); + write_file(&layer_root.path().join(source), "# Alert\n\nSource text.\n"); write_file( - &layer_root.join("feats/alert.md"), + &layer_root.path().join("feats/alert.md"), &format!( "---\nname: Alert\ntype: feat\nsource: {source}\n---\n# Alert\n\nDifferent text.\n" ), ); - let report = verify(&layer_root); + let report = verify(layer_root.path()); assert!( report @@ -308,18 +308,18 @@ mod tests { let layer_root = unique_temp_dir(); let light_source = "sources/dnd.srd.5.2.1/05_Feats/Feats_Each/Light.md"; let dim_light_source = "sources/dnd.srd.5.2.1/05_Feats/Feats_Each/Dim_Light.md"; - write_file(&layer_root.join(light_source), "content"); - write_file(&layer_root.join(dim_light_source), "content"); + write_file(&layer_root.path().join(light_source), "content"); + write_file(&layer_root.path().join(dim_light_source), "content"); write_file( - &layer_root.join("feats/light.md"), + &layer_root.path().join("feats/light.md"), &format!("---\nname: Light\ntype: feat\nsource: {light_source}\n---\nbody\n"), ); write_file( - &layer_root.join("feats/light-duplicate.md"), + &layer_root.path().join("feats/light-duplicate.md"), &format!("---\nname: Light\ntype: feat\nsource: {dim_light_source}\n---\nbody\n"), ); - let report = verify(&layer_root); + let report = verify(layer_root.path()); assert!( report diff --git a/src/srd/verify/readme_meta.rs b/src/srd/verify/readme_meta.rs index 489b709..7e28cf1 100644 --- a/src/srd/verify/readme_meta.rs +++ b/src/srd/verify/readme_meta.rs @@ -138,10 +138,10 @@ mod tests { const ATTRIBUTION: &str = "This work includes material from the System Reference Document 5.2.1 (\"SRD 5.2.1\") by Wizards of the Coast LLC, available at https://www.dndbeyond.com/srd."; - fn layer_with_legal(attribution: &str) -> std::path::PathBuf { + fn layer_with_legal(attribution: &str) -> tempfile::TempDir { let layer_root = unique_temp_dir(); write_file( - &layer_root.join(LEGAL_PATH), + &layer_root.path().join(LEGAL_PATH), &format!("# Legal Information\n\n{attribution}\n\nOther text.\n"), ); layer_root @@ -151,12 +151,12 @@ mod tests { fn check_readme_passes_with_attribution_and_link() { let layer_root = layer_with_legal(ATTRIBUTION); write_file( - &layer_root.join(README_PATH), + &layer_root.path().join(README_PATH), &format!("# SRD\n\n{ATTRIBUTION}\n\nSee {SOURCE_REPO_URL}.\n"), ); let mut failures = Vec::new(); - check_readme(&layer_root, &mut failures); + check_readme(layer_root.path(), &mut failures); assert_eq!(failures, vec![]); } @@ -166,12 +166,12 @@ mod tests { let layer_root = layer_with_legal(ATTRIBUTION); let rewrapped = ATTRIBUTION.replace(' ', "\n"); write_file( - &layer_root.join(README_PATH), + &layer_root.path().join(README_PATH), &format!("# SRD\n\n{rewrapped}\n\nSee {SOURCE_REPO_URL}.\n"), ); let mut failures = Vec::new(); - check_readme(&layer_root, &mut failures); + check_readme(layer_root.path(), &mut failures); assert_eq!(failures, vec![]); } @@ -181,7 +181,7 @@ mod tests { let layer_root = unique_temp_dir(); let mut failures = Vec::new(); - check_readme(&layer_root, &mut failures); + check_readme(layer_root.path(), &mut failures); assert!( failures @@ -194,12 +194,12 @@ mod tests { fn check_readme_reports_a_missing_attribution() { let layer_root = layer_with_legal(ATTRIBUTION); write_file( - &layer_root.join(README_PATH), + &layer_root.path().join(README_PATH), &format!("# SRD\n\nSee {SOURCE_REPO_URL}.\n"), ); let mut failures = Vec::new(); - check_readme(&layer_root, &mut failures); + check_readme(layer_root.path(), &mut failures); assert!( failures @@ -221,12 +221,12 @@ mod tests { source_attribution.replace("by/4.0/ legalcode", "by/4.0/legalcode"); let layer_root = layer_with_legal(&source_attribution); write_file( - &layer_root.join(README_PATH), + &layer_root.path().join(README_PATH), &format!("# SRD\n\n{readme_attribution}\n\nSee {SOURCE_REPO_URL}.\n"), ); let mut failures = Vec::new(); - check_readme(&layer_root, &mut failures); + check_readme(layer_root.path(), &mut failures); assert_eq!(failures, vec![]); } @@ -235,12 +235,12 @@ mod tests { fn check_readme_reports_a_missing_source_link() { let layer_root = layer_with_legal(ATTRIBUTION); write_file( - &layer_root.join(README_PATH), + &layer_root.path().join(README_PATH), &format!("# SRD\n\n{ATTRIBUTION}\n"), ); let mut failures = Vec::new(); - check_readme(&layer_root, &mut failures); + check_readme(layer_root.path(), &mut failures); assert!(failures.iter().any(|f| f.message.contains("does not link"))); } @@ -248,10 +248,10 @@ mod tests { #[test] fn check_readme_reports_an_unreadable_legal_file() { let layer_root = unique_temp_dir(); - write_file(&layer_root.join(README_PATH), "# SRD\n"); + write_file(&layer_root.path().join(README_PATH), "# SRD\n"); let mut failures = Vec::new(); - check_readme(&layer_root, &mut failures); + check_readme(layer_root.path(), &mut failures); assert!(failures.iter().any(|f| f.message.contains("cannot read"))); } @@ -260,13 +260,13 @@ mod tests { fn check_readme_reports_a_legal_file_without_an_attribution_paragraph() { let layer_root = unique_temp_dir(); write_file( - &layer_root.join(LEGAL_PATH), + &layer_root.path().join(LEGAL_PATH), "# Legal\n\nNo attribution here.\n", ); - write_file(&layer_root.join(README_PATH), "# SRD\n"); + write_file(&layer_root.path().join(README_PATH), "# SRD\n"); let mut failures = Vec::new(); - check_readme(&layer_root, &mut failures); + check_readme(layer_root.path(), &mut failures); assert!( failures @@ -287,10 +287,10 @@ mod tests { #[test] fn check_meta_passes_for_a_well_formed_file() { let layer_root = unique_temp_dir(); - write_file(&layer_root.join(META_PATH), valid_meta_yaml()); + write_file(&layer_root.path().join(META_PATH), valid_meta_yaml()); let mut failures = Vec::new(); - check_meta(&layer_root, &mut failures); + check_meta(layer_root.path(), &mut failures); assert_eq!(failures, vec![]); } @@ -300,7 +300,7 @@ mod tests { let layer_root = unique_temp_dir(); let mut failures = Vec::new(); - check_meta(&layer_root, &mut failures); + check_meta(layer_root.path(), &mut failures); assert!( failures @@ -312,10 +312,10 @@ mod tests { #[test] fn check_meta_reports_invalid_yaml() { let layer_root = unique_temp_dir(); - write_file(&layer_root.join(META_PATH), "not: [valid"); + write_file(&layer_root.path().join(META_PATH), "not: [valid"); let mut failures = Vec::new(); - check_meta(&layer_root, &mut failures); + check_meta(layer_root.path(), &mut failures); assert!(failures.iter().any(|f| f.message.contains("invalid YAML"))); } @@ -323,10 +323,10 @@ mod tests { #[test] fn check_meta_reports_a_non_mapping_document() { let layer_root = unique_temp_dir(); - write_file(&layer_root.join(META_PATH), "just a string"); + write_file(&layer_root.path().join(META_PATH), "just a string"); let mut failures = Vec::new(); - check_meta(&layer_root, &mut failures); + check_meta(layer_root.path(), &mut failures); assert!( failures @@ -339,12 +339,12 @@ mod tests { fn check_meta_reports_a_missing_version() { let layer_root = unique_temp_dir(); write_file( - &layer_root.join(META_PATH), + &layer_root.path().join(META_PATH), "sources:\n - path: a\n sha256: abc\n - path: b\n commit: deadbeef", ); let mut failures = Vec::new(); - check_meta(&layer_root, &mut failures); + check_meta(layer_root.path(), &mut failures); assert!(failures.iter().any(|f| f.message.contains("version"))); } @@ -352,10 +352,10 @@ mod tests { #[test] fn check_meta_reports_a_missing_sources_list() { let layer_root = unique_temp_dir(); - write_file(&layer_root.join(META_PATH), "version: 5.2.1"); + write_file(&layer_root.path().join(META_PATH), "version: 5.2.1"); let mut failures = Vec::new(); - check_meta(&layer_root, &mut failures); + check_meta(layer_root.path(), &mut failures); assert!( failures @@ -368,12 +368,12 @@ mod tests { fn check_meta_reports_a_missing_pdf_pin() { let layer_root = unique_temp_dir(); write_file( - &layer_root.join(META_PATH), + &layer_root.path().join(META_PATH), "version: 5.2.1\nsources:\n - path: sources/dnd.srd.5.2.1/\n commit: deadbeef", ); let mut failures = Vec::new(); - check_meta(&layer_root, &mut failures); + check_meta(layer_root.path(), &mut failures); assert!(failures.iter().any(|f| f.message.contains("sha256"))); } @@ -382,12 +382,12 @@ mod tests { fn check_meta_reports_a_null_sha256_pin() { let layer_root = unique_temp_dir(); write_file( - &layer_root.join(META_PATH), + &layer_root.path().join(META_PATH), "version: 5.2.1\nsources:\n - path: sources/SRD_CC_v5.2.1.pdf\n sha256: null\n - path: sources/dnd.srd.5.2.1/\n commit: deadbeef", ); let mut failures = Vec::new(); - check_meta(&layer_root, &mut failures); + check_meta(layer_root.path(), &mut failures); assert!(failures.iter().any(|f| f.message.contains("sha256"))); } @@ -396,12 +396,12 @@ mod tests { fn check_meta_reports_an_empty_commit_pin() { let layer_root = unique_temp_dir(); write_file( - &layer_root.join(META_PATH), + &layer_root.path().join(META_PATH), "version: 5.2.1\nsources:\n - path: sources/SRD_CC_v5.2.1.pdf\n sha256: abc\n - path: sources/dnd.srd.5.2.1/\n commit: ''", ); let mut failures = Vec::new(); - check_meta(&layer_root, &mut failures); + check_meta(layer_root.path(), &mut failures); assert!(failures.iter().any(|f| f.message.contains("commit"))); } @@ -410,12 +410,12 @@ mod tests { fn check_meta_reports_a_missing_text_pin() { let layer_root = unique_temp_dir(); write_file( - &layer_root.join(META_PATH), + &layer_root.path().join(META_PATH), "version: 5.2.1\nsources:\n - path: sources/SRD_CC_v5.2.1.pdf\n sha256: abc", ); let mut failures = Vec::new(); - check_meta(&layer_root, &mut failures); + check_meta(layer_root.path(), &mut failures); assert!(failures.iter().any(|f| f.message.contains("commit"))); } diff --git a/src/srd/verify/schema_tests.rs b/src/srd/verify/schema_tests.rs index ebbfae0..dd78a18 100644 --- a/src/srd/verify/schema_tests.rs +++ b/src/srd/verify/schema_tests.rs @@ -13,9 +13,9 @@ fn corpus_file(kind: Kind, path: PathBuf, frontmatter_yaml: &str) -> CorpusFile } } -fn layer_with_source(source_relative: &str) -> PathBuf { +fn layer_with_source(source_relative: &str) -> tempfile::TempDir { let layer_root = unique_temp_dir(); - write_file(&layer_root.join(source_relative), "content"); + write_file(&layer_root.path().join(source_relative), "content"); layer_root } @@ -55,12 +55,12 @@ fn validate_universal_rejects_an_animal_sourced_from_monsters_each() { let layer_root = layer_with_source("sources/dnd.srd.5.2.1/11_Monsters/Monsters_Each/Wolf.md"); let file = corpus_file( Kind::Animal, - layer_root.join("animals/wolf.md"), + layer_root.path().join("animals/wolf.md"), "name: Wolf\ntype: animal\nsource: sources/dnd.srd.5.2.1/11_Monsters/Monsters_Each/Wolf.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert!(universal.is_none()); assert!( @@ -75,12 +75,12 @@ fn validate_universal_accepts_an_animal_sourced_from_animals_each() { let layer_root = layer_with_source("sources/dnd.srd.5.2.1/12_Animals/Animals_Each/Wolf.md"); let file = corpus_file( Kind::Animal, - layer_root.join("animals/wolf.md"), + layer_root.path().join("animals/wolf.md"), "name: Wolf\ntype: animal\nsource: sources/dnd.srd.5.2.1/12_Animals/Animals_Each/Wolf.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert_eq!(failures, vec![]); assert!(universal.is_some()); @@ -92,12 +92,12 @@ fn validate_universal_accepts_a_species_source_from_its_family() { layer_with_source("sources/dnd.srd.5.2.1/04_Character_Origins/Species/Species_Each/Elf.md"); let file = corpus_file( Kind::Species, - layer_root.join("species/elf.md"), + layer_root.path().join("species/elf.md"), "name: Elf\ntype: species\nsource: sources/dnd.srd.5.2.1/04_Character_Origins/Species/Species_Each/Elf.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert_eq!(failures, vec![]); assert!(universal.is_some()); @@ -110,12 +110,12 @@ fn validate_universal_accepts_a_background_source_from_its_family() { ); let file = corpus_file( Kind::Background, - layer_root.join("backgrounds/acolyte.md"), + layer_root.path().join("backgrounds/acolyte.md"), "name: Acolyte\ntype: background\nsource: sources/dnd.srd.5.2.1/04_Character_Origins/Backgrounds/Backgrounds_Each/Acolyte.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert_eq!(failures, vec![]); assert!(universal.is_some()); @@ -128,12 +128,12 @@ fn validate_universal_accepts_a_glossary_source_from_its_family() { ); let file = corpus_file( Kind::Glossary, - layer_root.join("glossary/advantage.md"), + layer_root.path().join("glossary/advantage.md"), "name: Advantage\ntype: glossary\nsource: sources/dnd.srd.5.2.1/08_Rules_Glossary/Rules_Glossary_Each/Advantage.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert_eq!(failures, vec![]); assert!(universal.is_some()); @@ -145,12 +145,12 @@ fn validate_universal_accepts_an_equipment_source_from_its_family() { layer_with_source("sources/dnd.srd.5.2.1/06_Equipment/Equipment_Each/Armor.md"); let file = corpus_file( Kind::Equipment, - layer_root.join("equipment/armor.md"), + layer_root.path().join("equipment/armor.md"), "name: Armor\ntype: equipment\nsource: sources/dnd.srd.5.2.1/06_Equipment/Equipment_Each/Armor.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert_eq!(failures, vec![]); assert!(universal.is_some()); @@ -168,12 +168,12 @@ fn validate_universal_accepts_an_equipment_source_from_a_nested_each_directory() ); let file = corpus_file( Kind::Equipment, - layer_root.join("equipment/acid.md"), + layer_root.path().join("equipment/acid.md"), "name: Acid\ntype: equipment\nsource: sources/dnd.srd.5.2.1/06_Equipment/Equipment_A-Z/Adventuring_Gear/Adventuring_Gear_Each/Acid.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert_eq!(failures, vec![]); assert!(universal.is_some()); @@ -186,12 +186,12 @@ fn validate_universal_accepts_an_equipment_source_from_a_second_nested_each_dire ); let file = corpus_file( Kind::Equipment, - layer_root.join("equipment/alchemist-s-supplies.md"), + layer_root.path().join("equipment/alchemist-s-supplies.md"), "name: Alchemist's Supplies\ntype: equipment\nsource: sources/dnd.srd.5.2.1/06_Equipment/Equipment_A-Z/Tools/Tools_Each/Alchemist's_Supplies.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert_eq!(failures, vec![]); assert!(universal.is_some()); @@ -207,12 +207,12 @@ fn validate_universal_rejects_an_equipment_source_outside_any_each_directory() { ); let file = corpus_file( Kind::Equipment, - layer_root.join("equipment/adventuring-gear-a.md"), + layer_root.path().join("equipment/adventuring-gear-a.md"), "name: Adventuring Gear A\ntype: equipment\nsource: sources/dnd.srd.5.2.1/06_Equipment/Equipment_A-Z/Adventuring_Gear/Adventuring_Gear_A-Z/Adventuring_Gear_A.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert!(universal.is_none()); assert!( @@ -227,12 +227,12 @@ fn validate_universal_rejects_an_equipment_source_from_a_different_chapter() { let layer_root = layer_with_source("sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md"); let file = corpus_file( Kind::Equipment, - layer_root.join("equipment/fireball.md"), + layer_root.path().join("equipment/fireball.md"), "name: Fireball\ntype: equipment\nsource: sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert!(universal.is_none()); assert!( @@ -247,12 +247,12 @@ fn validate_universal_accepts_a_well_formed_file() { let layer_root = layer_with_source("sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md"); let file = corpus_file( Kind::Spell, - layer_root.join("spells/fireball.md"), + layer_root.path().join("spells/fireball.md"), "name: Fireball\ntype: spell\nsource: sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert_eq!(failures, vec![]); assert_eq!(universal.unwrap().name, "Fireball"); @@ -263,12 +263,12 @@ fn validate_universal_rejects_a_missing_name() { let layer_root = layer_with_source("sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md"); let file = corpus_file( Kind::Spell, - layer_root.join("spells/fireball.md"), + layer_root.path().join("spells/fireball.md"), "type: spell\nsource: sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert!(universal.is_none()); assert!(failures.iter().any(|f| f.message.contains("'name'"))); @@ -279,12 +279,12 @@ fn validate_universal_rejects_a_missing_type() { let layer_root = layer_with_source("sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md"); let file = corpus_file( Kind::Spell, - layer_root.join("spells/fireball.md"), + layer_root.path().join("spells/fireball.md"), "name: Fireball\nsource: sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md", ); let mut failures = Vec::new(); - validate_universal(&file, &layer_root, &mut failures); + validate_universal(&file, layer_root.path(), &mut failures); assert!(failures.iter().any(|f| f.message.contains("'type'"))); } @@ -294,12 +294,12 @@ fn validate_universal_rejects_a_missing_source_key() { let layer_root = unique_temp_dir(); let file = corpus_file( Kind::Spell, - layer_root.join("spells/fireball.md"), + layer_root.path().join("spells/fireball.md"), "name: Fireball\ntype: spell", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert!(universal.is_none()); assert!(failures.iter().any(|f| f.message.contains("'source'"))); @@ -310,12 +310,12 @@ fn validate_universal_rejects_an_unknown_key() { let layer_root = layer_with_source("sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md"); let file = corpus_file( Kind::Spell, - layer_root.join("spells/fireball.md"), + layer_root.path().join("spells/fireball.md"), "name: Fireball\ntype: spell\nsource: sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md\nfoo: bar", ); let mut failures = Vec::new(); - validate_universal(&file, &layer_root, &mut failures); + validate_universal(&file, layer_root.path(), &mut failures); assert!( failures @@ -329,12 +329,12 @@ fn validate_universal_rejects_a_non_string_key() { let layer_root = layer_with_source("sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md"); let file = corpus_file( Kind::Spell, - layer_root.join("spells/fireball.md"), + layer_root.path().join("spells/fireball.md"), "name: Fireball\ntype: spell\nsource: sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md\n5: bar", ); let mut failures = Vec::new(); - validate_universal(&file, &layer_root, &mut failures); + validate_universal(&file, layer_root.path(), &mut failures); assert!( failures @@ -348,12 +348,12 @@ fn validate_universal_rejects_a_non_mapping_frontmatter() { let layer_root = unique_temp_dir(); let file = corpus_file( Kind::Spell, - layer_root.join("spells/fireball.md"), + layer_root.path().join("spells/fireball.md"), "just a string", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert!(universal.is_none()); assert!( @@ -368,12 +368,12 @@ fn validate_universal_rejects_a_type_directory_mismatch() { let layer_root = layer_with_source("sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md"); let file = corpus_file( Kind::Spell, - layer_root.join("spells/fireball.md"), + layer_root.path().join("spells/fireball.md"), "name: Fireball\ntype: monster\nsource: sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md", ); let mut failures = Vec::new(); - validate_universal(&file, &layer_root, &mut failures); + validate_universal(&file, layer_root.path(), &mut failures); assert!( failures @@ -387,12 +387,12 @@ fn validate_universal_rejects_a_missing_source_file() { let layer_root = unique_temp_dir(); let file = corpus_file( Kind::Spell, - layer_root.join("spells/fireball.md"), + layer_root.path().join("spells/fireball.md"), "name: Fireball\ntype: spell\nsource: sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md", ); let mut failures = Vec::new(); - validate_universal(&file, &layer_root, &mut failures); + validate_universal(&file, layer_root.path(), &mut failures); assert!( failures @@ -410,12 +410,12 @@ fn validate_universal_rejects_a_source_from_the_wrong_kind_family() { let layer_root = layer_with_source("sources/dnd.srd.5.2.1/Legal.md"); let file = corpus_file( Kind::Feat, - layer_root.join("feats/legal-information.md"), + layer_root.path().join("feats/legal-information.md"), "name: Legal Information\ntype: feat\nsource: sources/dnd.srd.5.2.1/Legal.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert!(universal.is_none()); assert!( @@ -431,12 +431,12 @@ fn validate_universal_accepts_a_core_source_directly_under_a_numbered_chapter() layer_with_source("sources/dnd.srd.5.2.1/01_Playing_The_Game/Playing_The_Game.md"); let file = corpus_file( Kind::Core, - layer_root.join("core/playing-the-game.md"), + layer_root.path().join("core/playing-the-game.md"), "name: Playing the Game\ntype: core\nsource: sources/dnd.srd.5.2.1/01_Playing_The_Game/Playing_The_Game.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert_eq!(failures, vec![]); assert!(universal.is_some()); @@ -449,12 +449,12 @@ fn validate_universal_rejects_a_core_source_nested_in_an_each_directory() { let layer_root = layer_with_source("sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md"); let file = corpus_file( Kind::Core, - layer_root.join("core/fireball.md"), + layer_root.path().join("core/fireball.md"), "name: Fireball\ntype: core\nsource: sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md", ); let mut failures = Vec::new(); - let universal = validate_universal(&file, &layer_root, &mut failures); + let universal = validate_universal(&file, layer_root.path(), &mut failures); assert!(universal.is_none()); assert!( @@ -467,15 +467,15 @@ fn validate_universal_rejects_a_core_source_nested_in_an_each_directory() { #[test] fn validate_universal_rejects_a_source_outside_the_vendored_tree() { let layer_root = unique_temp_dir(); - write_file(&layer_root.join("elsewhere.md"), "content"); + write_file(&layer_root.path().join("elsewhere.md"), "content"); let file = corpus_file( Kind::Spell, - layer_root.join("spells/fireball.md"), + layer_root.path().join("spells/fireball.md"), "name: Fireball\ntype: spell\nsource: elsewhere.md", ); let mut failures = Vec::new(); - validate_universal(&file, &layer_root, &mut failures); + validate_universal(&file, layer_root.path(), &mut failures); assert!(failures.iter().any(|f| f.message.contains("is not under"))); } @@ -485,12 +485,12 @@ fn validate_universal_rejects_a_slug_mismatch() { let layer_root = layer_with_source("sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md"); let file = corpus_file( Kind::Spell, - layer_root.join("spells/not-fireball.md"), + layer_root.path().join("spells/not-fireball.md"), "name: Fireball\ntype: spell\nsource: sources/dnd.srd.5.2.1/07_Spells/Spells_Each/Fireball.md", ); let mut failures = Vec::new(); - validate_universal(&file, &layer_root, &mut failures); + validate_universal(&file, layer_root.path(), &mut failures); assert!( failures -- 2.51.2