From 8270cb74745b7112784d398f6765074451cf8bf8 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Fri, 12 Jul 2024 10:42:05 +0100 Subject: [PATCH 01/65] Refactor test harness structure --- dotenv/tests/integration/dotenv.rs | 11 +++++++++++ dotenv/tests/integration/main.rs | 5 ++++- dotenv/tests/integration/util/mod.rs | 4 ++-- 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 dotenv/tests/integration/dotenv.rs diff --git a/dotenv/tests/integration/dotenv.rs b/dotenv/tests/integration/dotenv.rs new file mode 100644 index 00000000..a763659c --- /dev/null +++ b/dotenv/tests/integration/dotenv.rs @@ -0,0 +1,11 @@ +use crate::util::*; + +#[test] +fn dotenv_ok_default_env() { + test_in_default_env(|| { + dotenvy::dotenv().ok(); + assert_env_var(TEST_EXISTING_KEY, TEST_EXISTING_VALUE); + assert_env_var(TEST_KEY, TEST_VALUE); + }); +} + diff --git a/dotenv/tests/integration/main.rs b/dotenv/tests/integration/main.rs index 83c8c0aa..abb4e03a 100644 --- a/dotenv/tests/integration/main.rs +++ b/dotenv/tests/integration/main.rs @@ -1 +1,4 @@ -mod util; +pub mod util; + +mod dotenv; + diff --git a/dotenv/tests/integration/util/mod.rs b/dotenv/tests/integration/util/mod.rs index 12430b63..cae03caf 100644 --- a/dotenv/tests/integration/util/mod.rs +++ b/dotenv/tests/integration/util/mod.rs @@ -1,8 +1,8 @@ -#![allow(dead_code)] +use std::env::{self, VarError}; mod testenv; -use std::env::{self, VarError}; +pub use testenv::{test_in_default_env, test_in_env, KeyVal, TestEnv}; /// Default key used in envfile pub const TEST_KEY: &str = "TESTKEY"; From 23c6f9e2e103d512cceab563909ea6b9a383e2bf Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Fri, 12 Jul 2024 10:43:46 +0100 Subject: [PATCH 02/65] Test the test harness --- dotenv/tests/integration/util/mod.rs | 1 + dotenv/tests/integration/util/tests.rs | 95 ++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 dotenv/tests/integration/util/tests.rs diff --git a/dotenv/tests/integration/util/mod.rs b/dotenv/tests/integration/util/mod.rs index cae03caf..f77d5605 100644 --- a/dotenv/tests/integration/util/mod.rs +++ b/dotenv/tests/integration/util/mod.rs @@ -1,6 +1,7 @@ use std::env::{self, VarError}; mod testenv; +mod tests; pub use testenv::{test_in_default_env, test_in_env, KeyVal, TestEnv}; diff --git a/dotenv/tests/integration/util/tests.rs b/dotenv/tests/integration/util/tests.rs new file mode 100644 index 00000000..a5d21c7b --- /dev/null +++ b/dotenv/tests/integration/util/tests.rs @@ -0,0 +1,95 @@ +use super::*; + +mod default_env { + use super::*; + + #[test] + fn vars_state() { + test_in_default_env(|| { + assert_env_var(TEST_EXISTING_KEY, TEST_EXISTING_VALUE); + assert_env_var_unset(TEST_KEY); + }); + } + + #[test] + fn envfile_exists() { + let testenv = TestEnv::default(); + assert_envfile_exists_in_testenv(testenv); + } + + + #[test] + fn envfile_loaded_vars_state() { + test_in_default_env(|| { + dotenvy::dotenv().expect("Default TestEnv should have .env file"); + // dotenv() does not override existing var + assert_env_var(TEST_EXISTING_KEY, TEST_EXISTING_VALUE); + assert_env_var(TEST_KEY, TEST_VALUE); + }); + } +} + +mod testenv_init { + use super::*; + + #[test] + fn vars_state() { + let init_testenv = TestEnv::init(); + assert_default_keys_not_set_in_testenv(init_testenv); + } + + #[test] + fn no_envfile() { + let init_testenv = TestEnv::init(); + let envfile_path = init_testenv.envfile_path().to_owned(); + + test_in_env(init_testenv, || { + assert!(!envfile_path.exists()); + assert!(dotenvy::dotenv().is_err()); + }); + } +} + +mod testenv_init_with_envfile { + use super::*; + + #[test] + fn default_envfile_vars_state() { + let testenv = TestEnv::init_with_envfile(create_default_envfile()); + assert_default_keys_not_set_in_testenv(testenv); + } + + #[test] + fn default_envfile_exists() { + let testenv = TestEnv::init_with_envfile(create_default_envfile()); + assert_envfile_exists_in_testenv(testenv); + } + + #[test] + fn default_envfile_loaded_vars_state() { + let init_testenv = TestEnv::init_with_envfile(create_default_envfile()); + test_in_env(init_testenv, || { + dotenvy::dotenv().expect("Default TestEnv should have .env file"); + // dotenv() does not override existing var + // but existing key is not set in this testenv + assert_env_var(TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE); + assert_env_var(TEST_KEY, TEST_VALUE); + }); + } +} + +fn assert_envfile_exists_in_testenv(testenv: TestEnv) { + let envfile_path = testenv.envfile_path().to_owned(); + + test_in_env(testenv, || { + assert!(envfile_path.exists()); + assert!(dotenvy::dotenv().is_ok()); + }); +} + +fn assert_default_keys_not_set_in_testenv(testenv: TestEnv) { + test_in_env(testenv, || { + assert_env_var_unset(TEST_EXISTING_KEY); + assert_env_var_unset(TEST_KEY); + }); +} From 726c0571ce3bc8e23731cb09f55811aeb465a4e0 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Fri, 12 Jul 2024 11:07:47 +0100 Subject: [PATCH 03/65] Rustfmt test harness --- dotenv/tests/integration/dotenv.rs | 1 - dotenv/tests/integration/main.rs | 1 - dotenv/tests/integration/util/tests.rs | 1 - 3 files changed, 3 deletions(-) diff --git a/dotenv/tests/integration/dotenv.rs b/dotenv/tests/integration/dotenv.rs index a763659c..966fbf23 100644 --- a/dotenv/tests/integration/dotenv.rs +++ b/dotenv/tests/integration/dotenv.rs @@ -8,4 +8,3 @@ fn dotenv_ok_default_env() { assert_env_var(TEST_KEY, TEST_VALUE); }); } - diff --git a/dotenv/tests/integration/main.rs b/dotenv/tests/integration/main.rs index abb4e03a..a61ebe72 100644 --- a/dotenv/tests/integration/main.rs +++ b/dotenv/tests/integration/main.rs @@ -1,4 +1,3 @@ pub mod util; mod dotenv; - diff --git a/dotenv/tests/integration/util/tests.rs b/dotenv/tests/integration/util/tests.rs index a5d21c7b..fe8f60a2 100644 --- a/dotenv/tests/integration/util/tests.rs +++ b/dotenv/tests/integration/util/tests.rs @@ -17,7 +17,6 @@ mod default_env { assert_envfile_exists_in_testenv(testenv); } - #[test] fn envfile_loaded_vars_state() { test_in_default_env(|| { From 588959d27855252be55450cf5d105b07bd341d2f Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Fri, 12 Jul 2024 11:13:04 +0100 Subject: [PATCH 04/65] Refactor dotenv usage into wrapper --- dotenv/tests/integration/util/tests.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/dotenv/tests/integration/util/tests.rs b/dotenv/tests/integration/util/tests.rs index fe8f60a2..02ba6792 100644 --- a/dotenv/tests/integration/util/tests.rs +++ b/dotenv/tests/integration/util/tests.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use super::*; mod default_env { @@ -20,7 +22,7 @@ mod default_env { #[test] fn envfile_loaded_vars_state() { test_in_default_env(|| { - dotenvy::dotenv().expect("Default TestEnv should have .env file"); + dotenv_wrapper().expect("Default TestEnv should have .env file"); // dotenv() does not override existing var assert_env_var(TEST_EXISTING_KEY, TEST_EXISTING_VALUE); assert_env_var(TEST_KEY, TEST_VALUE); @@ -44,7 +46,7 @@ mod testenv_init { test_in_env(init_testenv, || { assert!(!envfile_path.exists()); - assert!(dotenvy::dotenv().is_err()); + assert!(dotenv_wrapper().is_err()); }); } } @@ -68,7 +70,7 @@ mod testenv_init_with_envfile { fn default_envfile_loaded_vars_state() { let init_testenv = TestEnv::init_with_envfile(create_default_envfile()); test_in_env(init_testenv, || { - dotenvy::dotenv().expect("Default TestEnv should have .env file"); + dotenv_wrapper().expect("Default TestEnv should have .env file"); // dotenv() does not override existing var // but existing key is not set in this testenv assert_env_var(TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE); @@ -82,7 +84,7 @@ fn assert_envfile_exists_in_testenv(testenv: TestEnv) { test_in_env(testenv, || { assert!(envfile_path.exists()); - assert!(dotenvy::dotenv().is_ok()); + assert!(dotenv_wrapper().is_ok()); }); } @@ -92,3 +94,7 @@ fn assert_default_keys_not_set_in_testenv(testenv: TestEnv) { assert_env_var_unset(TEST_KEY); }); } + +fn dotenv_wrapper() -> dotenvy::Result { + dotenvy::dotenv() +} From 48331425cc728844bad4fa03b3cb81bb34daf127 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Fri, 12 Jul 2024 11:57:52 +0100 Subject: [PATCH 05/65] Fix envfile contents now represented as bytes --- dotenv/tests/integration/util/testenv.rs | 49 +++++++++++++----------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/dotenv/tests/integration/util/testenv.rs b/dotenv/tests/integration/util/testenv.rs index a3d251bc..f085f2f4 100644 --- a/dotenv/tests/integration/util/testenv.rs +++ b/dotenv/tests/integration/util/testenv.rs @@ -31,7 +31,7 @@ pub struct TestEnv { temp_dir: TempDir, work_dir: PathBuf, env_vars: Vec, - envfile_contents: Option, + envfile_contents: Option>, envfile_path: PathBuf, } @@ -109,22 +109,12 @@ impl TestEnv { /// /// No pre-existing env_vars set. The envfile_name is set to `.env`. The /// working directory is the created temporary directory. - pub fn init_with_envfile(contents: impl ToString) -> Self { + pub fn init_with_envfile(contents: impl Into>) -> Self { let mut test_env = Self::init(); test_env.set_envfile_contents(contents); test_env } - /// Change the name of the default `.env` file. - /// - /// It will still be placed in the root temporary directory. If you need to - /// put the envfile in a different directory, use - /// [`set_envfile_path`](TestEnv::set_envfile_path) instead. - pub fn set_envfile_name(&mut self, name: impl AsRef) -> &mut Self { - self.envfile_path = self.temp_path().join(name); - self - } - /// Change the absolute path to the envfile. pub fn set_envfile_path(&mut self, path: PathBuf) -> &mut Self { self.envfile_path = path; @@ -134,11 +124,12 @@ impl TestEnv { /// Specify the contents of the envfile. /// /// If this is the only change to the [`TestEnv`] being made, use - /// [`new_with_envfile`](TestEnv::new_with_envfile). + /// [`init_with_envfile`](TestEnv::init_with_envfile). /// /// Setting it to an empty string will cause an empty envfile to be created - pub fn set_envfile_contents(&mut self, contents: impl ToString) -> &mut Self { - self.envfile_contents = Some(contents.to_string()); + pub fn set_envfile_contents(&mut self, contents: impl Into>) -> &mut Self { + let contents = contents.into(); + self.envfile_contents = Some(contents); self } @@ -232,13 +223,27 @@ impl TestEnv { &self.env_vars } - /// Get a reference to the string that will be placed in the envfile. + /// Get a reference to the bytes that will be placed in the envfile. /// /// If `None` is returned, an envfile will not be created - pub fn envfile_contents(&self) -> Option<&str> { + pub fn envfile_contents_as_bytes(&self) -> Option<&[u8]> { self.envfile_contents.as_deref() } + /// Get a reference to the string that will be placed in the envfile. + /// + /// If `None` is returned, an envfile will not be created + /// + /// ## Panics + /// + /// This will panic if the envfile contents are not valid UTF-8 + pub fn envfile_contents_as_str(&self) -> Option<&str> { + self.envfile_contents_as_bytes().map(|bytes| { + let out = std::str::from_utf8(bytes).expect("valid UTF-8"); + Some(out) + })? + } + /// Get a reference to the path of the envfile. pub fn envfile_path(&self) -> &Path { &self.envfile_path @@ -253,7 +258,7 @@ impl Default for TestEnv { key: TEST_EXISTING_KEY.into(), value: TEST_EXISTING_VALUE.into(), }]; - let envfile_contents = Some(create_default_envfile()); + let envfile_contents = Some(create_default_envfile().into()); let envfile_path = work_dir.join(".env"); Self { temp_dir, @@ -307,7 +312,7 @@ fn reset_env(original_env: &EnvMap) { /// Writes the envfile, sets the working directory, and sets environment vars. fn create_env(test_env: &TestEnv) { // only create the envfile if its contents has been set - if let Some(contents) = test_env.envfile_contents() { + if let Some(contents) = test_env.envfile_contents_as_bytes() { create_envfile(&test_env.envfile_path, contents); } @@ -319,14 +324,14 @@ fn create_env(test_env: &TestEnv) { } /// Create an envfile for use in tests. -fn create_envfile(path: &Path, contents: &str) { +fn create_envfile(path: &Path, contents: &[u8]) { if path.exists() { panic!("envfile `{}` already exists", path.display()) } // inner function to group together io::Results - fn create_env_file_inner(path: &Path, contents: &str) -> io::Result<()> { + fn create_env_file_inner(path: &Path, contents: &[u8]) -> io::Result<()> { let mut file = fs::File::create(path)?; - file.write_all(contents.as_bytes())?; + file.write_all(contents)?; file.sync_all() } // call inner function From 48d46099a46c5df47d997aa0aaf205f4d68af515 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Fri, 12 Jul 2024 12:44:02 +0100 Subject: [PATCH 06/65] Add more test harness assertions --- dotenv/tests/integration/util/mod.rs | 29 +++++++++++ dotenv/tests/integration/util/tests.rs | 66 ++++++++++++++++++++++---- 2 files changed, 85 insertions(+), 10 deletions(-) diff --git a/dotenv/tests/integration/util/mod.rs b/dotenv/tests/integration/util/mod.rs index f77d5605..394d8cbb 100644 --- a/dotenv/tests/integration/util/mod.rs +++ b/dotenv/tests/integration/util/mod.rs @@ -34,6 +34,35 @@ pub fn create_invalid_envfile() -> String { ) } +pub fn create_custom_envfile(env_vars: &[(&str, &str)]) -> String { + let mut envfile = String::new(); + for (key, value) in env_vars { + envfile.push_str(key); + envfile.push('='); + envfile.push_str(value); + envfile.push('\n'); + } + envfile +} + +/// Assert that a slice of environment variables are set and have the expected +/// values. +/// +/// ## Arguments +/// +/// * `vars` - A slice of key-expected value tuples +/// +/// ## Examples +/// +/// ```rust +/// assert_env_vars(&[("TESTKEY", "test_val"), ("TEST_EXISTING_KEY", "from_env")]); +/// ``` +pub fn assert_env_vars(vars: &[(&str, &str)]) { + for (key, expected) in vars { + assert_env_var(key, expected); + } +} + /// Assert that an environment variable is set and has the expected value. pub fn assert_env_var(key: &str, expected: &str) { match env::var(key) { diff --git a/dotenv/tests/integration/util/tests.rs b/dotenv/tests/integration/util/tests.rs index 02ba6792..0742a510 100644 --- a/dotenv/tests/integration/util/tests.rs +++ b/dotenv/tests/integration/util/tests.rs @@ -2,6 +2,13 @@ use std::path::PathBuf; use super::*; +const CUSTOM_VARS: &[(&str, &str)] = &[ + ("CUSTOM_KEY_1", "CUSTOM_VALUE_1"), + ("CUSTOM_KEY_2", "CUSTOM_VALUE_2"), +]; + +const DOTENV_EXPECT: &str = "TestEnv should have .env file"; + mod default_env { use super::*; @@ -22,7 +29,7 @@ mod default_env { #[test] fn envfile_loaded_vars_state() { test_in_default_env(|| { - dotenv_wrapper().expect("Default TestEnv should have .env file"); + dotenv_wrapper().expect(DOTENV_EXPECT); // dotenv() does not override existing var assert_env_var(TEST_EXISTING_KEY, TEST_EXISTING_VALUE); assert_env_var(TEST_KEY, TEST_VALUE); @@ -56,27 +63,64 @@ mod testenv_init_with_envfile { #[test] fn default_envfile_vars_state() { - let testenv = TestEnv::init_with_envfile(create_default_envfile()); + let testenv = init_default_testenv(); assert_default_keys_not_set_in_testenv(testenv); } #[test] fn default_envfile_exists() { - let testenv = TestEnv::init_with_envfile(create_default_envfile()); + let testenv = init_default_testenv(); assert_envfile_exists_in_testenv(testenv); } #[test] fn default_envfile_loaded_vars_state() { - let init_testenv = TestEnv::init_with_envfile(create_default_envfile()); - test_in_env(init_testenv, || { - dotenv_wrapper().expect("Default TestEnv should have .env file"); + let testenv = init_default_testenv(); + test_in_env(testenv, || { + dotenv_wrapper().expect(DOTENV_EXPECT); // dotenv() does not override existing var // but existing key is not set in this testenv assert_env_var(TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE); assert_env_var(TEST_KEY, TEST_VALUE); }); } + + #[test] + fn custom_envfile_vars_state() { + let testenv = init_custom_testenv(); + test_in_env(testenv, || { + assert_default_keys_not_set(); + for (key, _) in CUSTOM_VARS { + assert_env_var_unset(key); + } + }); + } + + #[test] + fn custom_envfile_exists() { + let testenv = init_custom_testenv(); + assert_envfile_exists_in_testenv(testenv); + } + + #[test] + fn custom_envfile_loaded_vars_state() { + let testenv = init_custom_testenv(); + test_in_env(testenv, || { + dotenv_wrapper().expect(DOTENV_EXPECT); + assert_default_keys_not_set(); + assert_env_vars(CUSTOM_VARS); + }); + } + + fn init_default_testenv() -> TestEnv { + let envfile = create_default_envfile(); + TestEnv::init_with_envfile(envfile) + } + + fn init_custom_testenv() -> TestEnv { + let envfile = create_custom_envfile(CUSTOM_VARS); + TestEnv::init_with_envfile(envfile) + } } fn assert_envfile_exists_in_testenv(testenv: TestEnv) { @@ -89,10 +133,12 @@ fn assert_envfile_exists_in_testenv(testenv: TestEnv) { } fn assert_default_keys_not_set_in_testenv(testenv: TestEnv) { - test_in_env(testenv, || { - assert_env_var_unset(TEST_EXISTING_KEY); - assert_env_var_unset(TEST_KEY); - }); + test_in_env(testenv, assert_default_keys_not_set); +} + +fn assert_default_keys_not_set() { + assert_env_var_unset(TEST_EXISTING_KEY); + assert_env_var_unset(TEST_KEY); } fn dotenv_wrapper() -> dotenvy::Result { From 461b9eb947f896ad9555acd56c43e62111a8e310 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Fri, 12 Jul 2024 13:33:35 +0100 Subject: [PATCH 07/65] Add API wrapper to test harness --- dotenv/tests/integration/util/mod.rs | 2 + dotenv/tests/integration/util/tests.rs | 16 ++--- dotenv/tests/integration/util/wrapper.rs | 80 ++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 11 deletions(-) create mode 100644 dotenv/tests/integration/util/wrapper.rs diff --git a/dotenv/tests/integration/util/mod.rs b/dotenv/tests/integration/util/mod.rs index 394d8cbb..89da3151 100644 --- a/dotenv/tests/integration/util/mod.rs +++ b/dotenv/tests/integration/util/mod.rs @@ -2,8 +2,10 @@ use std::env::{self, VarError}; mod testenv; mod tests; +mod wrapper; pub use testenv::{test_in_default_env, test_in_env, KeyVal, TestEnv}; +pub use wrapper::*; /// Default key used in envfile pub const TEST_KEY: &str = "TESTKEY"; diff --git a/dotenv/tests/integration/util/tests.rs b/dotenv/tests/integration/util/tests.rs index 0742a510..f6c4cb72 100644 --- a/dotenv/tests/integration/util/tests.rs +++ b/dotenv/tests/integration/util/tests.rs @@ -1,5 +1,3 @@ -use std::path::PathBuf; - use super::*; const CUSTOM_VARS: &[(&str, &str)] = &[ @@ -29,7 +27,7 @@ mod default_env { #[test] fn envfile_loaded_vars_state() { test_in_default_env(|| { - dotenv_wrapper().expect(DOTENV_EXPECT); + dotenv_wrap().expect(DOTENV_EXPECT); // dotenv() does not override existing var assert_env_var(TEST_EXISTING_KEY, TEST_EXISTING_VALUE); assert_env_var(TEST_KEY, TEST_VALUE); @@ -53,7 +51,7 @@ mod testenv_init { test_in_env(init_testenv, || { assert!(!envfile_path.exists()); - assert!(dotenv_wrapper().is_err()); + assert!(dotenv_wrap().is_err()); }); } } @@ -77,7 +75,7 @@ mod testenv_init_with_envfile { fn default_envfile_loaded_vars_state() { let testenv = init_default_testenv(); test_in_env(testenv, || { - dotenv_wrapper().expect(DOTENV_EXPECT); + dotenv_wrap().expect(DOTENV_EXPECT); // dotenv() does not override existing var // but existing key is not set in this testenv assert_env_var(TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE); @@ -106,7 +104,7 @@ mod testenv_init_with_envfile { fn custom_envfile_loaded_vars_state() { let testenv = init_custom_testenv(); test_in_env(testenv, || { - dotenv_wrapper().expect(DOTENV_EXPECT); + dotenv_wrap().expect(DOTENV_EXPECT); assert_default_keys_not_set(); assert_env_vars(CUSTOM_VARS); }); @@ -128,7 +126,7 @@ fn assert_envfile_exists_in_testenv(testenv: TestEnv) { test_in_env(testenv, || { assert!(envfile_path.exists()); - assert!(dotenv_wrapper().is_ok()); + assert!(dotenv_wrap().is_ok()); }); } @@ -140,7 +138,3 @@ fn assert_default_keys_not_set() { assert_env_var_unset(TEST_EXISTING_KEY); assert_env_var_unset(TEST_KEY); } - -fn dotenv_wrapper() -> dotenvy::Result { - dotenvy::dotenv() -} diff --git a/dotenv/tests/integration/util/wrapper.rs b/dotenv/tests/integration/util/wrapper.rs new file mode 100644 index 00000000..116791f7 --- /dev/null +++ b/dotenv/tests/integration/util/wrapper.rs @@ -0,0 +1,80 @@ +//! Wrappers for the `dotenvy` API. +//! +//! If the `dotenvy` API changes, only this module needs to be updated. + +use dotenvy::{self, Iter, Result}; +use std::env::Vars; +use std::ffi::OsStr; +use std::fs::File; +use std::io; +use std::path::{Path, PathBuf}; + +#[inline(always)] +pub fn var_wrap>(key: K) -> Result { + dotenvy::var(key) +} + +#[inline(always)] +pub fn vars_wrap() -> Vars { + dotenvy::vars() +} + +#[inline(always)] +pub fn from_path_wrap>(path: P) -> Result<()> { + dotenvy::from_path(path) +} + +#[inline(always)] +pub fn from_path_override_wrap>(path: P) -> Result<()> { + dotenvy::from_path_override(path) +} + +#[inline(always)] +pub fn from_path_iter_wrap>(path: P) -> Result> { + dotenvy::from_path_iter(path) +} + +#[inline(always)] +pub fn from_filename_wrap>(filename: P) -> Result { + dotenvy::from_filename(filename) +} + +#[inline(always)] +pub fn from_filename_override_wrap>(filename: P) -> Result { + dotenvy::from_filename_override(filename) +} + +#[inline(always)] +pub fn from_filename_iter_wrap>(filename: P) -> Result> { + dotenvy::from_filename_iter(filename) +} + +#[inline(always)] +pub fn from_read_wrap(reader: R) -> Result<()> { + dotenvy::from_read(reader) +} + +#[inline(always)] +pub fn from_read_override_wrap(reader: R) -> Result<()> { + dotenvy::from_read_override(reader) +} + +#[inline(always)] +pub fn from_read_iter_wrap(reader: R) -> Iter { + dotenvy::from_read_iter(reader) +} + +#[inline(always)] +pub fn dotenv_wrap() -> Result { + dotenvy::dotenv() +} + +#[inline(always)] +pub fn dotenv_override_wrap() -> Result { + dotenvy::dotenv_override() +} + +#[inline(always)] +pub fn dotenv_iter_wrap() -> Result> { + dotenvy::dotenv_iter() +} From 240d0f54f06a3d28bfaf620a602ea17286fabe44 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Fri, 12 Jul 2024 13:47:56 +0100 Subject: [PATCH 08/65] Test empty envfile in harness --- dotenv/tests/integration/util/tests.rs | 35 ++++++++++++++++++++------ 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/dotenv/tests/integration/util/tests.rs b/dotenv/tests/integration/util/tests.rs index f6c4cb72..303b0278 100644 --- a/dotenv/tests/integration/util/tests.rs +++ b/dotenv/tests/integration/util/tests.rs @@ -61,19 +61,19 @@ mod testenv_init_with_envfile { #[test] fn default_envfile_vars_state() { - let testenv = init_default_testenv(); + let testenv = init_default_envfile_testenv(); assert_default_keys_not_set_in_testenv(testenv); } #[test] fn default_envfile_exists() { - let testenv = init_default_testenv(); + let testenv = init_default_envfile_testenv(); assert_envfile_exists_in_testenv(testenv); } #[test] fn default_envfile_loaded_vars_state() { - let testenv = init_default_testenv(); + let testenv = init_default_envfile_testenv(); test_in_env(testenv, || { dotenv_wrap().expect(DOTENV_EXPECT); // dotenv() does not override existing var @@ -85,7 +85,7 @@ mod testenv_init_with_envfile { #[test] fn custom_envfile_vars_state() { - let testenv = init_custom_testenv(); + let testenv = init_custom_envfile_testenv(); test_in_env(testenv, || { assert_default_keys_not_set(); for (key, _) in CUSTOM_VARS { @@ -96,13 +96,13 @@ mod testenv_init_with_envfile { #[test] fn custom_envfile_exists() { - let testenv = init_custom_testenv(); + let testenv = init_custom_envfile_testenv(); assert_envfile_exists_in_testenv(testenv); } #[test] fn custom_envfile_loaded_vars_state() { - let testenv = init_custom_testenv(); + let testenv = init_custom_envfile_testenv(); test_in_env(testenv, || { dotenv_wrap().expect(DOTENV_EXPECT); assert_default_keys_not_set(); @@ -110,15 +110,34 @@ mod testenv_init_with_envfile { }); } - fn init_default_testenv() -> TestEnv { + #[test] + fn empty_envfile_exists() { + let testenv = init_empty_envfile_testenv(); + assert_envfile_exists_in_testenv(testenv); + } + + #[test] + fn empty_envfile_loaded_vars_state() { + let testenv = init_empty_envfile_testenv(); + test_in_env(testenv, || { + dotenv_wrap().expect(DOTENV_EXPECT); + assert_default_keys_not_set(); + }); + } + + fn init_default_envfile_testenv() -> TestEnv { let envfile = create_default_envfile(); TestEnv::init_with_envfile(envfile) } - fn init_custom_testenv() -> TestEnv { + fn init_custom_envfile_testenv() -> TestEnv { let envfile = create_custom_envfile(CUSTOM_VARS); TestEnv::init_with_envfile(envfile) } + + fn init_empty_envfile_testenv() -> TestEnv { + TestEnv::init_with_envfile([]) + } } fn assert_envfile_exists_in_testenv(testenv: TestEnv) { From 97e69b5b9c8c23e85d00d856e6d29e97fe805d61 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Fri, 12 Jul 2024 14:31:05 +0100 Subject: [PATCH 09/65] Add EnvFileBuilder to test harness --- dotenv/tests/integration/util/envfile.rs | 96 ++++++++++++++++++++ dotenv/tests/integration/util/mod.rs | 13 ++- dotenv/tests/integration/util/tests.rs | 109 +++++++++++++++++++++++ 3 files changed, 210 insertions(+), 8 deletions(-) create mode 100644 dotenv/tests/integration/util/envfile.rs diff --git a/dotenv/tests/integration/util/envfile.rs b/dotenv/tests/integration/util/envfile.rs new file mode 100644 index 00000000..4516823c --- /dev/null +++ b/dotenv/tests/integration/util/envfile.rs @@ -0,0 +1,96 @@ +/// Create envfile contents for testing +/// +/// Represented as bytes to allow for advanced manipulation and BOM testing. +#[derive(Debug, Default)] +pub struct EnvFileBuilder { + contents: Vec, +} + +impl EnvFileBuilder { + pub fn new() -> Self { + Self { + contents: Vec::new(), + } + } + + /// Build a byte vector from the contents of the builder. + pub fn build(&self) -> Vec { + self.contents.clone() + } + + /// Build a string from the contents of the builder. + /// + /// ## Panics + /// + /// If the contents of the builder are not valid UTF-8. + pub fn build_string(&self) -> String { + String::from_utf8(self.contents.clone()).expect("valid UTF-8") + } + + /// Transform the builder into a byte vector. + pub fn into_owned_bytes(self) -> Vec { + self.contents + } + + /// Transform the builder into a string. + /// + /// ## Panics + /// + /// If the contents of the builder are not valid UTF-8. + pub fn into_owned_string(self) -> String { + String::from_utf8(self.contents).expect("valid UTF-8") + } + + /// Get a reference to the contents of the builder. + pub fn as_bytes(&self) -> &[u8] { + &self.contents + } + + /// Add + pub fn add_vars(&mut self, env_vars: &[(&str, &str)]) -> &mut Self { + let mut many = String::new(); + for (key, value) in env_vars { + many.push_str(key); + many.push('='); + many.push_str(value); + many.push('\n'); + } + self.add_str(&many); + self + } + + /// Add a key-value pair and newline + pub fn add_key_value(&mut self, key: &str, value: &str) -> &mut Self { + self.add_strln(&format!("{}={}", key, value)) + } + + /// Add a string without a newline + pub fn add_str(&mut self, s: &str) -> &mut Self { + self.add_bytes(s.as_bytes()) + } + + /// Add a string with a newline + pub fn add_strln(&mut self, line: &str) -> &mut Self { + self.add_str(line).add_byte(b'\n') + } + + /// Add a byte slice + pub fn add_bytes(&mut self, bytes: &[u8]) -> &mut Self { + self.contents.extend_from_slice(bytes); + self + } + + /// Add a single byte + pub fn add_byte(&mut self, byte: u8) -> &mut Self { + self.contents.push(byte); + self + } + + /// Insert the UTF-8 Byte Order Mark at the beginning of the file + pub fn insert_utf8_bom(&mut self) -> &mut Self { + // https://www.compart.com/en/unicode/U+FEFF + let bom = b"\xEF\xBB\xBF"; + self.contents.splice(0..0, bom.iter().cloned()); + self + } +} diff --git a/dotenv/tests/integration/util/mod.rs b/dotenv/tests/integration/util/mod.rs index 89da3151..98423b43 100644 --- a/dotenv/tests/integration/util/mod.rs +++ b/dotenv/tests/integration/util/mod.rs @@ -1,9 +1,11 @@ use std::env::{self, VarError}; +mod envfile; mod testenv; mod tests; mod wrapper; +pub use envfile::EnvFileBuilder; pub use testenv::{test_in_default_env, test_in_env, KeyVal, TestEnv}; pub use wrapper::*; @@ -37,14 +39,9 @@ pub fn create_invalid_envfile() -> String { } pub fn create_custom_envfile(env_vars: &[(&str, &str)]) -> String { - let mut envfile = String::new(); - for (key, value) in env_vars { - envfile.push_str(key); - envfile.push('='); - envfile.push_str(value); - envfile.push('\n'); - } - envfile + let mut efb = EnvFileBuilder::new(); + efb.add_vars(env_vars); + efb.into_owned_string() } /// Assert that a slice of environment variables are set and have the expected diff --git a/dotenv/tests/integration/util/tests.rs b/dotenv/tests/integration/util/tests.rs index 303b0278..a6c0b40c 100644 --- a/dotenv/tests/integration/util/tests.rs +++ b/dotenv/tests/integration/util/tests.rs @@ -140,6 +140,115 @@ mod testenv_init_with_envfile { } } +mod envfile_builder { + use super::*; + + #[test] + fn new_builds_empty() { + let efb = EnvFileBuilder::new(); + assert_contents_empty(efb); + } + + #[test] + fn default_builds_empty() { + let efb = EnvFileBuilder::default(); + assert_contents_empty(efb); + } + + #[test] + fn add_key_empty_value() { + let mut efb = EnvFileBuilder::new(); + efb.add_key_value(TEST_KEY, ""); + let expected = format!("{}=\n", TEST_KEY); + assert_contents_str(efb, &expected); + } + + #[test] + fn add_key_value() { + let mut efb = EnvFileBuilder::new(); + efb.add_key_value(TEST_KEY, TEST_VALUE); + let expected = format!("{}={}\n", TEST_KEY, TEST_VALUE); + assert_contents_str(efb, &expected); + } + + #[test] + fn add_multiple_key_values() { + let mut efb = EnvFileBuilder::new(); + efb.add_key_value(TEST_KEY, TEST_VALUE); + efb.add_key_value(TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE); + let expected = expected_envfile(&[ + (TEST_KEY, TEST_VALUE), + (TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE), + ]); + assert_contents_str(efb, &expected); + } + + #[test] + fn add_vars() { + let mut efb = EnvFileBuilder::new(); + efb.add_vars(CUSTOM_VARS); + let expected = expected_envfile(CUSTOM_VARS); + assert_contents_str(efb, &expected); + } + + #[test] + fn add_str() { + let mut efb = EnvFileBuilder::new(); + efb.add_str("test"); + assert_contents_str(efb, "test"); + } + + #[test] + fn add_bytes() { + let mut efb = EnvFileBuilder::new(); + efb.add_bytes(b"test"); + assert_contents_str(efb, "test"); + } + + #[test] + fn add_byte() { + let mut efb = EnvFileBuilder::new(); + efb.add_byte(b't'); + assert_contents_str(efb, "t"); + } + + #[test] + fn insert_utf8_bom() { + let mut efb = EnvFileBuilder::new(); + efb.add_str("test"); + efb.insert_utf8_bom(); + assert_contents_str(efb, "\u{FEFF}test"); + } + + #[test] + fn add_strln() { + let mut efb = EnvFileBuilder::new(); + efb.add_strln("test"); + assert_contents_str(efb, "test\n"); + } + + fn assert_contents_empty(efb: EnvFileBuilder) { + let contents = efb.into_owned_bytes(); + assert!(contents.is_empty()); + } + + fn assert_contents_str(efb: EnvFileBuilder, expected: &str) { + let contents = efb.into_owned_string(); + assert_eq!(expected, contents,); + } + + fn expected_envfile(env_vars: &[(&str, &str)]) -> String { + let mut envfile = String::new(); + for (key, value) in env_vars { + envfile.push_str(key); + envfile.push('='); + envfile.push_str(value); + envfile.push('\n'); + } + envfile + } +} + fn assert_envfile_exists_in_testenv(testenv: TestEnv) { let envfile_path = testenv.envfile_path().to_owned(); From 88386acd35d38fe48a3fb7af357b8c0ce7eb7e75 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Fri, 12 Jul 2024 14:42:34 +0100 Subject: [PATCH 10/65] Test harness BOM handling --- dotenv/tests/integration/util/tests.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/dotenv/tests/integration/util/tests.rs b/dotenv/tests/integration/util/tests.rs index a6c0b40c..3262f455 100644 --- a/dotenv/tests/integration/util/tests.rs +++ b/dotenv/tests/integration/util/tests.rs @@ -125,6 +125,21 @@ mod testenv_init_with_envfile { }); } + #[test] + fn custom_bom_envfile_exists() { + let testenv = init_custom_bom_envfile_testenv(); + assert_envfile_exists_in_testenv(testenv); + } + + #[test] + fn custom_bom_envfile_loaded_vars_state() { + let testenv = init_custom_bom_envfile_testenv(); + test_in_env(testenv, || { + dotenv_wrap().expect(DOTENV_EXPECT); + assert_env_var(TEST_KEY, TEST_VALUE); + }); + } + fn init_default_envfile_testenv() -> TestEnv { let envfile = create_default_envfile(); TestEnv::init_with_envfile(envfile) @@ -138,6 +153,14 @@ mod testenv_init_with_envfile { fn init_empty_envfile_testenv() -> TestEnv { TestEnv::init_with_envfile([]) } + + fn init_custom_bom_envfile_testenv() -> TestEnv { + let mut efb = EnvFileBuilder::new(); + efb.add_key_value(TEST_KEY, TEST_VALUE); + efb.insert_utf8_bom(); + let envfile = efb.into_owned_string(); + TestEnv::init_with_envfile(envfile) + } } mod envfile_builder { From d872f141c7dca12ad7358e694df5cb7fa89dcf8d Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Sun, 14 Jul 2024 11:05:17 +0100 Subject: [PATCH 11/65] Move test harness tests to subdir --- dotenv/tests/integration/util/tests.rs | 291 ------------------ .../integration/util/tests/default_env.rs | 25 ++ .../integration/util/tests/envfile_builder.rs | 106 +++++++ dotenv/tests/integration/util/tests/mod.rs | 30 ++ .../tests/integration/util/tests/testenv.rs | 129 ++++++++ 5 files changed, 290 insertions(+), 291 deletions(-) delete mode 100644 dotenv/tests/integration/util/tests.rs create mode 100644 dotenv/tests/integration/util/tests/default_env.rs create mode 100644 dotenv/tests/integration/util/tests/envfile_builder.rs create mode 100644 dotenv/tests/integration/util/tests/mod.rs create mode 100644 dotenv/tests/integration/util/tests/testenv.rs diff --git a/dotenv/tests/integration/util/tests.rs b/dotenv/tests/integration/util/tests.rs deleted file mode 100644 index 3262f455..00000000 --- a/dotenv/tests/integration/util/tests.rs +++ /dev/null @@ -1,291 +0,0 @@ -use super::*; - -const CUSTOM_VARS: &[(&str, &str)] = &[ - ("CUSTOM_KEY_1", "CUSTOM_VALUE_1"), - ("CUSTOM_KEY_2", "CUSTOM_VALUE_2"), -]; - -const DOTENV_EXPECT: &str = "TestEnv should have .env file"; - -mod default_env { - use super::*; - - #[test] - fn vars_state() { - test_in_default_env(|| { - assert_env_var(TEST_EXISTING_KEY, TEST_EXISTING_VALUE); - assert_env_var_unset(TEST_KEY); - }); - } - - #[test] - fn envfile_exists() { - let testenv = TestEnv::default(); - assert_envfile_exists_in_testenv(testenv); - } - - #[test] - fn envfile_loaded_vars_state() { - test_in_default_env(|| { - dotenv_wrap().expect(DOTENV_EXPECT); - // dotenv() does not override existing var - assert_env_var(TEST_EXISTING_KEY, TEST_EXISTING_VALUE); - assert_env_var(TEST_KEY, TEST_VALUE); - }); - } -} - -mod testenv_init { - use super::*; - - #[test] - fn vars_state() { - let init_testenv = TestEnv::init(); - assert_default_keys_not_set_in_testenv(init_testenv); - } - - #[test] - fn no_envfile() { - let init_testenv = TestEnv::init(); - let envfile_path = init_testenv.envfile_path().to_owned(); - - test_in_env(init_testenv, || { - assert!(!envfile_path.exists()); - assert!(dotenv_wrap().is_err()); - }); - } -} - -mod testenv_init_with_envfile { - use super::*; - - #[test] - fn default_envfile_vars_state() { - let testenv = init_default_envfile_testenv(); - assert_default_keys_not_set_in_testenv(testenv); - } - - #[test] - fn default_envfile_exists() { - let testenv = init_default_envfile_testenv(); - assert_envfile_exists_in_testenv(testenv); - } - - #[test] - fn default_envfile_loaded_vars_state() { - let testenv = init_default_envfile_testenv(); - test_in_env(testenv, || { - dotenv_wrap().expect(DOTENV_EXPECT); - // dotenv() does not override existing var - // but existing key is not set in this testenv - assert_env_var(TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE); - assert_env_var(TEST_KEY, TEST_VALUE); - }); - } - - #[test] - fn custom_envfile_vars_state() { - let testenv = init_custom_envfile_testenv(); - test_in_env(testenv, || { - assert_default_keys_not_set(); - for (key, _) in CUSTOM_VARS { - assert_env_var_unset(key); - } - }); - } - - #[test] - fn custom_envfile_exists() { - let testenv = init_custom_envfile_testenv(); - assert_envfile_exists_in_testenv(testenv); - } - - #[test] - fn custom_envfile_loaded_vars_state() { - let testenv = init_custom_envfile_testenv(); - test_in_env(testenv, || { - dotenv_wrap().expect(DOTENV_EXPECT); - assert_default_keys_not_set(); - assert_env_vars(CUSTOM_VARS); - }); - } - - #[test] - fn empty_envfile_exists() { - let testenv = init_empty_envfile_testenv(); - assert_envfile_exists_in_testenv(testenv); - } - - #[test] - fn empty_envfile_loaded_vars_state() { - let testenv = init_empty_envfile_testenv(); - test_in_env(testenv, || { - dotenv_wrap().expect(DOTENV_EXPECT); - assert_default_keys_not_set(); - }); - } - - #[test] - fn custom_bom_envfile_exists() { - let testenv = init_custom_bom_envfile_testenv(); - assert_envfile_exists_in_testenv(testenv); - } - - #[test] - fn custom_bom_envfile_loaded_vars_state() { - let testenv = init_custom_bom_envfile_testenv(); - test_in_env(testenv, || { - dotenv_wrap().expect(DOTENV_EXPECT); - assert_env_var(TEST_KEY, TEST_VALUE); - }); - } - - fn init_default_envfile_testenv() -> TestEnv { - let envfile = create_default_envfile(); - TestEnv::init_with_envfile(envfile) - } - - fn init_custom_envfile_testenv() -> TestEnv { - let envfile = create_custom_envfile(CUSTOM_VARS); - TestEnv::init_with_envfile(envfile) - } - - fn init_empty_envfile_testenv() -> TestEnv { - TestEnv::init_with_envfile([]) - } - - fn init_custom_bom_envfile_testenv() -> TestEnv { - let mut efb = EnvFileBuilder::new(); - efb.add_key_value(TEST_KEY, TEST_VALUE); - efb.insert_utf8_bom(); - let envfile = efb.into_owned_string(); - TestEnv::init_with_envfile(envfile) - } -} - -mod envfile_builder { - use super::*; - - #[test] - fn new_builds_empty() { - let efb = EnvFileBuilder::new(); - assert_contents_empty(efb); - } - - #[test] - fn default_builds_empty() { - let efb = EnvFileBuilder::default(); - assert_contents_empty(efb); - } - - #[test] - fn add_key_empty_value() { - let mut efb = EnvFileBuilder::new(); - efb.add_key_value(TEST_KEY, ""); - let expected = format!("{}=\n", TEST_KEY); - assert_contents_str(efb, &expected); - } - - #[test] - fn add_key_value() { - let mut efb = EnvFileBuilder::new(); - efb.add_key_value(TEST_KEY, TEST_VALUE); - let expected = format!("{}={}\n", TEST_KEY, TEST_VALUE); - assert_contents_str(efb, &expected); - } - - #[test] - fn add_multiple_key_values() { - let mut efb = EnvFileBuilder::new(); - efb.add_key_value(TEST_KEY, TEST_VALUE); - efb.add_key_value(TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE); - let expected = expected_envfile(&[ - (TEST_KEY, TEST_VALUE), - (TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE), - ]); - assert_contents_str(efb, &expected); - } - - #[test] - fn add_vars() { - let mut efb = EnvFileBuilder::new(); - efb.add_vars(CUSTOM_VARS); - let expected = expected_envfile(CUSTOM_VARS); - assert_contents_str(efb, &expected); - } - - #[test] - fn add_str() { - let mut efb = EnvFileBuilder::new(); - efb.add_str("test"); - assert_contents_str(efb, "test"); - } - - #[test] - fn add_bytes() { - let mut efb = EnvFileBuilder::new(); - efb.add_bytes(b"test"); - assert_contents_str(efb, "test"); - } - - #[test] - fn add_byte() { - let mut efb = EnvFileBuilder::new(); - efb.add_byte(b't'); - assert_contents_str(efb, "t"); - } - - #[test] - fn insert_utf8_bom() { - let mut efb = EnvFileBuilder::new(); - efb.add_str("test"); - efb.insert_utf8_bom(); - assert_contents_str(efb, "\u{FEFF}test"); - } - - #[test] - fn add_strln() { - let mut efb = EnvFileBuilder::new(); - efb.add_strln("test"); - assert_contents_str(efb, "test\n"); - } - - fn assert_contents_empty(efb: EnvFileBuilder) { - let contents = efb.into_owned_bytes(); - assert!(contents.is_empty()); - } - - fn assert_contents_str(efb: EnvFileBuilder, expected: &str) { - let contents = efb.into_owned_string(); - assert_eq!(expected, contents,); - } - - fn expected_envfile(env_vars: &[(&str, &str)]) -> String { - let mut envfile = String::new(); - for (key, value) in env_vars { - envfile.push_str(key); - envfile.push('='); - envfile.push_str(value); - envfile.push('\n'); - } - envfile - } -} - -fn assert_envfile_exists_in_testenv(testenv: TestEnv) { - let envfile_path = testenv.envfile_path().to_owned(); - - test_in_env(testenv, || { - assert!(envfile_path.exists()); - assert!(dotenv_wrap().is_ok()); - }); -} - -fn assert_default_keys_not_set_in_testenv(testenv: TestEnv) { - test_in_env(testenv, assert_default_keys_not_set); -} - -fn assert_default_keys_not_set() { - assert_env_var_unset(TEST_EXISTING_KEY); - assert_env_var_unset(TEST_KEY); -} diff --git a/dotenv/tests/integration/util/tests/default_env.rs b/dotenv/tests/integration/util/tests/default_env.rs new file mode 100644 index 00000000..de6e8e96 --- /dev/null +++ b/dotenv/tests/integration/util/tests/default_env.rs @@ -0,0 +1,25 @@ +use super::*; + +#[test] +fn vars_state() { + test_in_default_env(|| { + assert_env_var(TEST_EXISTING_KEY, TEST_EXISTING_VALUE); + assert_env_var_unset(TEST_KEY); + }); +} + +#[test] +fn envfile_exists() { + let testenv = TestEnv::default(); + assert_envfile_exists_in_testenv(testenv); +} + +#[test] +fn envfile_loaded_vars_state() { + test_in_default_env(|| { + dotenv_wrap().expect(DOTENV_EXPECT); + // dotenv() does not override existing var + assert_env_var(TEST_EXISTING_KEY, TEST_EXISTING_VALUE); + assert_env_var(TEST_KEY, TEST_VALUE); + }); +} diff --git a/dotenv/tests/integration/util/tests/envfile_builder.rs b/dotenv/tests/integration/util/tests/envfile_builder.rs new file mode 100644 index 00000000..4bd431a2 --- /dev/null +++ b/dotenv/tests/integration/util/tests/envfile_builder.rs @@ -0,0 +1,106 @@ +use super::*; + +#[test] +fn new_builds_empty() { + let efb = EnvFileBuilder::new(); + assert_contents_empty(efb); +} + +#[test] +fn default_builds_empty() { + let efb = EnvFileBuilder::default(); + assert_contents_empty(efb); +} + +#[test] +fn add_key_empty_value() { + let mut efb = EnvFileBuilder::new(); + efb.add_key_value(TEST_KEY, ""); + let expected = format!("{}=\n", TEST_KEY); + assert_contents_str(efb, &expected); +} + +#[test] +fn add_key_value() { + let mut efb = EnvFileBuilder::new(); + efb.add_key_value(TEST_KEY, TEST_VALUE); + let expected = format!("{}={}\n", TEST_KEY, TEST_VALUE); + assert_contents_str(efb, &expected); +} + +#[test] +fn add_multiple_key_values() { + let mut efb = EnvFileBuilder::new(); + efb.add_key_value(TEST_KEY, TEST_VALUE); + efb.add_key_value(TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE); + let expected = expected_envfile(&[ + (TEST_KEY, TEST_VALUE), + (TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE), + ]); + assert_contents_str(efb, &expected); +} + +#[test] +fn add_vars() { + let mut efb = EnvFileBuilder::new(); + efb.add_vars(CUSTOM_VARS); + let expected = expected_envfile(CUSTOM_VARS); + assert_contents_str(efb, &expected); +} + +#[test] +fn add_str() { + let mut efb = EnvFileBuilder::new(); + efb.add_str("test"); + assert_contents_str(efb, "test"); +} + +#[test] +fn add_bytes() { + let mut efb = EnvFileBuilder::new(); + efb.add_bytes(b"test"); + assert_contents_str(efb, "test"); +} + +#[test] +fn add_byte() { + let mut efb = EnvFileBuilder::new(); + efb.add_byte(b't'); + assert_contents_str(efb, "t"); +} + +#[test] +fn insert_utf8_bom() { + let mut efb = EnvFileBuilder::new(); + efb.add_str("test"); + efb.insert_utf8_bom(); + assert_contents_str(efb, "\u{FEFF}test"); +} + +#[test] +fn add_strln() { + let mut efb = EnvFileBuilder::new(); + efb.add_strln("test"); + assert_contents_str(efb, "test\n"); +} + +fn assert_contents_empty(efb: EnvFileBuilder) { + let contents = efb.into_owned_bytes(); + assert!(contents.is_empty()); +} + +fn assert_contents_str(efb: EnvFileBuilder, expected: &str) { + let contents = efb.into_owned_string(); + assert_eq!(expected, contents,); +} + +fn expected_envfile(env_vars: &[(&str, &str)]) -> String { + let mut envfile = String::new(); + for (key, value) in env_vars { + envfile.push_str(key); + envfile.push('='); + envfile.push_str(value); + envfile.push('\n'); + } + envfile +} diff --git a/dotenv/tests/integration/util/tests/mod.rs b/dotenv/tests/integration/util/tests/mod.rs new file mode 100644 index 00000000..8f7c34ce --- /dev/null +++ b/dotenv/tests/integration/util/tests/mod.rs @@ -0,0 +1,30 @@ +use super::*; + +mod default_env; +mod envfile_builder; +mod testenv; + +const CUSTOM_VARS: &[(&str, &str)] = &[ + ("CUSTOM_KEY_1", "CUSTOM_VALUE_1"), + ("CUSTOM_KEY_2", "CUSTOM_VALUE_2"), +]; + +const DOTENV_EXPECT: &str = "TestEnv should have .env file"; + +fn assert_envfile_exists_in_testenv(testenv: TestEnv) { + let envfile_path = testenv.envfile_path().to_owned(); + + test_in_env(testenv, || { + assert!(envfile_path.exists()); + assert!(dotenv_wrap().is_ok()); + }); +} + +fn assert_default_keys_not_set_in_testenv(testenv: TestEnv) { + test_in_env(testenv, assert_default_keys_not_set); +} + +fn assert_default_keys_not_set() { + assert_env_var_unset(TEST_EXISTING_KEY); + assert_env_var_unset(TEST_KEY); +} diff --git a/dotenv/tests/integration/util/tests/testenv.rs b/dotenv/tests/integration/util/tests/testenv.rs new file mode 100644 index 00000000..fb24edfe --- /dev/null +++ b/dotenv/tests/integration/util/tests/testenv.rs @@ -0,0 +1,129 @@ +use super::*; + +mod init { + use super::*; + + #[test] + fn vars_state() { + let init_testenv = TestEnv::init(); + assert_default_keys_not_set_in_testenv(init_testenv); + } + + #[test] + fn no_envfile() { + let init_testenv = TestEnv::init(); + let envfile_path = init_testenv.envfile_path().to_owned(); + + test_in_env(init_testenv, || { + assert!(!envfile_path.exists()); + assert!(dotenv_wrap().is_err()); + }); + } +} + +mod init_with_envfile { + use super::*; + + #[test] + fn default_envfile_vars_state() { + let testenv = init_default_envfile_testenv(); + assert_default_keys_not_set_in_testenv(testenv); + } + + #[test] + fn default_envfile_exists() { + let testenv = init_default_envfile_testenv(); + assert_envfile_exists_in_testenv(testenv); + } + + #[test] + fn default_envfile_loaded_vars_state() { + let testenv = init_default_envfile_testenv(); + test_in_env(testenv, || { + dotenv_wrap().expect(DOTENV_EXPECT); + // dotenv() does not override existing var + // but existing key is not set in this testenv + assert_env_var(TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE); + assert_env_var(TEST_KEY, TEST_VALUE); + }); + } + + #[test] + fn custom_envfile_vars_state() { + let testenv = init_custom_envfile_testenv(); + test_in_env(testenv, || { + assert_default_keys_not_set(); + for (key, _) in CUSTOM_VARS { + assert_env_var_unset(key); + } + }); + } + + #[test] + fn custom_envfile_exists() { + let testenv = init_custom_envfile_testenv(); + assert_envfile_exists_in_testenv(testenv); + } + + #[test] + fn custom_envfile_loaded_vars_state() { + let testenv = init_custom_envfile_testenv(); + test_in_env(testenv, || { + dotenv_wrap().expect(DOTENV_EXPECT); + assert_default_keys_not_set(); + assert_env_vars(CUSTOM_VARS); + }); + } + + #[test] + fn empty_envfile_exists() { + let testenv = init_empty_envfile_testenv(); + assert_envfile_exists_in_testenv(testenv); + } + + #[test] + fn empty_envfile_loaded_vars_state() { + let testenv = init_empty_envfile_testenv(); + test_in_env(testenv, || { + dotenv_wrap().expect(DOTENV_EXPECT); + assert_default_keys_not_set(); + }); + } + + #[test] + fn custom_bom_envfile_exists() { + let testenv = init_custom_bom_envfile_testenv(); + assert_envfile_exists_in_testenv(testenv); + } + + #[test] + fn custom_bom_envfile_loaded_vars_state() { + let testenv = init_custom_bom_envfile_testenv(); + test_in_env(testenv, || { + dotenv_wrap().expect(DOTENV_EXPECT); + assert_env_var(TEST_KEY, TEST_VALUE); + }); + } + + fn init_default_envfile_testenv() -> TestEnv { + let envfile = create_default_envfile(); + TestEnv::init_with_envfile(envfile) + } + + fn init_custom_envfile_testenv() -> TestEnv { + let envfile = create_custom_envfile(CUSTOM_VARS); + TestEnv::init_with_envfile(envfile) + } + + fn init_empty_envfile_testenv() -> TestEnv { + TestEnv::init_with_envfile([]) + } + + fn init_custom_bom_envfile_testenv() -> TestEnv { + let mut efb = EnvFileBuilder::new(); + efb.add_key_value(TEST_KEY, TEST_VALUE); + efb.insert_utf8_bom(); + let envfile = efb.into_owned_string(); + TestEnv::init_with_envfile(envfile) + } +} From e870d600328728e7051adf311675316a69d36c3b Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Sun, 14 Jul 2024 11:31:25 +0100 Subject: [PATCH 12/65] Clean harness utility layout --- dotenv/tests/integration/dotenv.rs | 4 +- dotenv/tests/integration/util/assertions.rs | 62 +++++++++++++ dotenv/tests/integration/util/envfile.rs | 37 +++++++- dotenv/tests/integration/util/mod.rs | 92 ++----------------- dotenv/tests/integration/util/testenv.rs | 16 ++-- .../integration/util/tests/default_env.rs | 8 +- .../integration/util/tests/envfile_builder.rs | 16 ++-- dotenv/tests/integration/util/tests/mod.rs | 7 +- .../tests/integration/util/tests/testenv.rs | 14 +-- 9 files changed, 137 insertions(+), 119 deletions(-) create mode 100644 dotenv/tests/integration/util/assertions.rs diff --git a/dotenv/tests/integration/dotenv.rs b/dotenv/tests/integration/dotenv.rs index 966fbf23..a47fc423 100644 --- a/dotenv/tests/integration/dotenv.rs +++ b/dotenv/tests/integration/dotenv.rs @@ -4,7 +4,7 @@ use crate::util::*; fn dotenv_ok_default_env() { test_in_default_env(|| { dotenvy::dotenv().ok(); - assert_env_var(TEST_EXISTING_KEY, TEST_EXISTING_VALUE); - assert_env_var(TEST_KEY, TEST_VALUE); + assert_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); + assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); }); } diff --git a/dotenv/tests/integration/util/assertions.rs b/dotenv/tests/integration/util/assertions.rs new file mode 100644 index 00000000..fbaff16b --- /dev/null +++ b/dotenv/tests/integration/util/assertions.rs @@ -0,0 +1,62 @@ +use super::*; +use std::env::{self, VarError}; + +/// Assert that a slice of environment variables are set and have the expected +/// values. +/// +/// ## Arguments +/// +/// * `vars` - A slice of key-expected value tuples +/// +/// ## Examples +/// +/// ```rust +/// assert_env_vars(&[ +/// ("DEFAULT_TEST_KEY", "default_test_val"), +/// ("DEFAULT_EXISTING_KEY", "loaded_from_env"), +/// ]); +/// ``` +pub fn assert_env_vars(vars: &[(&str, &str)]) { + for (key, expected) in vars { + assert_env_var(key, expected); + } +} + +/// Assert that an environment variable is set and has the expected value. +pub fn assert_env_var(key: &str, expected: &str) { + match env::var(key) { + Ok(actual) => assert_eq!( + expected, actual, + "\n\nFor Environment Variable `{}`:\n EXPECTED: `{}`\n ACTUAL: `{}`\n", + key, expected, actual + ), + Err(VarError::NotPresent) => panic!("env var `{}` not found", key), + Err(VarError::NotUnicode(val)) => panic!( + "env var `{}` currently has invalid unicode: `{}`", + key, + val.to_string_lossy() + ), + } +} + +/// Assert that an environment variable is not currently set. +pub fn assert_env_var_unset(key: &str) { + match env::var(key) { + Ok(actual) => panic!( + "env var `{}` should not be set, currently it is: `{}`", + key, actual + ), + Err(VarError::NotUnicode(val)) => panic!( + "env var `{}` should not be set, currently has invalid unicode: `{}`", + key, + val.to_string_lossy() + ), + _ => (), + } +} + +/// Assert that the default testing environment variables are not set. +pub fn assert_default_keys_unset() { + assert_env_var_unset(DEFAULT_EXISTING_KEY); + assert_env_var_unset(DEFAULT_TEST_KEY); +} diff --git a/dotenv/tests/integration/util/envfile.rs b/dotenv/tests/integration/util/envfile.rs index 4516823c..76f93d42 100644 --- a/dotenv/tests/integration/util/envfile.rs +++ b/dotenv/tests/integration/util/envfile.rs @@ -1,4 +1,39 @@ -/// Create envfile contents for testing +use super::*; + +#[inline(always)] +pub fn create_default_envfile() -> String { + format!( + "{}={}\n{}={}", + DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE, DEFAULT_EXISTING_KEY, TEST_OVERRIDING_VALUE + ) +} + +/// Invalid due to missing `=` between key and value. +#[inline(always)] +pub fn create_invalid_envfile() -> String { + format!( + "{}{}\n{}{}", + DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE, DEFAULT_EXISTING_KEY, TEST_OVERRIDING_VALUE + ) +} + +/// Create an envfile with custom key-value pairs. +/// +/// ## Example +/// +/// ```rust +/// let contents = create_custom_envfile(&[ +/// ("CUSTOM_KEY", "test_val"), +/// ("ANOTHER_KEY", "another_val"), +/// ]); +/// ``` +pub fn create_custom_envfile(env_vars: &[(&str, &str)]) -> String { + let mut efb = EnvFileBuilder::new(); + efb.add_vars(env_vars); + efb.into_owned_string() +} + +/// Advanced test-envfile constructor. /// /// Represented as bytes to allow for advanced manipulation and BOM testing. #[derive(Debug, Default)] diff --git a/dotenv/tests/integration/util/mod.rs b/dotenv/tests/integration/util/mod.rs index 98423b43..4bbcb065 100644 --- a/dotenv/tests/integration/util/mod.rs +++ b/dotenv/tests/integration/util/mod.rs @@ -1,96 +1,22 @@ -use std::env::{self, VarError}; - +mod assertions; mod envfile; mod testenv; mod tests; mod wrapper; -pub use envfile::EnvFileBuilder; -pub use testenv::{test_in_default_env, test_in_env, KeyVal, TestEnv}; +pub use assertions::*; +pub use envfile::*; +pub use testenv::*; pub use wrapper::*; /// Default key used in envfile -pub const TEST_KEY: &str = "TESTKEY"; +pub const DEFAULT_TEST_KEY: &str = "DEFAULT_TEST_KEY"; /// Default value used in envfile -pub const TEST_VALUE: &str = "test_val"; +pub const DEFAULT_TEST_VALUE: &str = "default_test_val"; /// Default existing key set before test is run -pub const TEST_EXISTING_KEY: &str = "TEST_EXISTING_KEY"; +pub const DEFAULT_EXISTING_KEY: &str = "DEFAULT_EXISTING_KEY"; /// Default existing value set before test is run -pub const TEST_EXISTING_VALUE: &str = "from_env"; +pub const DEFAULT_EXISTING_VALUE: &str = "loaded_from_env"; /// Default overriding value in envfile -pub const TEST_OVERRIDING_VALUE: &str = "from_file"; - -#[inline(always)] -pub fn create_default_envfile() -> String { - format!( - "{}={}\n{}={}", - TEST_KEY, TEST_VALUE, TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE - ) -} - -/// missing equals -#[inline(always)] -pub fn create_invalid_envfile() -> String { - format!( - "{}{}\n{}{}", - TEST_KEY, TEST_VALUE, TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE - ) -} - -pub fn create_custom_envfile(env_vars: &[(&str, &str)]) -> String { - let mut efb = EnvFileBuilder::new(); - efb.add_vars(env_vars); - efb.into_owned_string() -} - -/// Assert that a slice of environment variables are set and have the expected -/// values. -/// -/// ## Arguments -/// -/// * `vars` - A slice of key-expected value tuples -/// -/// ## Examples -/// -/// ```rust -/// assert_env_vars(&[("TESTKEY", "test_val"), ("TEST_EXISTING_KEY", "from_env")]); -/// ``` -pub fn assert_env_vars(vars: &[(&str, &str)]) { - for (key, expected) in vars { - assert_env_var(key, expected); - } -} - -/// Assert that an environment variable is set and has the expected value. -pub fn assert_env_var(key: &str, expected: &str) { - match env::var(key) { - Ok(actual) => assert_eq!( - expected, actual, - "\n\nFor Environment Variable `{}`:\n EXPECTED: `{}`\n ACTUAL: `{}`\n", - key, expected, actual - ), - Err(VarError::NotPresent) => panic!("env var `{}` not found", key), - Err(VarError::NotUnicode(val)) => panic!( - "env var `{}` currently has invalid unicode: `{}`", - key, - val.to_string_lossy() - ), - } -} - -/// Assert that an environment variable is not currently set. -pub fn assert_env_var_unset(key: &str) { - match env::var(key) { - Ok(actual) => panic!( - "env var `{}` should not be set, currently it is: `{}`", - key, actual - ), - Err(VarError::NotUnicode(val)) => panic!( - "env var `{}` should not be set, currently has invalid unicode: `{}`", - key, - val.to_string_lossy() - ), - _ => (), - } -} +pub const TEST_OVERRIDING_VALUE: &str = "loaded_from_file"; diff --git a/dotenv/tests/integration/util/testenv.rs b/dotenv/tests/integration/util/testenv.rs index f085f2f4..da1f195a 100644 --- a/dotenv/tests/integration/util/testenv.rs +++ b/dotenv/tests/integration/util/testenv.rs @@ -1,4 +1,4 @@ -use super::{create_default_envfile, TEST_EXISTING_KEY, TEST_EXISTING_VALUE}; +use super::{create_default_envfile, DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE}; use once_cell::sync::OnceCell; use std::{ collections::HashMap, @@ -68,15 +68,15 @@ where /// environment. /// /// The default testing environment sets an existing environment variable -/// `TEST_EXISTING_KEY`, which is set to `from_env`. It also creates a `.env` -/// file with the two lines: +/// `DEFAULT_EXISTING_KEY`, which is set to `loaded_from_env`. It also creates a +/// `.env` file with the two lines: /// /// ```ini -/// TESTKEY=test_val -/// TEST_EXISTING_KEY=from_file +/// DEFAULT_TEST_KEY=default_test_val +/// DEFAULT_EXISTING_KEY=loaded_from_file /// ``` /// -/// Notice that file has the potential to override `TEST_EXISTING_KEY` depending +/// Notice that file has the potential to override `DEFAULT_EXISTING_KEY` depending /// on the what's being tested. pub fn test_in_default_env(test: F) where @@ -255,8 +255,8 @@ impl Default for TestEnv { let temp_dir = tempdir().expect("create tempdir"); let work_dir = temp_dir.path().to_owned(); let env_vars = vec![KeyVal { - key: TEST_EXISTING_KEY.into(), - value: TEST_EXISTING_VALUE.into(), + key: DEFAULT_EXISTING_KEY.into(), + value: DEFAULT_EXISTING_VALUE.into(), }]; let envfile_contents = Some(create_default_envfile().into()); let envfile_path = work_dir.join(".env"); diff --git a/dotenv/tests/integration/util/tests/default_env.rs b/dotenv/tests/integration/util/tests/default_env.rs index de6e8e96..babbf1b1 100644 --- a/dotenv/tests/integration/util/tests/default_env.rs +++ b/dotenv/tests/integration/util/tests/default_env.rs @@ -3,8 +3,8 @@ use super::*; #[test] fn vars_state() { test_in_default_env(|| { - assert_env_var(TEST_EXISTING_KEY, TEST_EXISTING_VALUE); - assert_env_var_unset(TEST_KEY); + assert_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); + assert_env_var_unset(DEFAULT_TEST_KEY); }); } @@ -19,7 +19,7 @@ fn envfile_loaded_vars_state() { test_in_default_env(|| { dotenv_wrap().expect(DOTENV_EXPECT); // dotenv() does not override existing var - assert_env_var(TEST_EXISTING_KEY, TEST_EXISTING_VALUE); - assert_env_var(TEST_KEY, TEST_VALUE); + assert_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); + assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); }); } diff --git a/dotenv/tests/integration/util/tests/envfile_builder.rs b/dotenv/tests/integration/util/tests/envfile_builder.rs index 4bd431a2..91f24a94 100644 --- a/dotenv/tests/integration/util/tests/envfile_builder.rs +++ b/dotenv/tests/integration/util/tests/envfile_builder.rs @@ -15,27 +15,27 @@ fn default_builds_empty() { #[test] fn add_key_empty_value() { let mut efb = EnvFileBuilder::new(); - efb.add_key_value(TEST_KEY, ""); - let expected = format!("{}=\n", TEST_KEY); + efb.add_key_value(DEFAULT_TEST_KEY, ""); + let expected = format!("{}=\n", DEFAULT_TEST_KEY); assert_contents_str(efb, &expected); } #[test] fn add_key_value() { let mut efb = EnvFileBuilder::new(); - efb.add_key_value(TEST_KEY, TEST_VALUE); - let expected = format!("{}={}\n", TEST_KEY, TEST_VALUE); + efb.add_key_value(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); + let expected = format!("{}={}\n", DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); assert_contents_str(efb, &expected); } #[test] fn add_multiple_key_values() { let mut efb = EnvFileBuilder::new(); - efb.add_key_value(TEST_KEY, TEST_VALUE); - efb.add_key_value(TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE); + efb.add_key_value(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); + efb.add_key_value(DEFAULT_EXISTING_KEY, TEST_OVERRIDING_VALUE); let expected = expected_envfile(&[ - (TEST_KEY, TEST_VALUE), - (TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE), + (DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE), + (DEFAULT_EXISTING_KEY, TEST_OVERRIDING_VALUE), ]); assert_contents_str(efb, &expected); } diff --git a/dotenv/tests/integration/util/tests/mod.rs b/dotenv/tests/integration/util/tests/mod.rs index 8f7c34ce..464593be 100644 --- a/dotenv/tests/integration/util/tests/mod.rs +++ b/dotenv/tests/integration/util/tests/mod.rs @@ -21,10 +21,5 @@ fn assert_envfile_exists_in_testenv(testenv: TestEnv) { } fn assert_default_keys_not_set_in_testenv(testenv: TestEnv) { - test_in_env(testenv, assert_default_keys_not_set); -} - -fn assert_default_keys_not_set() { - assert_env_var_unset(TEST_EXISTING_KEY); - assert_env_var_unset(TEST_KEY); + test_in_env(testenv, assert_default_keys_unset); } diff --git a/dotenv/tests/integration/util/tests/testenv.rs b/dotenv/tests/integration/util/tests/testenv.rs index fb24edfe..fbb2d01f 100644 --- a/dotenv/tests/integration/util/tests/testenv.rs +++ b/dotenv/tests/integration/util/tests/testenv.rs @@ -43,8 +43,8 @@ mod init_with_envfile { dotenv_wrap().expect(DOTENV_EXPECT); // dotenv() does not override existing var // but existing key is not set in this testenv - assert_env_var(TEST_EXISTING_KEY, TEST_OVERRIDING_VALUE); - assert_env_var(TEST_KEY, TEST_VALUE); + assert_env_var(DEFAULT_EXISTING_KEY, TEST_OVERRIDING_VALUE); + assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); }); } @@ -52,7 +52,7 @@ mod init_with_envfile { fn custom_envfile_vars_state() { let testenv = init_custom_envfile_testenv(); test_in_env(testenv, || { - assert_default_keys_not_set(); + assert_default_keys_unset(); for (key, _) in CUSTOM_VARS { assert_env_var_unset(key); } @@ -70,7 +70,7 @@ mod init_with_envfile { let testenv = init_custom_envfile_testenv(); test_in_env(testenv, || { dotenv_wrap().expect(DOTENV_EXPECT); - assert_default_keys_not_set(); + assert_default_keys_unset(); assert_env_vars(CUSTOM_VARS); }); } @@ -86,7 +86,7 @@ mod init_with_envfile { let testenv = init_empty_envfile_testenv(); test_in_env(testenv, || { dotenv_wrap().expect(DOTENV_EXPECT); - assert_default_keys_not_set(); + assert_default_keys_unset(); }); } @@ -101,7 +101,7 @@ mod init_with_envfile { let testenv = init_custom_bom_envfile_testenv(); test_in_env(testenv, || { dotenv_wrap().expect(DOTENV_EXPECT); - assert_env_var(TEST_KEY, TEST_VALUE); + assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); }); } @@ -121,7 +121,7 @@ mod init_with_envfile { fn init_custom_bom_envfile_testenv() -> TestEnv { let mut efb = EnvFileBuilder::new(); - efb.add_key_value(TEST_KEY, TEST_VALUE); + efb.add_key_value(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); efb.insert_utf8_bom(); let envfile = efb.into_owned_string(); TestEnv::init_with_envfile(envfile) From 799cfb66c4f664cb101de104d8e1a1a5004a2a68 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Sun, 14 Jul 2024 12:08:11 +0100 Subject: [PATCH 13/65] Move test harness to separate crate Allow both dotenvy and dotenvy_macro to make use of the test harness --- Cargo.toml | 3 ++- dotenv/Cargo.toml | 2 +- dotenv/tests/integration/dotenv.rs | 2 +- dotenv/tests/integration/main.rs | 2 -- test_util/Cargo.toml | 20 +++++++++++++++++++ .../util => test_util/src}/assertions.rs | 5 +++-- .../util => test_util/src}/envfile.rs | 4 +++- .../util/mod.rs => test_util/src/lib.rs | 4 +++- .../util => test_util/src}/testenv.rs | 0 .../src}/tests/default_env.rs | 0 .../src}/tests/envfile_builder.rs | 0 .../util => test_util/src}/tests/mod.rs | 0 .../util => test_util/src}/tests/testenv.rs | 0 .../util => test_util/src}/wrapper.rs | 0 14 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 test_util/Cargo.toml rename {dotenv/tests/integration/util => test_util/src}/assertions.rs (95%) rename {dotenv/tests/integration/util => test_util/src}/envfile.rs (96%) rename dotenv/tests/integration/util/mod.rs => test_util/src/lib.rs (97%) rename {dotenv/tests/integration/util => test_util/src}/testenv.rs (100%) rename {dotenv/tests/integration/util => test_util/src}/tests/default_env.rs (100%) rename {dotenv/tests/integration/util => test_util/src}/tests/envfile_builder.rs (100%) rename {dotenv/tests/integration/util => test_util/src}/tests/mod.rs (100%) rename {dotenv/tests/integration/util => test_util/src}/tests/testenv.rs (100%) rename {dotenv/tests/integration/util => test_util/src}/wrapper.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 68bfba33..d5ca61b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,4 @@ [workspace] +resolver = "2" -members = ["dotenv", "dotenv_codegen"] +members = ["dotenv", "dotenv_codegen", "test_util"] diff --git a/dotenv/Cargo.toml b/dotenv/Cargo.toml index 51b32a3e..84c41eb8 100644 --- a/dotenv/Cargo.toml +++ b/dotenv/Cargo.toml @@ -29,8 +29,8 @@ required-features = ["cli"] clap = { version = "4.3.11", optional = true } [dev-dependencies] +dotenvy_test_util = { path = "../test_util", version = "0.1.0" } tempfile = "3.3.0" -once_cell = "1.16.0" [features] cli = ["clap"] diff --git a/dotenv/tests/integration/dotenv.rs b/dotenv/tests/integration/dotenv.rs index a47fc423..3c95d826 100644 --- a/dotenv/tests/integration/dotenv.rs +++ b/dotenv/tests/integration/dotenv.rs @@ -1,4 +1,4 @@ -use crate::util::*; +use dotenvy_test_util::*; #[test] fn dotenv_ok_default_env() { diff --git a/dotenv/tests/integration/main.rs b/dotenv/tests/integration/main.rs index a61ebe72..8c74edd4 100644 --- a/dotenv/tests/integration/main.rs +++ b/dotenv/tests/integration/main.rs @@ -1,3 +1 @@ -pub mod util; - mod dotenv; diff --git a/test_util/Cargo.toml b/test_util/Cargo.toml new file mode 100644 index 00000000..8368d21e --- /dev/null +++ b/test_util/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "dotenvy_test_util" +version = "0.1.0" +authors = [ + "Christopher Morton ", +] +description = "Test utilities for dotenvy" +homepage = "https://github.com/allan2/dotenvy" +readme = "README.md" +keywords = ["dotenv", "testing", "utility", "util", "harness"] +categories = ["development-tools", "development-tools::testing"] +license = "MIT" +repository = "https://github.com/allan2/dotenvy" +edition = "2021" +rust-version = "1.68.0" + +[dependencies] +dotenvy = { path = "../dotenv", version = "0.15.7" } +tempfile = "3.3.0" +once_cell = "1.16.0" diff --git a/dotenv/tests/integration/util/assertions.rs b/test_util/src/assertions.rs similarity index 95% rename from dotenv/tests/integration/util/assertions.rs rename to test_util/src/assertions.rs index fbaff16b..fa353d0c 100644 --- a/dotenv/tests/integration/util/assertions.rs +++ b/test_util/src/assertions.rs @@ -8,9 +8,10 @@ use std::env::{self, VarError}; /// /// * `vars` - A slice of key-expected value tuples /// -/// ## Examples +/// ## Example /// -/// ```rust +/// ```no_run +/// # use dotenvy_test_util::assert_env_vars; /// assert_env_vars(&[ /// ("DEFAULT_TEST_KEY", "default_test_val"), /// ("DEFAULT_EXISTING_KEY", "loaded_from_env"), diff --git a/dotenv/tests/integration/util/envfile.rs b/test_util/src/envfile.rs similarity index 96% rename from dotenv/tests/integration/util/envfile.rs rename to test_util/src/envfile.rs index 76f93d42..57f2675d 100644 --- a/dotenv/tests/integration/util/envfile.rs +++ b/test_util/src/envfile.rs @@ -21,11 +21,13 @@ pub fn create_invalid_envfile() -> String { /// /// ## Example /// -/// ```rust +/// ```no_run +/// # use dotenvy_test_util::create_custom_envfile; /// let contents = create_custom_envfile(&[ /// ("CUSTOM_KEY", "test_val"), /// ("ANOTHER_KEY", "another_val"), /// ]); +/// assert_eq!(contents, "CUSTOM_KEY=test_val\nANOTHER_KEY=another_val\n"); /// ``` pub fn create_custom_envfile(env_vars: &[(&str, &str)]) -> String { let mut efb = EnvFileBuilder::new(); diff --git a/dotenv/tests/integration/util/mod.rs b/test_util/src/lib.rs similarity index 97% rename from dotenv/tests/integration/util/mod.rs rename to test_util/src/lib.rs index 4bbcb065..33c10833 100644 --- a/dotenv/tests/integration/util/mod.rs +++ b/test_util/src/lib.rs @@ -1,9 +1,11 @@ mod assertions; mod envfile; mod testenv; -mod tests; mod wrapper; +#[cfg(test)] +mod tests; + pub use assertions::*; pub use envfile::*; pub use testenv::*; diff --git a/dotenv/tests/integration/util/testenv.rs b/test_util/src/testenv.rs similarity index 100% rename from dotenv/tests/integration/util/testenv.rs rename to test_util/src/testenv.rs diff --git a/dotenv/tests/integration/util/tests/default_env.rs b/test_util/src/tests/default_env.rs similarity index 100% rename from dotenv/tests/integration/util/tests/default_env.rs rename to test_util/src/tests/default_env.rs diff --git a/dotenv/tests/integration/util/tests/envfile_builder.rs b/test_util/src/tests/envfile_builder.rs similarity index 100% rename from dotenv/tests/integration/util/tests/envfile_builder.rs rename to test_util/src/tests/envfile_builder.rs diff --git a/dotenv/tests/integration/util/tests/mod.rs b/test_util/src/tests/mod.rs similarity index 100% rename from dotenv/tests/integration/util/tests/mod.rs rename to test_util/src/tests/mod.rs diff --git a/dotenv/tests/integration/util/tests/testenv.rs b/test_util/src/tests/testenv.rs similarity index 100% rename from dotenv/tests/integration/util/tests/testenv.rs rename to test_util/src/tests/testenv.rs diff --git a/dotenv/tests/integration/util/wrapper.rs b/test_util/src/wrapper.rs similarity index 100% rename from dotenv/tests/integration/util/wrapper.rs rename to test_util/src/wrapper.rs From cddf9bddeebf6ca45eb6c905c97400d9919c316d Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Sun, 14 Jul 2024 12:28:59 +0100 Subject: [PATCH 14/65] Fix const naming convension --- test_util/src/envfile.rs | 4 ++-- test_util/src/lib.rs | 2 +- test_util/src/tests/envfile_builder.rs | 4 ++-- test_util/src/tests/testenv.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test_util/src/envfile.rs b/test_util/src/envfile.rs index 57f2675d..7354327c 100644 --- a/test_util/src/envfile.rs +++ b/test_util/src/envfile.rs @@ -4,7 +4,7 @@ use super::*; pub fn create_default_envfile() -> String { format!( "{}={}\n{}={}", - DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE, DEFAULT_EXISTING_KEY, TEST_OVERRIDING_VALUE + DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE, DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE ) } @@ -13,7 +13,7 @@ pub fn create_default_envfile() -> String { pub fn create_invalid_envfile() -> String { format!( "{}{}\n{}{}", - DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE, DEFAULT_EXISTING_KEY, TEST_OVERRIDING_VALUE + DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE, DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE ) } diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index 33c10833..640e0db6 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -21,4 +21,4 @@ pub const DEFAULT_EXISTING_KEY: &str = "DEFAULT_EXISTING_KEY"; /// Default existing value set before test is run pub const DEFAULT_EXISTING_VALUE: &str = "loaded_from_env"; /// Default overriding value in envfile -pub const TEST_OVERRIDING_VALUE: &str = "loaded_from_file"; +pub const DEFAULT_OVERRIDING_VALUE: &str = "loaded_from_file"; diff --git a/test_util/src/tests/envfile_builder.rs b/test_util/src/tests/envfile_builder.rs index 91f24a94..cd720fed 100644 --- a/test_util/src/tests/envfile_builder.rs +++ b/test_util/src/tests/envfile_builder.rs @@ -32,10 +32,10 @@ fn add_key_value() { fn add_multiple_key_values() { let mut efb = EnvFileBuilder::new(); efb.add_key_value(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); - efb.add_key_value(DEFAULT_EXISTING_KEY, TEST_OVERRIDING_VALUE); + efb.add_key_value(DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE); let expected = expected_envfile(&[ (DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE), - (DEFAULT_EXISTING_KEY, TEST_OVERRIDING_VALUE), + (DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE), ]); assert_contents_str(efb, &expected); } diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index fbb2d01f..5c23f2cd 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -43,7 +43,7 @@ mod init_with_envfile { dotenv_wrap().expect(DOTENV_EXPECT); // dotenv() does not override existing var // but existing key is not set in this testenv - assert_env_var(DEFAULT_EXISTING_KEY, TEST_OVERRIDING_VALUE); + assert_env_var(DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE); assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); }); } From 1a93d8698e50b90e2d29b89142986b3728ea458d Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Mon, 15 Jul 2024 12:23:01 +0100 Subject: [PATCH 15/65] Refactor wrappers into module --- test_util/src/lib.rs | 3 +-- test_util/src/tests/default_env.rs | 2 +- test_util/src/tests/mod.rs | 2 +- test_util/src/tests/testenv.rs | 10 ++++---- test_util/src/{wrapper.rs => wrap.rs} | 35 ++++++++++++++++----------- 5 files changed, 29 insertions(+), 23 deletions(-) rename test_util/src/{wrapper.rs => wrap.rs} (51%) diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index 640e0db6..2a3eb9da 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -1,7 +1,7 @@ mod assertions; mod envfile; mod testenv; -mod wrapper; +pub mod wrap; #[cfg(test)] mod tests; @@ -9,7 +9,6 @@ mod tests; pub use assertions::*; pub use envfile::*; pub use testenv::*; -pub use wrapper::*; /// Default key used in envfile pub const DEFAULT_TEST_KEY: &str = "DEFAULT_TEST_KEY"; diff --git a/test_util/src/tests/default_env.rs b/test_util/src/tests/default_env.rs index babbf1b1..fe54e76c 100644 --- a/test_util/src/tests/default_env.rs +++ b/test_util/src/tests/default_env.rs @@ -17,7 +17,7 @@ fn envfile_exists() { #[test] fn envfile_loaded_vars_state() { test_in_default_env(|| { - dotenv_wrap().expect(DOTENV_EXPECT); + wrap::dotenv().expect(DOTENV_EXPECT); // dotenv() does not override existing var assert_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); diff --git a/test_util/src/tests/mod.rs b/test_util/src/tests/mod.rs index 464593be..1b27599c 100644 --- a/test_util/src/tests/mod.rs +++ b/test_util/src/tests/mod.rs @@ -16,7 +16,7 @@ fn assert_envfile_exists_in_testenv(testenv: TestEnv) { test_in_env(testenv, || { assert!(envfile_path.exists()); - assert!(dotenv_wrap().is_ok()); + assert!(wrap::dotenv().is_ok()); }); } diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index 5c23f2cd..518bb547 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -16,7 +16,7 @@ mod init { test_in_env(init_testenv, || { assert!(!envfile_path.exists()); - assert!(dotenv_wrap().is_err()); + assert!(wrap::dotenv().is_err()); }); } } @@ -40,7 +40,7 @@ mod init_with_envfile { fn default_envfile_loaded_vars_state() { let testenv = init_default_envfile_testenv(); test_in_env(testenv, || { - dotenv_wrap().expect(DOTENV_EXPECT); + wrap::dotenv().expect(DOTENV_EXPECT); // dotenv() does not override existing var // but existing key is not set in this testenv assert_env_var(DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE); @@ -69,7 +69,7 @@ mod init_with_envfile { fn custom_envfile_loaded_vars_state() { let testenv = init_custom_envfile_testenv(); test_in_env(testenv, || { - dotenv_wrap().expect(DOTENV_EXPECT); + wrap::dotenv().expect(DOTENV_EXPECT); assert_default_keys_unset(); assert_env_vars(CUSTOM_VARS); }); @@ -85,7 +85,7 @@ mod init_with_envfile { fn empty_envfile_loaded_vars_state() { let testenv = init_empty_envfile_testenv(); test_in_env(testenv, || { - dotenv_wrap().expect(DOTENV_EXPECT); + wrap::dotenv().expect(DOTENV_EXPECT); assert_default_keys_unset(); }); } @@ -100,7 +100,7 @@ mod init_with_envfile { fn custom_bom_envfile_loaded_vars_state() { let testenv = init_custom_bom_envfile_testenv(); test_in_env(testenv, || { - dotenv_wrap().expect(DOTENV_EXPECT); + wrap::dotenv().expect(DOTENV_EXPECT); assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); }); } diff --git a/test_util/src/wrapper.rs b/test_util/src/wrap.rs similarity index 51% rename from test_util/src/wrapper.rs rename to test_util/src/wrap.rs index 116791f7..04d5a03f 100644 --- a/test_util/src/wrapper.rs +++ b/test_util/src/wrap.rs @@ -1,6 +1,13 @@ //! Wrappers for the `dotenvy` API. //! //! If the `dotenvy` API changes, only this module needs to be updated. +//! +//! ## Example +//! +//! ```no_run +//! use dotenvy_test_util::wrap; +//! wrap::dotenv().expect("Failed to load .env file"); +//! ``` use dotenvy::{self, Iter, Result}; use std::env::Vars; @@ -10,71 +17,71 @@ use std::io; use std::path::{Path, PathBuf}; #[inline(always)] -pub fn var_wrap>(key: K) -> Result { +pub fn var>(key: K) -> Result { dotenvy::var(key) } #[inline(always)] -pub fn vars_wrap() -> Vars { +pub fn vars() -> Vars { dotenvy::vars() } #[inline(always)] -pub fn from_path_wrap>(path: P) -> Result<()> { +pub fn from_path>(path: P) -> Result<()> { dotenvy::from_path(path) } #[inline(always)] -pub fn from_path_override_wrap>(path: P) -> Result<()> { +pub fn from_path_override>(path: P) -> Result<()> { dotenvy::from_path_override(path) } #[inline(always)] -pub fn from_path_iter_wrap>(path: P) -> Result> { +pub fn from_path_iter>(path: P) -> Result> { dotenvy::from_path_iter(path) } #[inline(always)] -pub fn from_filename_wrap>(filename: P) -> Result { +pub fn from_filename>(filename: P) -> Result { dotenvy::from_filename(filename) } #[inline(always)] -pub fn from_filename_override_wrap>(filename: P) -> Result { +pub fn from_filename_override>(filename: P) -> Result { dotenvy::from_filename_override(filename) } #[inline(always)] -pub fn from_filename_iter_wrap>(filename: P) -> Result> { +pub fn from_filename_iter>(filename: P) -> Result> { dotenvy::from_filename_iter(filename) } #[inline(always)] -pub fn from_read_wrap(reader: R) -> Result<()> { +pub fn from_read(reader: R) -> Result<()> { dotenvy::from_read(reader) } #[inline(always)] -pub fn from_read_override_wrap(reader: R) -> Result<()> { +pub fn from_read_override(reader: R) -> Result<()> { dotenvy::from_read_override(reader) } #[inline(always)] -pub fn from_read_iter_wrap(reader: R) -> Iter { +pub fn from_read_iter(reader: R) -> Iter { dotenvy::from_read_iter(reader) } #[inline(always)] -pub fn dotenv_wrap() -> Result { +pub fn dotenv() -> Result { dotenvy::dotenv() } #[inline(always)] -pub fn dotenv_override_wrap() -> Result { +pub fn dotenv_override() -> Result { dotenvy::dotenv_override() } #[inline(always)] -pub fn dotenv_iter_wrap() -> Result> { +pub fn dotenv_iter() -> Result> { dotenvy::dotenv_iter() } From 538e196d3d9114a45b07432c74834a931e31b70f Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Mon, 15 Jul 2024 12:23:22 +0100 Subject: [PATCH 16/65] Flesh out test_util docs --- test_util/src/assertions.rs | 10 ++++---- test_util/src/envfile.rs | 13 +++++++--- test_util/src/lib.rs | 50 +++++++++++++++++++++++++++++++++++++ test_util/src/testenv.rs | 4 +-- 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/test_util/src/assertions.rs b/test_util/src/assertions.rs index fa353d0c..f9fa9e68 100644 --- a/test_util/src/assertions.rs +++ b/test_util/src/assertions.rs @@ -1,12 +1,12 @@ use super::*; use std::env::{self, VarError}; -/// Assert that a slice of environment variables are set and have the expected +/// Assert multiple environment variables are set and have the expected /// values. /// /// ## Arguments /// -/// * `vars` - A slice of key-expected value tuples +/// * `vars` - A slice of `(key, expected_value)` tuples /// /// ## Example /// @@ -23,7 +23,7 @@ pub fn assert_env_vars(vars: &[(&str, &str)]) { } } -/// Assert that an environment variable is set and has the expected value. +/// Assert environment variable is set and has the expected value. pub fn assert_env_var(key: &str, expected: &str) { match env::var(key) { Ok(actual) => assert_eq!( @@ -40,7 +40,7 @@ pub fn assert_env_var(key: &str, expected: &str) { } } -/// Assert that an environment variable is not currently set. +/// Assert environment variable is not currently set. pub fn assert_env_var_unset(key: &str) { match env::var(key) { Ok(actual) => panic!( @@ -56,7 +56,7 @@ pub fn assert_env_var_unset(key: &str) { } } -/// Assert that the default testing environment variables are not set. +/// Assert default testing environment variables are not set. pub fn assert_default_keys_unset() { assert_env_var_unset(DEFAULT_EXISTING_KEY); assert_env_var_unset(DEFAULT_TEST_KEY); diff --git a/test_util/src/envfile.rs b/test_util/src/envfile.rs index 7354327c..39da4317 100644 --- a/test_util/src/envfile.rs +++ b/test_util/src/envfile.rs @@ -1,6 +1,11 @@ use super::*; #[inline(always)] +/// Create the default envfile contents. +/// +/// [`DEFAULT_TEST_KEY`] set as [`DEFAULT_TEST_VALUE`] +/// +/// [`DEFAULT_EXISTING_KEY`] set as [`DEFAULT_OVERRIDING_VALUE`] pub fn create_default_envfile() -> String { format!( "{}={}\n{}={}", @@ -59,7 +64,7 @@ impl EnvFileBuilder { /// /// ## Panics /// - /// If the contents of the builder are not valid UTF-8. + /// If the contents of the builder is not valid UTF-8. pub fn build_string(&self) -> String { String::from_utf8(self.contents.clone()).expect("valid UTF-8") } @@ -73,7 +78,7 @@ impl EnvFileBuilder { /// /// ## Panics /// - /// If the contents of the builder are not valid UTF-8. + /// If the contents of the builder is not valid UTF-8. pub fn into_owned_string(self) -> String { String::from_utf8(self.contents).expect("valid UTF-8") } @@ -83,7 +88,9 @@ impl EnvFileBuilder { &self.contents } - /// Add + /// Add a slice of key-value pairs, separated by newlines. + /// + /// Includes a trailing newline. pub fn add_vars(&mut self, env_vars: &[(&str, &str)]) -> &mut Self { let mut many = String::new(); for (key, value) in env_vars { diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index 2a3eb9da..4d2c78c3 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -1,3 +1,53 @@ +//! Test environment setup, assertions and helpers. +//! +//! Setup a [`TestEnv`] and run your tests via [`test_in_env`]. The environment +//! can be tweaked with: +//! +//! - pre-existing environment variables, +//! - different directory layouts, +//! - custom `.env` file contents, +//! - custom envfile name/path. +//! +//! Use the `create_` helper functions, such as [`create_custom_envfile`], to +//! generate the `.env` file contents. If you need more control of the +//! envfile's bytes, use the [`EnvFileBuilder`]. +//! +//! In your tests, call the [`dotenvy`] API via the [`wrap`] module to keep any +//! API changes in one place. Then make use of the `assert_` helpers, such as +//! [`assert_env_var`] and [`assert_env_var_unset`], to check the state of the +//! environment. +//! +//! ## Example +//! +//! ```no_run +//! use dotenvy_test_util::*; +//! +//! const EXISTING_KEY: &str = "TEST_KEY"; +//! const EXISTING_VAL: &str = "test_val"; +//! const OVERRIDING_VAL: &str = "overriding_val"; +//! +//! #[test] +//! fn dotenv_override_existing_key() { +//! // setup testing environment +//! let mut testenv = TestEnv::init(); +//! +//! // with an existing environment variable +//! testenv.set_env_var(EXISTING_KEY, EXISTING_VAL); +//! +//! // with an envfile that overrides it +//! testenv.set_envfile_contents( +//! create_custom_envfile(&[(EXISTING_KEY, OVERRIDING_VAL)]), +//! ); +//! +//! // execute the closure in the testing environment +//! test_in_env(testenv, || { +//! wrap::dotenv_override().expect(".env should be loaded"); +//! assert_env_var(EXISTING_KEY, OVERRIDING_VAL); +//! }); +//! // any changes to environment variables will be reset for other tests +//! } +//! ``` + mod assertions; mod envfile; mod testenv; diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index da1f195a..0846b1fb 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -38,8 +38,8 @@ pub struct TestEnv { /// Simple key value struct for representing environment variables #[derive(Debug, Clone)] pub struct KeyVal { - key: String, - value: String, + pub key: String, + pub value: String, } /// Run a test closure within a test environment. From 31cf0746afa90c91dad553fac76c1d4d5be6ba16 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Mon, 15 Jul 2024 12:56:45 +0100 Subject: [PATCH 17/65] Add more than one envfile to TestEnv --- test_util/src/testenv.rs | 118 ++++++++++++----------------- test_util/src/tests/default_env.rs | 2 +- test_util/src/tests/mod.rs | 13 +++- test_util/src/tests/testenv.rs | 10 +-- 4 files changed, 63 insertions(+), 80 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 0846b1fb..542f3525 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -31,8 +31,14 @@ pub struct TestEnv { temp_dir: TempDir, work_dir: PathBuf, env_vars: Vec, - envfile_contents: Option>, - envfile_path: PathBuf, + envfiles: Vec, +} + +#[derive(Debug)] +/// Simple path and byte contents representing a `.env` file +pub struct EnvFile { + pub path: PathBuf, + pub contents: Vec, } /// Simple key value struct for representing environment variables @@ -95,50 +101,35 @@ impl TestEnv { pub fn init() -> Self { let tempdir = tempdir().expect("create tempdir"); let work_dir = tempdir.path().to_owned(); - let envfile_path = work_dir.join(".env"); Self { temp_dir: tempdir, work_dir, env_vars: Default::default(), - envfile_contents: None, - envfile_path, + envfiles: vec![], } } - /// Testing environment with custom envfile_contents. + /// Testing environment with custom envfile contents. /// /// No pre-existing env_vars set. The envfile_name is set to `.env`. The /// working directory is the created temporary directory. pub fn init_with_envfile(contents: impl Into>) -> Self { let mut test_env = Self::init(); - test_env.set_envfile_contents(contents); + test_env.add_envfile(".env", contents); test_env } - /// Change the absolute path to the envfile. - pub fn set_envfile_path(&mut self, path: PathBuf) -> &mut Self { - self.envfile_path = path; - self - } - - /// Specify the contents of the envfile. - /// - /// If this is the only change to the [`TestEnv`] being made, use - /// [`init_with_envfile`](TestEnv::init_with_envfile). - /// - /// Setting it to an empty string will cause an empty envfile to be created - pub fn set_envfile_contents(&mut self, contents: impl Into>) -> &mut Self { - let contents = contents.into(); - self.envfile_contents = Some(contents); - self - } - - /// Set the working directory the test will run from. - /// - /// The default is the created temporary directory. This method is useful if - /// you wish to run a test from a subdirectory or somewhere else. - pub fn set_work_dir(&mut self, path: PathBuf) -> &mut Self { - self.work_dir = path; + /// Add an individual envfile. + pub fn add_envfile(&mut self, path: P, contents: C) -> &mut Self + where + P: Into, + C: Into>, + { + let envfile = EnvFile { + path: self.temp_dir.path().join(path.into()), + contents: contents.into(), + }; + self.envfiles.push(envfile); self } @@ -154,7 +145,7 @@ impl TestEnv { self } - /// Set the pre-existing environment variables. + /// Set all the pre-existing environment variables. /// /// These variables will get added to the process' environment before the /// test is run. This overrides any previous env vars added to the @@ -167,7 +158,7 @@ impl TestEnv { self } - /// Set the pre-existing environment variables using [`str`] tuples. + /// Set all the pre-existing environment variables using [`str`] tuples. /// /// These variables will get added to the process' environment before the /// test is run. This overrides any previous env vars added to the @@ -187,6 +178,15 @@ impl TestEnv { self } + /// Set the working directory the test will run from. + /// + /// The default is the created temporary directory. This method is useful if + /// you wish to run a test from a subdirectory or somewhere else. + pub fn set_work_dir(&mut self, path: PathBuf) -> &mut Self { + self.work_dir = path; + self + } + /// Create a child folder within the temporary directory. /// /// This will not change the working directory the test is run in, or where @@ -207,46 +207,24 @@ impl TestEnv { child_dir } - /// Get a reference to the path of the temporary directory. + /// Reference to the path of the temporary directory. pub fn temp_path(&self) -> &Path { self.temp_dir.path() } - /// Get a reference to the working directory the test will be run from. + /// Reference to the working directory the test will be run from. pub fn work_dir(&self) -> &Path { &self.work_dir } - /// Get a reference to environment variables that will be set **before** - /// the test. + /// Reference to environment variables that will be set **before** the test. pub fn env_vars(&self) -> &[KeyVal] { &self.env_vars } - /// Get a reference to the bytes that will be placed in the envfile. - /// - /// If `None` is returned, an envfile will not be created - pub fn envfile_contents_as_bytes(&self) -> Option<&[u8]> { - self.envfile_contents.as_deref() - } - - /// Get a reference to the string that will be placed in the envfile. - /// - /// If `None` is returned, an envfile will not be created - /// - /// ## Panics - /// - /// This will panic if the envfile contents are not valid UTF-8 - pub fn envfile_contents_as_str(&self) -> Option<&str> { - self.envfile_contents_as_bytes().map(|bytes| { - let out = std::str::from_utf8(bytes).expect("valid UTF-8"); - Some(out) - })? - } - - /// Get a reference to the path of the envfile. - pub fn envfile_path(&self) -> &Path { - &self.envfile_path + /// Get a reference to the environment files that will created + pub fn envfiles(&self) -> &[EnvFile] { + &self.envfiles } } @@ -258,14 +236,15 @@ impl Default for TestEnv { key: DEFAULT_EXISTING_KEY.into(), value: DEFAULT_EXISTING_VALUE.into(), }]; - let envfile_contents = Some(create_default_envfile().into()); - let envfile_path = work_dir.join(".env"); + let envfiles = vec![EnvFile { + path: work_dir.join(".env"), + contents: create_default_envfile().into(), + }]; Self { temp_dir, work_dir, env_vars, - envfile_contents, - envfile_path, + envfiles, } } } @@ -311,13 +290,12 @@ fn reset_env(original_env: &EnvMap) { /// /// Writes the envfile, sets the working directory, and sets environment vars. fn create_env(test_env: &TestEnv) { - // only create the envfile if its contents has been set - if let Some(contents) = test_env.envfile_contents_as_bytes() { - create_envfile(&test_env.envfile_path, contents); - } - env::set_current_dir(&test_env.work_dir).expect("setting working directory"); + for EnvFile { path, contents } in &test_env.envfiles { + create_envfile(path, contents); + } + for KeyVal { key, value } in &test_env.env_vars { env::set_var(key, value) } diff --git a/test_util/src/tests/default_env.rs b/test_util/src/tests/default_env.rs index fe54e76c..858defcc 100644 --- a/test_util/src/tests/default_env.rs +++ b/test_util/src/tests/default_env.rs @@ -11,7 +11,7 @@ fn vars_state() { #[test] fn envfile_exists() { let testenv = TestEnv::default(); - assert_envfile_exists_in_testenv(testenv); + assert_envfiles_exist_in_testenv(testenv); } #[test] diff --git a/test_util/src/tests/mod.rs b/test_util/src/tests/mod.rs index 1b27599c..5c2a9803 100644 --- a/test_util/src/tests/mod.rs +++ b/test_util/src/tests/mod.rs @@ -11,12 +11,17 @@ const CUSTOM_VARS: &[(&str, &str)] = &[ const DOTENV_EXPECT: &str = "TestEnv should have .env file"; -fn assert_envfile_exists_in_testenv(testenv: TestEnv) { - let envfile_path = testenv.envfile_path().to_owned(); +fn assert_envfiles_exist_in_testenv(testenv: TestEnv) { + let paths = testenv + .envfiles() + .iter() + .map(|envfile| envfile.path.clone()) + .collect::>(); test_in_env(testenv, || { - assert!(envfile_path.exists()); - assert!(wrap::dotenv().is_ok()); + for path in paths { + assert!(path.exists()); + } }); } diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index 518bb547..9976e9ca 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -12,7 +12,7 @@ mod init { #[test] fn no_envfile() { let init_testenv = TestEnv::init(); - let envfile_path = init_testenv.envfile_path().to_owned(); + let envfile_path = init_testenv.temp_path().join(".env"); test_in_env(init_testenv, || { assert!(!envfile_path.exists()); @@ -33,7 +33,7 @@ mod init_with_envfile { #[test] fn default_envfile_exists() { let testenv = init_default_envfile_testenv(); - assert_envfile_exists_in_testenv(testenv); + assert_envfiles_exist_in_testenv(testenv); } #[test] @@ -62,7 +62,7 @@ mod init_with_envfile { #[test] fn custom_envfile_exists() { let testenv = init_custom_envfile_testenv(); - assert_envfile_exists_in_testenv(testenv); + assert_envfiles_exist_in_testenv(testenv); } #[test] @@ -78,7 +78,7 @@ mod init_with_envfile { #[test] fn empty_envfile_exists() { let testenv = init_empty_envfile_testenv(); - assert_envfile_exists_in_testenv(testenv); + assert_envfiles_exist_in_testenv(testenv); } #[test] @@ -93,7 +93,7 @@ mod init_with_envfile { #[test] fn custom_bom_envfile_exists() { let testenv = init_custom_bom_envfile_testenv(); - assert_envfile_exists_in_testenv(testenv); + assert_envfiles_exist_in_testenv(testenv); } #[test] From 443ab304cd999e7c0d466b0f2f58e9275d5d01be Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Mon, 15 Jul 2024 13:04:28 +0100 Subject: [PATCH 18/65] Fix parameter ownership in methods --- test_util/src/testenv.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 542f3525..81fc8a04 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -137,10 +137,14 @@ impl TestEnv { /// /// This adds more pre-existing environment variables to the process before /// any tests are run. - pub fn add_env_var(&mut self, key: impl ToString, value: impl ToString) -> &mut Self { + pub fn add_env_var(&mut self, key: K, value: V) -> &mut Self + where + K: Into, + V: Into, + { self.env_vars.push(KeyVal { - key: key.to_string(), - value: value.to_string(), + key: key.into(), + value: value.into(), }); self } @@ -182,8 +186,8 @@ impl TestEnv { /// /// The default is the created temporary directory. This method is useful if /// you wish to run a test from a subdirectory or somewhere else. - pub fn set_work_dir(&mut self, path: PathBuf) -> &mut Self { - self.work_dir = path; + pub fn set_work_dir(&mut self, path: impl Into) -> &mut Self { + self.work_dir = path.into(); self } @@ -193,18 +197,17 @@ impl TestEnv { /// the envfile is created. /// /// Will create parent directories if they are missing. - pub fn add_child_dir_all(&self, rel_path: impl AsRef) -> PathBuf { - let rel_path = rel_path.as_ref(); - let child_dir = self.temp_path().join(rel_path); - if let Err(err) = fs::create_dir_all(&child_dir) { + pub fn add_child_dir_all(&self, path: impl AsRef) { + let path = path.as_ref(); + let child_dir = self.temp_path().join(path); + if let Err(err) = fs::create_dir_all(child_dir) { panic!( "unable to create child directory: `{}` in `{}`: {}", self.temp_path().display(), - rel_path.display(), + path.display(), err ); } - child_dir } /// Reference to the path of the temporary directory. @@ -288,7 +291,7 @@ fn reset_env(original_env: &EnvMap) { /// Create an environment to run tests in. /// -/// Writes the envfile, sets the working directory, and sets environment vars. +/// Writes the envfiles, sets the working directory, and sets environment vars. fn create_env(test_env: &TestEnv) { env::set_current_dir(&test_env.work_dir).expect("setting working directory"); From 972162cd2177d31a4f5a711e5e3b73d3b1ae1db0 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Mon, 15 Jul 2024 13:31:05 +0100 Subject: [PATCH 19/65] Fix testenv lifetime --- test_util/src/testenv.rs | 10 +++++----- test_util/src/tests/default_env.rs | 2 +- test_util/src/tests/mod.rs | 30 +++++++++++++++++++++--------- test_util/src/tests/testenv.rs | 24 ++++++++++++------------ 4 files changed, 39 insertions(+), 27 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 81fc8a04..b7fb68c7 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -34,7 +34,7 @@ pub struct TestEnv { envfiles: Vec, } -#[derive(Debug)] +#[derive(Debug, Clone)] /// Simple path and byte contents representing a `.env` file pub struct EnvFile { pub path: PathBuf, @@ -52,7 +52,7 @@ pub struct KeyVal { /// /// Resets the environment variables, loads the [`TestEnv`], then runs the test /// closure. Ensures only one thread has access to the process environment. -pub fn test_in_env(test_env: TestEnv, test: F) +pub fn test_in_env(test_env: &TestEnv, test: F) where F: FnOnce(), { @@ -62,9 +62,9 @@ where let original_env = locker.lock().unwrap_or_else(PoisonError::into_inner); // we reset the environment anyway upon acquiring the lock reset_env(&original_env); - create_env(&test_env); + create_env(test_env); test(); - // drop the lock and the `TestEnv` - should delete the tempdir + // drop the lock } /// Run a test closure within the default test environment. @@ -89,7 +89,7 @@ where F: FnOnce(), { let test_env = TestEnv::default(); - test_in_env(test_env, test); + test_in_env(&test_env, test); } impl TestEnv { diff --git a/test_util/src/tests/default_env.rs b/test_util/src/tests/default_env.rs index 858defcc..f9d5b09b 100644 --- a/test_util/src/tests/default_env.rs +++ b/test_util/src/tests/default_env.rs @@ -11,7 +11,7 @@ fn vars_state() { #[test] fn envfile_exists() { let testenv = TestEnv::default(); - assert_envfiles_exist_in_testenv(testenv); + assert_envfiles_in_testenv(&testenv); } #[test] diff --git a/test_util/src/tests/mod.rs b/test_util/src/tests/mod.rs index 5c2a9803..dcd55885 100644 --- a/test_util/src/tests/mod.rs +++ b/test_util/src/tests/mod.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use super::*; mod default_env; @@ -11,20 +13,30 @@ const CUSTOM_VARS: &[(&str, &str)] = &[ const DOTENV_EXPECT: &str = "TestEnv should have .env file"; -fn assert_envfiles_exist_in_testenv(testenv: TestEnv) { - let paths = testenv - .envfiles() - .iter() - .map(|envfile| envfile.path.clone()) - .collect::>(); +fn assert_envfiles_in_testenv(testenv: &TestEnv) { + let files = testenv.envfiles(); test_in_env(testenv, || { - for path in paths { - assert!(path.exists()); + for EnvFile { path, contents } in files { + assert_envfile(path, contents); } }); } -fn assert_default_keys_not_set_in_testenv(testenv: TestEnv) { +fn assert_envfile(path: &Path, expected: &[u8]) { + assert!(path.exists(), "{} should exist in testenv", path.display()); + + let actual = std::fs::read(path) + .unwrap_or_else(|e| panic!("failed to read {} in testenv: {}", path.display(), e)); + + assert_eq!( + expected, + &actual, + "{} has incorrect contents", + path.display() + ); +} + +fn assert_default_keys_not_set_in_testenv(testenv: &TestEnv) { test_in_env(testenv, assert_default_keys_unset); } diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index 9976e9ca..17d7868d 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -6,7 +6,7 @@ mod init { #[test] fn vars_state() { let init_testenv = TestEnv::init(); - assert_default_keys_not_set_in_testenv(init_testenv); + assert_default_keys_not_set_in_testenv(&init_testenv); } #[test] @@ -14,7 +14,7 @@ mod init { let init_testenv = TestEnv::init(); let envfile_path = init_testenv.temp_path().join(".env"); - test_in_env(init_testenv, || { + test_in_env(&init_testenv, || { assert!(!envfile_path.exists()); assert!(wrap::dotenv().is_err()); }); @@ -27,19 +27,19 @@ mod init_with_envfile { #[test] fn default_envfile_vars_state() { let testenv = init_default_envfile_testenv(); - assert_default_keys_not_set_in_testenv(testenv); + assert_default_keys_not_set_in_testenv(&testenv); } #[test] fn default_envfile_exists() { let testenv = init_default_envfile_testenv(); - assert_envfiles_exist_in_testenv(testenv); + assert_envfiles_in_testenv(&testenv); } #[test] fn default_envfile_loaded_vars_state() { let testenv = init_default_envfile_testenv(); - test_in_env(testenv, || { + test_in_env(&testenv, || { wrap::dotenv().expect(DOTENV_EXPECT); // dotenv() does not override existing var // but existing key is not set in this testenv @@ -51,7 +51,7 @@ mod init_with_envfile { #[test] fn custom_envfile_vars_state() { let testenv = init_custom_envfile_testenv(); - test_in_env(testenv, || { + test_in_env(&testenv, || { assert_default_keys_unset(); for (key, _) in CUSTOM_VARS { assert_env_var_unset(key); @@ -62,13 +62,13 @@ mod init_with_envfile { #[test] fn custom_envfile_exists() { let testenv = init_custom_envfile_testenv(); - assert_envfiles_exist_in_testenv(testenv); + assert_envfiles_in_testenv(&testenv); } #[test] fn custom_envfile_loaded_vars_state() { let testenv = init_custom_envfile_testenv(); - test_in_env(testenv, || { + test_in_env(&testenv, || { wrap::dotenv().expect(DOTENV_EXPECT); assert_default_keys_unset(); assert_env_vars(CUSTOM_VARS); @@ -78,13 +78,13 @@ mod init_with_envfile { #[test] fn empty_envfile_exists() { let testenv = init_empty_envfile_testenv(); - assert_envfiles_exist_in_testenv(testenv); + assert_envfiles_in_testenv(&testenv); } #[test] fn empty_envfile_loaded_vars_state() { let testenv = init_empty_envfile_testenv(); - test_in_env(testenv, || { + test_in_env(&testenv, || { wrap::dotenv().expect(DOTENV_EXPECT); assert_default_keys_unset(); }); @@ -93,13 +93,13 @@ mod init_with_envfile { #[test] fn custom_bom_envfile_exists() { let testenv = init_custom_bom_envfile_testenv(); - assert_envfiles_exist_in_testenv(testenv); + assert_envfiles_in_testenv(&testenv); } #[test] fn custom_bom_envfile_loaded_vars_state() { let testenv = init_custom_bom_envfile_testenv(); - test_in_env(testenv, || { + test_in_env(&testenv, || { wrap::dotenv().expect(DOTENV_EXPECT); assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); }); From a1a554d0546139d5583a019c185b9793c97c7cb9 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Mon, 15 Jul 2024 13:34:39 +0100 Subject: [PATCH 20/65] Reflect test harness changes in API docs --- test_util/src/lib.rs | 5 +++-- test_util/src/testenv.rs | 9 ++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index 4d2c78c3..1ccac2c4 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -32,10 +32,11 @@ //! let mut testenv = TestEnv::init(); //! //! // with an existing environment variable -//! testenv.set_env_var(EXISTING_KEY, EXISTING_VAL); +//! testenv.add_env_var(EXISTING_KEY, EXISTING_VAL); //! //! // with an envfile that overrides it -//! testenv.set_envfile_contents( +//! testenv.add_envfile( +//! ".env", //! create_custom_envfile(&[(EXISTING_KEY, OVERRIDING_VAL)]), //! ); //! diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index b7fb68c7..af0a0e07 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -23,7 +23,7 @@ static ENV_LOCKER: OnceCell>> = OnceCell::new(); /// /// Creation methods: /// - [`TestEnv::init`]: blank environment (no envfile) -/// - [`TestEnv::init_with_envfile`]: blank environment with an envfile +/// - [`TestEnv::init_with_envfile`]: blank environment with a custom `.env` /// - [`TestEnv::default`]: default testing environment (1 existing var and 2 /// set in a `.env` file) #[derive(Debug)] @@ -95,9 +95,8 @@ where impl TestEnv { /// Blank testing environment in a new temporary directory. /// - /// No envfile_contents or pre-existing variables to set. The envfile_name - /// is set to `.env` but won't be written until its content is set. The - /// working directory is the created temporary directory. + /// No envfile or pre-existing variables set. The working directory is the + /// created temporary directory. pub fn init() -> Self { let tempdir = tempdir().expect("create tempdir"); let work_dir = tempdir.path().to_owned(); @@ -111,7 +110,7 @@ impl TestEnv { /// Testing environment with custom envfile contents. /// - /// No pre-existing env_vars set. The envfile_name is set to `.env`. The + /// No pre-existing env_vars set. The envfile path is set to `.env`. The /// working directory is the created temporary directory. pub fn init_with_envfile(contents: impl Into>) -> Self { let mut test_env = Self::init(); From 3dfc3945482ad188094a9ff9d43c01fe23fc1125 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Mon, 15 Jul 2024 15:10:11 +0100 Subject: [PATCH 21/65] Test adding envfiles to testenv --- test_util/src/testenv.rs | 21 +++++++++++-- test_util/src/tests/testenv.rs | 57 ++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index af0a0e07..45248c0a 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -119,13 +119,30 @@ impl TestEnv { } /// Add an individual envfile. + /// + /// ## Arguments + /// + /// - `path`: relative from the temporary directory + /// - `contents`: bytes or string + /// + /// ## Panics + /// + /// - if the path is empty or the same as the temporary directory + /// - if the envfile already exists pub fn add_envfile(&mut self, path: P, contents: C) -> &mut Self where - P: Into, + P: AsRef, C: Into>, { + let path = self.temp_dir.path().join(path); + if path == self.temp_path() { + panic!("path cannot be empty or the same as the temporary directory"); + } + if self.envfiles.iter().any(|f| f.path == path) { + panic!("envfile already in testenv: {}", path.display()); + } let envfile = EnvFile { - path: self.temp_dir.path().join(path.into()), + path, contents: contents.into(), }; self.envfiles.push(envfile); diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index 17d7868d..fd76d125 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -127,3 +127,60 @@ mod init_with_envfile { TestEnv::init_with_envfile(envfile) } } + +mod add_envfile { + use super::*; + + #[test] + #[should_panic] + fn panics_add_twice() { + let mut testenv = TestEnv::init(); + testenv.add_envfile(".env", create_default_envfile()); + testenv.add_envfile(".env", create_custom_envfile(CUSTOM_VARS)); + } + + #[test] + #[should_panic] + fn panics_same_path_as_init() { + let mut testenv = TestEnv::init_with_envfile(create_default_envfile()); + testenv.add_envfile(".env", create_default_envfile()); + } + + #[test] + #[should_panic] + fn panics_same_path_as_default() { + let mut testenv = TestEnv::default(); + testenv.add_envfile(".env", create_invalid_envfile()); + } + + #[test] + #[should_panic] + fn panics_path() { + let mut testenv = TestEnv::init(); + testenv.add_envfile("", create_default_envfile()); + } + + #[test] + fn allow_empty_contents() { + let mut testenv = TestEnv::init(); + testenv.add_envfile(".env", []); + assert_envfiles_in_testenv(&testenv); + } + + #[test] + fn allow_absolute_path() { + let mut testenv = TestEnv::init(); + let path = testenv.temp_path().join(".env"); + assert!(path.is_absolute()); + testenv.add_envfile(&path, create_default_envfile()); + assert_envfiles_in_testenv(&testenv); + } + + #[test] + fn two_files() { + let mut testenv = TestEnv::init(); + testenv.add_envfile(".env", create_default_envfile()); + testenv.add_envfile(".env.local", create_custom_envfile(CUSTOM_VARS)); + assert_envfiles_in_testenv(&testenv); + } +} From c70aa939d12b6e52a1a1b316db33832552bc0e8f Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Tue, 16 Jul 2024 11:11:26 +0100 Subject: [PATCH 22/65] Clean TestEnv::add_envfile --- test_util/src/testenv.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 45248c0a..7b8bd79a 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -135,18 +135,8 @@ impl TestEnv { C: Into>, { let path = self.temp_dir.path().join(path); - if path == self.temp_path() { - panic!("path cannot be empty or the same as the temporary directory"); - } - if self.envfiles.iter().any(|f| f.path == path) { - panic!("envfile already in testenv: {}", path.display()); - } - let envfile = EnvFile { - path, - contents: contents.into(), - }; - self.envfiles.push(envfile); - self + self.assert_envfile_path_is_valid(&path); + self.add_envfile_assume_valid(path, contents.into()) } /// Add an individual environment variable. @@ -245,6 +235,21 @@ impl TestEnv { pub fn envfiles(&self) -> &[EnvFile] { &self.envfiles } + + fn add_envfile_assume_valid(&mut self, path: PathBuf, contents: Vec) -> &mut Self { + let envfile = EnvFile { path, contents }; + self.envfiles.push(envfile); + self + } + + fn assert_envfile_path_is_valid(&self, path: &Path) { + if path == self.temp_path() { + panic!("path cannot be empty or the same as the temporary directory"); + } + if self.envfiles.iter().any(|f| f.path == path) { + panic!("envfile already in testenv: {}", path.display()); + } + } } impl Default for TestEnv { From 9ba20ddbef9333ff0bb1f822282e456bfa8fb368 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Tue, 16 Jul 2024 13:05:59 +0100 Subject: [PATCH 23/65] Test adding env vars to testenv --- test_util/src/testenv.rs | 27 ++++++++++++++++++---- test_util/src/tests/mod.rs | 4 ++++ test_util/src/tests/testenv.rs | 42 ++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 7b8bd79a..8c99b65d 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -143,16 +143,19 @@ impl TestEnv { /// /// This adds more pre-existing environment variables to the process before /// any tests are run. + /// + /// ## Panics + /// + /// - if the env var already exists in the testenv + /// - if the key is empty pub fn add_env_var(&mut self, key: K, value: V) -> &mut Self where K: Into, V: Into, { - self.env_vars.push(KeyVal { - key: key.into(), - value: value.into(), - }); - self + let key = key.into(); + self.assert_env_var_is_valid(&key); + self.add_env_var_assume_valid(key, value.into()) } /// Set all the pre-existing environment variables. @@ -242,6 +245,11 @@ impl TestEnv { self } + fn add_env_var_assume_valid(&mut self, key: String, value: String) -> &mut Self { + self.env_vars.push(KeyVal { key, value }); + self + } + fn assert_envfile_path_is_valid(&self, path: &Path) { if path == self.temp_path() { panic!("path cannot be empty or the same as the temporary directory"); @@ -250,6 +258,15 @@ impl TestEnv { panic!("envfile already in testenv: {}", path.display()); } } + + fn assert_env_var_is_valid(&self, key: &str) { + if key.is_empty() { + panic!("env_var key cannot be empty"); + } + if self.env_vars.iter().any(|kv| kv.key == key) { + panic!("env var already in testenv: {}", key); + } + } } impl Default for TestEnv { diff --git a/test_util/src/tests/mod.rs b/test_util/src/tests/mod.rs index dcd55885..56b809f4 100644 --- a/test_util/src/tests/mod.rs +++ b/test_util/src/tests/mod.rs @@ -40,3 +40,7 @@ fn assert_envfile(path: &Path, expected: &[u8]) { fn assert_default_keys_not_set_in_testenv(testenv: &TestEnv) { test_in_env(testenv, assert_default_keys_unset); } + +fn assert_env_vars_in_testenv(testenv: &TestEnv, vars: &[(&str, &str)]) { + test_in_env(testenv, || assert_env_vars(vars)); +} diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index fd76d125..9a7ffe65 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -184,3 +184,45 @@ mod add_envfile { assert_envfiles_in_testenv(&testenv); } } + +mod add_env_var { + use super::*; + + #[test] + #[should_panic] + fn panics_add_twice() { + let mut testenv = TestEnv::init(); + testenv.add_env_var("TEST_KEY", "one_value"); + testenv.add_env_var("TEST_KEY", "two_value"); + } + + #[test] + #[should_panic] + fn panics_same_var_as_default() { + let mut testenv = TestEnv::default(); + testenv.add_env_var(DEFAULT_EXISTING_KEY, "value"); + } + + #[test] + #[should_panic] + fn panics_emtpy_key() { + let mut testenv = TestEnv::init(); + testenv.add_env_var("", "value"); + } + + #[test] + fn allow_empty_value() { + let mut testenv = TestEnv::init(); + testenv.add_env_var("TEST_KEY", ""); + assert_env_vars_in_testenv(&testenv, &[("TEST_KEY", "")]); + } + + #[test] + fn two_vars() { + let mut testenv = TestEnv::init(); + let vars = [("TEST_KEY", "one_value"), ("TEST_KEY_2", "two_value")]; + testenv.add_env_var(vars[0].0, vars[0].1); + testenv.add_env_var(vars[1].0, vars[1].1); + assert_env_vars_in_testenv(&testenv, &vars); + } +} From 0fce9796ab029c17bbabcac7127c4067d617c9c9 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Tue, 16 Jul 2024 14:17:24 +0100 Subject: [PATCH 24/65] Simplify adding env vars to testenv --- test_util/src/testenv.rs | 96 +++++++--------------------------- test_util/src/tests/testenv.rs | 55 +++++++++++++++++++ 2 files changed, 75 insertions(+), 76 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 8c99b65d..5ea37f50 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -30,7 +30,7 @@ static ENV_LOCKER: OnceCell>> = OnceCell::new(); pub struct TestEnv { temp_dir: TempDir, work_dir: PathBuf, - env_vars: Vec, + env_vars: EnvMap, envfiles: Vec, } @@ -41,13 +41,6 @@ pub struct EnvFile { pub contents: Vec, } -/// Simple key value struct for representing environment variables -#[derive(Debug, Clone)] -pub struct KeyVal { - pub key: String, - pub value: String, -} - /// Run a test closure within a test environment. /// /// Resets the environment variables, loads the [`TestEnv`], then runs the test @@ -155,7 +148,8 @@ impl TestEnv { { let key = key.into(); self.assert_env_var_is_valid(&key); - self.add_env_var_assume_valid(key, value.into()) + self.env_vars.insert(key, value.into()); + self } /// Set all the pre-existing environment variables. @@ -164,30 +158,14 @@ impl TestEnv { /// test is run. This overrides any previous env vars added to the /// [`TestEnv`]. /// - /// If you wish to just use a slice of tuples, use - /// [`set_env_vars_tuple`](TestEnv::set_env_vars_tuple) instead. - pub fn set_env_vars(&mut self, env_vars: Vec) -> &mut Self { - self.env_vars = env_vars; - self - } - - /// Set all the pre-existing environment variables using [`str`] tuples. - /// - /// These variables will get added to the process' environment before the - /// test is run. This overrides any previous env vars added to the - /// [`TestEnv`]. + /// ## Panics /// - /// If you wish to add an owned `Vec` instead of `str` tuples, use - /// [`set_env_vars`](TestEnv::set_env_vars) instead. - pub fn set_env_vars_tuple(&mut self, env_vars: &[(&str, &str)]) -> &mut Self { - self.env_vars = env_vars - .iter() - .map(|(key, value)| KeyVal { - key: key.to_string(), - value: value.to_string(), - }) - .collect(); - + /// - if an env var is set twice + /// - if a key is empty + pub fn set_env_vars(&mut self, env_vars: &[(&str, &str)]) -> &mut Self { + for &(key, value) in env_vars { + self.add_env_var(key, value); + } self } @@ -212,8 +190,8 @@ impl TestEnv { if let Err(err) = fs::create_dir_all(child_dir) { panic!( "unable to create child directory: `{}` in `{}`: {}", - self.temp_path().display(), path.display(), + self.temp_path().display(), err ); } @@ -230,7 +208,7 @@ impl TestEnv { } /// Reference to environment variables that will be set **before** the test. - pub fn env_vars(&self) -> &[KeyVal] { + pub fn env_vars(&self) -> &EnvMap { &self.env_vars } @@ -245,11 +223,6 @@ impl TestEnv { self } - fn add_env_var_assume_valid(&mut self, key: String, value: String) -> &mut Self { - self.env_vars.push(KeyVal { key, value }); - self - } - fn assert_envfile_path_is_valid(&self, path: &Path) { if path == self.temp_path() { panic!("path cannot be empty or the same as the temporary directory"); @@ -261,49 +234,20 @@ impl TestEnv { fn assert_env_var_is_valid(&self, key: &str) { if key.is_empty() { - panic!("env_var key cannot be empty"); + panic!("key cannot be empty"); } - if self.env_vars.iter().any(|kv| kv.key == key) { - panic!("env var already in testenv: {}", key); + if self.env_vars.contains_key(key) { + panic!("key already in testenv: {}", key); } } } impl Default for TestEnv { fn default() -> Self { - let temp_dir = tempdir().expect("create tempdir"); - let work_dir = temp_dir.path().to_owned(); - let env_vars = vec![KeyVal { - key: DEFAULT_EXISTING_KEY.into(), - value: DEFAULT_EXISTING_VALUE.into(), - }]; - let envfiles = vec![EnvFile { - path: work_dir.join(".env"), - contents: create_default_envfile().into(), - }]; - Self { - temp_dir, - work_dir, - env_vars, - envfiles, - } - } -} - -impl From<(&str, &str)> for KeyVal { - fn from(kv: (&str, &str)) -> Self { - let (key, value) = kv; - Self { - key: key.to_string(), - value: value.to_string(), - } - } -} - -impl From<(String, String)> for KeyVal { - fn from(kv: (String, String)) -> Self { - let (key, value) = kv; - Self { key, value } + let mut testenv = TestEnv::init(); + testenv.add_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); + testenv.add_envfile(".env", create_default_envfile()); + testenv } } @@ -337,7 +281,7 @@ fn create_env(test_env: &TestEnv) { create_envfile(path, contents); } - for KeyVal { key, value } in &test_env.env_vars { + for (key, value) in &test_env.env_vars { env::set_var(key, value) } } diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index 9a7ffe65..e771fe89 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -225,4 +225,59 @@ mod add_env_var { testenv.add_env_var(vars[1].0, vars[1].1); assert_env_vars_in_testenv(&testenv, &vars); } + + #[test] + fn owned_strings() { + let mut testenv = TestEnv::init(); + testenv.add_env_var("TEST_KEY".to_string(), "test_val".to_string()); + assert_env_vars_in_testenv(&testenv, &[("TEST_KEY", "test_val")]); + } +} + +mod set_env_vars { + use super::*; + + #[test] + #[should_panic] + fn panics_double_key() { + let mut testenv = TestEnv::init(); + let mut vars = VARS.to_vec(); + vars.push(VARS[0]); + testenv.set_env_vars(&vars); + } + + #[test] + #[should_panic] + fn panics_empty_key() { + let mut testenv = TestEnv::init(); + testenv.set_env_vars(&[("", "value")]); + } + + #[test] + fn from_tuples_slice() { + let mut testenv = TestEnv::init(); + testenv.set_env_vars(VARS.as_slice()); + assert_vars_in_testenv(&testenv); + } + + #[test] + fn from_tuples_ref() { + let mut testenv = TestEnv::init(); + testenv.set_env_vars(&VARS); + assert_vars_in_testenv(&testenv); + } + + #[test] + fn from_vec_slice() { + let mut testenv = TestEnv::init(); + let vec = VARS.to_vec(); + testenv.set_env_vars(vec.as_slice()); + assert_vars_in_testenv(&testenv); + } + + const VARS: [(&str, &str); 2] = [("TEST_KEY", "one_value"), ("TEST_KEY_2", "two_value")]; + + fn assert_vars_in_testenv(testenv: &TestEnv) { + assert_env_vars_in_testenv(testenv, &VARS); + } } From 69924165cf991f37dd54fb2a24bd5eb688a8e2e9 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Tue, 16 Jul 2024 15:26:00 +0100 Subject: [PATCH 25/65] Test rest of TestEnv --- test_util/src/testenv.rs | 17 +++++-- test_util/src/tests/mod.rs | 5 ++ test_util/src/tests/testenv.rs | 87 ++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 3 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 5ea37f50..7c61562a 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -173,8 +173,19 @@ impl TestEnv { /// /// The default is the created temporary directory. This method is useful if /// you wish to run a test from a subdirectory or somewhere else. - pub fn set_work_dir(&mut self, path: impl Into) -> &mut Self { - self.work_dir = path.into(); + /// + /// ## Arguments + /// + /// - `path`: relative from the temporary directory + /// + /// ## Panics + /// + /// - if the path does not exist + pub fn set_work_dir(&mut self, path: impl AsRef) -> &mut Self { + self.work_dir = self.temp_path().join(path.as_ref()); + if !self.work_dir.exists() { + panic!("work_dir does not exist: {}", self.work_dir.display()); + } self } @@ -184,7 +195,7 @@ impl TestEnv { /// the envfile is created. /// /// Will create parent directories if they are missing. - pub fn add_child_dir_all(&self, path: impl AsRef) { + pub fn add_child_dir(&self, path: impl AsRef) { let path = path.as_ref(); let child_dir = self.temp_path().join(path); if let Err(err) = fs::create_dir_all(child_dir) { diff --git a/test_util/src/tests/mod.rs b/test_util/src/tests/mod.rs index 56b809f4..00c56e73 100644 --- a/test_util/src/tests/mod.rs +++ b/test_util/src/tests/mod.rs @@ -44,3 +44,8 @@ fn assert_default_keys_not_set_in_testenv(testenv: &TestEnv) { fn assert_env_vars_in_testenv(testenv: &TestEnv, vars: &[(&str, &str)]) { test_in_env(testenv, || assert_env_vars(vars)); } + +fn assert_path_exists_in_testenv(testenv: &TestEnv, path: impl AsRef) { + let path = testenv.temp_path().join(path.as_ref()); + assert!(path.exists(), "{} should exist in testenv", path.display()); +} diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index e771fe89..b8d4839a 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -19,6 +19,24 @@ mod init { assert!(wrap::dotenv().is_err()); }); } + + #[test] + fn work_dir_is_temp() { + let testenv = TestEnv::init(); + assert_eq!(testenv.work_dir(), testenv.temp_path()); + } + + #[test] + fn env_vars_are_empty() { + let testenv = TestEnv::init(); + assert!(testenv.env_vars().is_empty()); + } + + #[test] + fn envfiles_are_empty() { + let testenv = TestEnv::init(); + assert!(testenv.envfiles().is_empty()); + } } mod init_with_envfile { @@ -281,3 +299,72 @@ mod set_env_vars { assert_env_vars_in_testenv(testenv, &VARS); } } + +mod set_work_dir { + use super::*; + + #[test] + #[should_panic] + fn panics_non_existing() { + let mut testenv = TestEnv::init(); + testenv.set_work_dir("subdir"); + } + + #[test] + fn allow_absolute_path() { + let mut testenv = TestEnv::init(); + let path = testenv.temp_path().join("subdir"); + assert!(path.is_absolute()); + std::fs::create_dir_all(&path).expect("failed to create subdir"); + testenv.set_work_dir(&path); + assert_path_exists_in_testenv(&testenv, "subdir"); + } + + #[test] + fn relative_path() { + let mut testenv = TestEnv::init(); + std::fs::create_dir_all(testenv.temp_path().join("subdir")) + .expect("failed to create subdir"); + testenv.set_work_dir("subdir"); + assert_path_exists_in_testenv(&testenv, "subdir"); + } + + #[test] + fn in_testenv() { + let mut testenv = TestEnv::init(); + std::fs::create_dir_all(testenv.temp_path().join("subdir")) + .expect("failed to create subdir"); + testenv.set_work_dir("subdir"); + test_in_env(&testenv, || { + let current_dir = std::env::current_dir().expect("failed to get current dir"); + assert_eq!(current_dir, testenv.work_dir()); + }); + } +} + +mod add_child_dir { + use super::*; + + #[test] + fn subdir() { + let testenv = TestEnv::init(); + testenv.add_child_dir("subdir"); + assert_path_exists_in_testenv(&testenv, "subdir"); + } + + #[test] + fn allow_absolute_path() { + let testenv = TestEnv::init(); + let path = testenv.temp_path().join("subdir"); + assert!(path.is_absolute()); + testenv.add_child_dir(&path); + assert_path_exists_in_testenv(&testenv, "subdir"); + } + + #[test] + fn create_parents() { + let testenv = TestEnv::init(); + testenv.add_child_dir("subdir/subsubdir"); + assert_path_exists_in_testenv(&testenv, "subdir/subsubdir"); + } +} From 55ed5013765fd047ef6e2628b514867aba863127 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Wed, 17 Jul 2024 11:09:22 +0100 Subject: [PATCH 26/65] Test envfile functions --- test_util/src/envfile.rs | 2 +- test_util/src/tests/envfile.rs | 37 ++++++++++++++++++++++++++ test_util/src/tests/envfile_builder.rs | 11 -------- test_util/src/tests/mod.rs | 12 +++++++++ 4 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 test_util/src/tests/envfile.rs diff --git a/test_util/src/envfile.rs b/test_util/src/envfile.rs index 39da4317..cbc0be65 100644 --- a/test_util/src/envfile.rs +++ b/test_util/src/envfile.rs @@ -1,11 +1,11 @@ use super::*; -#[inline(always)] /// Create the default envfile contents. /// /// [`DEFAULT_TEST_KEY`] set as [`DEFAULT_TEST_VALUE`] /// /// [`DEFAULT_EXISTING_KEY`] set as [`DEFAULT_OVERRIDING_VALUE`] +#[inline(always)] pub fn create_default_envfile() -> String { format!( "{}={}\n{}={}", diff --git a/test_util/src/tests/envfile.rs b/test_util/src/tests/envfile.rs new file mode 100644 index 00000000..45239482 --- /dev/null +++ b/test_util/src/tests/envfile.rs @@ -0,0 +1,37 @@ +use super::*; + +#[test] +fn create_default() { + let expected = format!( + "{}={}\n{}={}", + DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE, DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE + ); + let actual = create_default_envfile(); + assert_eq!( + expected, actual, + "envfile should be created with default values" + ); +} + +#[test] +fn create_invalid() { + let expected = format!( + "{}{}\n{}{}", + DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE, DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE + ); + let actual = create_invalid_envfile(); + assert_eq!( + expected, actual, + "envfile should be created without equals sign" + ); +} + +#[test] +fn create_custom() { + let expected = expected_envfile(CUSTOM_VARS); + let actual = create_custom_envfile(CUSTOM_VARS); + assert_eq!( + expected, actual, + "envfile should be created with custom values" + ); +} diff --git a/test_util/src/tests/envfile_builder.rs b/test_util/src/tests/envfile_builder.rs index cd720fed..dd4ecd5f 100644 --- a/test_util/src/tests/envfile_builder.rs +++ b/test_util/src/tests/envfile_builder.rs @@ -93,14 +93,3 @@ fn assert_contents_str(efb: EnvFileBuilder, expected: &str) { let contents = efb.into_owned_string(); assert_eq!(expected, contents,); } - -fn expected_envfile(env_vars: &[(&str, &str)]) -> String { - let mut envfile = String::new(); - for (key, value) in env_vars { - envfile.push_str(key); - envfile.push('='); - envfile.push_str(value); - envfile.push('\n'); - } - envfile -} diff --git a/test_util/src/tests/mod.rs b/test_util/src/tests/mod.rs index 00c56e73..55921981 100644 --- a/test_util/src/tests/mod.rs +++ b/test_util/src/tests/mod.rs @@ -3,6 +3,7 @@ use std::path::Path; use super::*; mod default_env; +mod envfile; mod envfile_builder; mod testenv; @@ -49,3 +50,14 @@ fn assert_path_exists_in_testenv(testenv: &TestEnv, path: impl AsRef) { let path = testenv.temp_path().join(path.as_ref()); assert!(path.exists(), "{} should exist in testenv", path.display()); } + +fn expected_envfile(env_vars: &[(&str, &str)]) -> String { + let mut envfile = String::new(); + for (key, value) in env_vars { + envfile.push_str(key); + envfile.push('='); + envfile.push_str(value); + envfile.push('\n'); + } + envfile +} From 34712110d41aedb626c3ca2cbabb42f26a88bb61 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Wed, 17 Jul 2024 11:45:12 +0100 Subject: [PATCH 27/65] Document test util with a readme --- test_util/LICENSE | 21 +++++++ test_util/README.md | 135 +++++++++++++++++++++++++++++++++++++++++++ test_util/src/lib.rs | 5 +- 3 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 test_util/LICENSE create mode 100644 test_util/README.md diff --git a/test_util/LICENSE b/test_util/LICENSE new file mode 100644 index 00000000..1fc6dfdc --- /dev/null +++ b/test_util/LICENSE @@ -0,0 +1,21 @@ +# The MIT License (MIT) + +Copyright (c) 2024 Christopher Morton and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/test_util/README.md b/test_util/README.md new file mode 100644 index 00000000..b75b3820 --- /dev/null +++ b/test_util/README.md @@ -0,0 +1,135 @@ +# dotenvy test util + +This is an internal package used for testing dotenvy. + +## Why + +Eases the annoyance of setting up custom `.env` files, managing existing +environment variables, and running multiple tests at once. + +## How + +By setting up a `TestEnv`, and running a closure via `test_in_env`. + +Before executing the closure, the `TestEnv` will: + +- Create a temporary directory +- Lock the environment from other tests +- Store the existing environment variables +- Add any custom env_vars to the environment +- Create any custom envfiles in the temporary directory + +In the closure you can: + +- Use the `wrap` module to call dotenvy's api +- Use the assertion functions to test the environment + +After executing the closure, the `TestEnv` will: + +- Remove the temporary directory +- Restore the environment variables to the original state +- Unlock the environment + +See the API docs for more details. For now, they must be built locally with +`cargo doc`. + +### Commented example + +```rust +use dotenvy_test_util::*; + +const EXISTING_KEY: &str = "TEST_KEY"; +const EXISTING_VAL: &str = "test_val"; +const OVERRIDING_VAL: &str = "overriding_val"; + +#[test] +fn dotenv_override_existing_key() { + // setup testing environment + let mut testenv = TestEnv::init(); + + // with an existing environment variable + testenv.add_env_var(EXISTING_KEY, EXISTING_VAL); + + // with an envfile that overrides it + testenv.add_envfile( + ".env", + create_custom_envfile(&[(EXISTING_KEY, OVERRIDING_VAL)]), + ); + + // execute a closure in the testing environment + test_in_env(&testenv, || { + wrap::dotenv_override().expect(".env should be loaded"); + assert_env_var(EXISTING_KEY, OVERRIDING_VAL); + }); + // any changes to environment variables will be reset for other tests +} +``` + +### Default TestEnv + +Use the default `TestEnv` for simple tests. + +```rust +use dotenvy_test_util::*; + +#[test] +fn dotenv_works() { + test_in_default_env(|| { + wrap::dotenv().expect(".env should be loaded"); + assert_env_var(DEFAULT_KEY, DEFAULT_VAL); + }) +} +``` + +The default `TestEnv` has 1 existing environment variable: + +- `DEFAULT_EXISTING_KEY` set to `DEFAULT_EXISTING_VAL` + +It has an envfile `.env` that sets: + +- `DEFAULT_TEST_KEY` to `DEFAULT_TEST_VAL` +- `DEFAULT_EXISTING_KEY` to `DEFAULT_OVERRIDING_VAL` + +### Customised Envfile + +Use the `EnvFileBuilder` to manipulate the content of an envfile. Useful +for byte-order-mark(BOM) testing, and other edge cases. + +```rust +use dotenvy_test_util::*; + +#[test] +fn comments_ignored_in_utf8bom_envfile() { + let mut efb = EnvFileBuilder::new(); + efb.insert_utf8_bom(); + efb.add_strln("# TEST_KEY=TEST_VAL"); + let envfile = efb.into_owned_bytes(); + + let testenv = TestEnv::init_with_envfile(envfile); + + test_in_env(&testenv, || { + wrap::dotenv().expect(".env should be loaded"); + assert_env_var_unset("TEST_KEY"); + }); +} +``` + +Or use anything that can be converted to a `Vec` if your envfile is +simple. + +```rust +use dotenvy_test_util::*; + +#[test] +fn comments_ignored() { + let envfile = "# TEST_KEY=TEST_VAL\n"; + + let testenv = TestEnv::init_with_envfile(envfile); + + test_in_env(&testenv, || { + wrap::dotenv().expect(".env should be loaded"); + assert_env_var_unset("TEST_KEY"); + }); +} +``` + diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index 1ccac2c4..bc6f0625 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -6,6 +6,7 @@ //! - pre-existing environment variables, //! - different directory layouts, //! - custom `.env` file contents, +//! - multiple `.env` files, //! - custom envfile name/path. //! //! Use the `create_` helper functions, such as [`create_custom_envfile`], to @@ -40,8 +41,8 @@ //! create_custom_envfile(&[(EXISTING_KEY, OVERRIDING_VAL)]), //! ); //! -//! // execute the closure in the testing environment -//! test_in_env(testenv, || { +//! // execute a closure in the testing environment +//! test_in_env(&testenv, || { //! wrap::dotenv_override().expect(".env should be loaded"); //! assert_env_var(EXISTING_KEY, OVERRIDING_VAL); //! }); From 6edeb9fc6d2f068718d4198857152df19bb27476 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Wed, 17 Jul 2024 13:50:12 +0100 Subject: [PATCH 28/65] Fix macos working directory --- test_util/src/testenv.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 7c61562a..a8553593 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -182,7 +182,11 @@ impl TestEnv { /// /// - if the path does not exist pub fn set_work_dir(&mut self, path: impl AsRef) -> &mut Self { - self.work_dir = self.temp_path().join(path.as_ref()); + self.work_dir = self + .temp_path() + .join(path.as_ref()) + .canonicalize() + .expect("canonicalize work_dir"); if !self.work_dir.exists() { panic!("work_dir does not exist: {}", self.work_dir.display()); } From 896c32f3ebfc8d28f60a53c35a9dd1df1b55137c Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Wed, 17 Jul 2024 15:54:09 +0100 Subject: [PATCH 29/65] Remove wrap module --- test_util/README.md | 21 ++++---- test_util/src/lib.rs | 11 ++-- test_util/src/tests/default_env.rs | 2 +- test_util/src/tests/testenv.rs | 11 ++-- test_util/src/wrap.rs | 87 ------------------------------ 5 files changed, 23 insertions(+), 109 deletions(-) delete mode 100644 test_util/src/wrap.rs diff --git a/test_util/README.md b/test_util/README.md index b75b3820..fe59c622 100644 --- a/test_util/README.md +++ b/test_util/README.md @@ -11,7 +11,7 @@ environment variables, and running multiple tests at once. By setting up a `TestEnv`, and running a closure via `test_in_env`. -Before executing the closure, the `TestEnv` will: +**Before** executing the closure, the `TestEnv` will: - Create a temporary directory - Lock the environment from other tests @@ -19,12 +19,9 @@ Before executing the closure, the `TestEnv` will: - Add any custom env_vars to the environment - Create any custom envfiles in the temporary directory -In the closure you can: +**In the closure** you can use the assertion functions to test the environment. -- Use the `wrap` module to call dotenvy's api -- Use the assertion functions to test the environment - -After executing the closure, the `TestEnv` will: +**After** executing the closure, the `TestEnv` will: - Remove the temporary directory - Restore the environment variables to the original state @@ -37,6 +34,7 @@ See the API docs for more details. For now, they must be built locally with ```rust use dotenvy_test_util::*; +use dotenvy::dotenv_override; const EXISTING_KEY: &str = "TEST_KEY"; const EXISTING_VAL: &str = "test_val"; @@ -58,7 +56,7 @@ fn dotenv_override_existing_key() { // execute a closure in the testing environment test_in_env(&testenv, || { - wrap::dotenv_override().expect(".env should be loaded"); + dotenv_override().expect(".env should be loaded"); assert_env_var(EXISTING_KEY, OVERRIDING_VAL); }); // any changes to environment variables will be reset for other tests @@ -71,11 +69,12 @@ Use the default `TestEnv` for simple tests. ```rust use dotenvy_test_util::*; +use dotenvy::dotenv; #[test] fn dotenv_works() { test_in_default_env(|| { - wrap::dotenv().expect(".env should be loaded"); + dotenv().expect(".env should be loaded"); assert_env_var(DEFAULT_KEY, DEFAULT_VAL); }) } @@ -97,6 +96,7 @@ for byte-order-mark(BOM) testing, and other edge cases. ```rust use dotenvy_test_util::*; +use dotenvy::dotenv; #[test] fn comments_ignored_in_utf8bom_envfile() { @@ -108,7 +108,7 @@ fn comments_ignored_in_utf8bom_envfile() { let testenv = TestEnv::init_with_envfile(envfile); test_in_env(&testenv, || { - wrap::dotenv().expect(".env should be loaded"); + dotenv().expect(".env should be loaded"); assert_env_var_unset("TEST_KEY"); }); } @@ -119,6 +119,7 @@ simple. ```rust use dotenvy_test_util::*; +use dotenvy::dotenv; #[test] fn comments_ignored() { @@ -127,7 +128,7 @@ fn comments_ignored() { let testenv = TestEnv::init_with_envfile(envfile); test_in_env(&testenv, || { - wrap::dotenv().expect(".env should be loaded"); + dotenv().expect(".env should be loaded"); assert_env_var_unset("TEST_KEY"); }); } diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index bc6f0625..ea775d0b 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -13,15 +13,15 @@ //! generate the `.env` file contents. If you need more control of the //! envfile's bytes, use the [`EnvFileBuilder`]. //! -//! In your tests, call the [`dotenvy`] API via the [`wrap`] module to keep any -//! API changes in one place. Then make use of the `assert_` helpers, such as -//! [`assert_env_var`] and [`assert_env_var_unset`], to check the state of the -//! environment. +//! In your tests, call the [`dotenvy`] API, then make use of the `assert_` +//! helpers, such as [`assert_env_var`] and [`assert_env_var_unset`], to check +//! the state of the environment. //! //! ## Example //! //! ```no_run //! use dotenvy_test_util::*; +//! use dotenvy::dotenv_override; //! //! const EXISTING_KEY: &str = "TEST_KEY"; //! const EXISTING_VAL: &str = "test_val"; @@ -43,7 +43,7 @@ //! //! // execute a closure in the testing environment //! test_in_env(&testenv, || { -//! wrap::dotenv_override().expect(".env should be loaded"); +//! dotenv_override().expect(".env should be loaded"); //! assert_env_var(EXISTING_KEY, OVERRIDING_VAL); //! }); //! // any changes to environment variables will be reset for other tests @@ -53,7 +53,6 @@ mod assertions; mod envfile; mod testenv; -pub mod wrap; #[cfg(test)] mod tests; diff --git a/test_util/src/tests/default_env.rs b/test_util/src/tests/default_env.rs index f9d5b09b..68d56b5a 100644 --- a/test_util/src/tests/default_env.rs +++ b/test_util/src/tests/default_env.rs @@ -17,7 +17,7 @@ fn envfile_exists() { #[test] fn envfile_loaded_vars_state() { test_in_default_env(|| { - wrap::dotenv().expect(DOTENV_EXPECT); + dotenvy::dotenv().expect(DOTENV_EXPECT); // dotenv() does not override existing var assert_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index b8d4839a..26290153 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -1,4 +1,5 @@ use super::*; +use dotenvy::dotenv; mod init { use super::*; @@ -16,7 +17,7 @@ mod init { test_in_env(&init_testenv, || { assert!(!envfile_path.exists()); - assert!(wrap::dotenv().is_err()); + assert!(dotenv().is_err()); }); } @@ -58,7 +59,7 @@ mod init_with_envfile { fn default_envfile_loaded_vars_state() { let testenv = init_default_envfile_testenv(); test_in_env(&testenv, || { - wrap::dotenv().expect(DOTENV_EXPECT); + dotenv().expect(DOTENV_EXPECT); // dotenv() does not override existing var // but existing key is not set in this testenv assert_env_var(DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE); @@ -87,7 +88,7 @@ mod init_with_envfile { fn custom_envfile_loaded_vars_state() { let testenv = init_custom_envfile_testenv(); test_in_env(&testenv, || { - wrap::dotenv().expect(DOTENV_EXPECT); + dotenv().expect(DOTENV_EXPECT); assert_default_keys_unset(); assert_env_vars(CUSTOM_VARS); }); @@ -103,7 +104,7 @@ mod init_with_envfile { fn empty_envfile_loaded_vars_state() { let testenv = init_empty_envfile_testenv(); test_in_env(&testenv, || { - wrap::dotenv().expect(DOTENV_EXPECT); + dotenv().expect(DOTENV_EXPECT); assert_default_keys_unset(); }); } @@ -118,7 +119,7 @@ mod init_with_envfile { fn custom_bom_envfile_loaded_vars_state() { let testenv = init_custom_bom_envfile_testenv(); test_in_env(&testenv, || { - wrap::dotenv().expect(DOTENV_EXPECT); + dotenv().expect(DOTENV_EXPECT); assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); }); } diff --git a/test_util/src/wrap.rs b/test_util/src/wrap.rs deleted file mode 100644 index 04d5a03f..00000000 --- a/test_util/src/wrap.rs +++ /dev/null @@ -1,87 +0,0 @@ -//! Wrappers for the `dotenvy` API. -//! -//! If the `dotenvy` API changes, only this module needs to be updated. -//! -//! ## Example -//! -//! ```no_run -//! use dotenvy_test_util::wrap; -//! wrap::dotenv().expect("Failed to load .env file"); -//! ``` - -use dotenvy::{self, Iter, Result}; -use std::env::Vars; -use std::ffi::OsStr; -use std::fs::File; -use std::io; -use std::path::{Path, PathBuf}; - -#[inline(always)] -pub fn var>(key: K) -> Result { - dotenvy::var(key) -} - -#[inline(always)] -pub fn vars() -> Vars { - dotenvy::vars() -} - -#[inline(always)] -pub fn from_path>(path: P) -> Result<()> { - dotenvy::from_path(path) -} - -#[inline(always)] -pub fn from_path_override>(path: P) -> Result<()> { - dotenvy::from_path_override(path) -} - -#[inline(always)] -pub fn from_path_iter>(path: P) -> Result> { - dotenvy::from_path_iter(path) -} - -#[inline(always)] -pub fn from_filename>(filename: P) -> Result { - dotenvy::from_filename(filename) -} - -#[inline(always)] -pub fn from_filename_override>(filename: P) -> Result { - dotenvy::from_filename_override(filename) -} - -#[inline(always)] -pub fn from_filename_iter>(filename: P) -> Result> { - dotenvy::from_filename_iter(filename) -} - -#[inline(always)] -pub fn from_read(reader: R) -> Result<()> { - dotenvy::from_read(reader) -} - -#[inline(always)] -pub fn from_read_override(reader: R) -> Result<()> { - dotenvy::from_read_override(reader) -} - -#[inline(always)] -pub fn from_read_iter(reader: R) -> Iter { - dotenvy::from_read_iter(reader) -} - -#[inline(always)] -pub fn dotenv() -> Result { - dotenvy::dotenv() -} - -#[inline(always)] -pub fn dotenv_override() -> Result { - dotenvy::dotenv_override() -} - -#[inline(always)] -pub fn dotenv_iter() -> Result> { - dotenvy::dotenv_iter() -} From dc14ec5478a78d74f4ed314b84db32277e21fda5 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Wed, 17 Jul 2024 16:07:51 +0100 Subject: [PATCH 30/65] Add extra assertions --- test_util/src/assertions.rs | 12 ++++++++++++ test_util/src/tests/default_env.rs | 5 ++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/test_util/src/assertions.rs b/test_util/src/assertions.rs index f9fa9e68..8a169a62 100644 --- a/test_util/src/assertions.rs +++ b/test_util/src/assertions.rs @@ -61,3 +61,15 @@ pub fn assert_default_keys_unset() { assert_env_var_unset(DEFAULT_EXISTING_KEY); assert_env_var_unset(DEFAULT_TEST_KEY); } + +/// Assert default testing environment variables are set. +/// Assuming the default envfile is loaded. +pub fn assert_default_keys() { + assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); + assert_default_existing_var(); +} + +/// Assert default existing environment variable is set. +pub fn assert_default_existing_var() { + assert_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); +} diff --git a/test_util/src/tests/default_env.rs b/test_util/src/tests/default_env.rs index 68d56b5a..4ac848e0 100644 --- a/test_util/src/tests/default_env.rs +++ b/test_util/src/tests/default_env.rs @@ -3,8 +3,8 @@ use super::*; #[test] fn vars_state() { test_in_default_env(|| { - assert_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); assert_env_var_unset(DEFAULT_TEST_KEY); + assert_default_existing_var(); }); } @@ -19,7 +19,6 @@ fn envfile_loaded_vars_state() { test_in_default_env(|| { dotenvy::dotenv().expect(DOTENV_EXPECT); // dotenv() does not override existing var - assert_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); - assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); + assert_default_keys(); }); } From 1bd15d013bf2dd0aaab6aebff72298c85391f401 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Wed, 17 Jul 2024 16:10:34 +0100 Subject: [PATCH 31/65] Fix naming consistency --- test_util/src/testenv.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index a8553593..5ae01846 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -45,7 +45,7 @@ pub struct EnvFile { /// /// Resets the environment variables, loads the [`TestEnv`], then runs the test /// closure. Ensures only one thread has access to the process environment. -pub fn test_in_env(test_env: &TestEnv, test: F) +pub fn test_in_env(testenv: &TestEnv, test: F) where F: FnOnce(), { @@ -55,7 +55,7 @@ where let original_env = locker.lock().unwrap_or_else(PoisonError::into_inner); // we reset the environment anyway upon acquiring the lock reset_env(&original_env); - create_env(test_env); + create_env(testenv); test(); // drop the lock } @@ -81,8 +81,8 @@ pub fn test_in_default_env(test: F) where F: FnOnce(), { - let test_env = TestEnv::default(); - test_in_env(&test_env, test); + let testenv = TestEnv::default(); + test_in_env(&testenv, test); } impl TestEnv { @@ -106,9 +106,9 @@ impl TestEnv { /// No pre-existing env_vars set. The envfile path is set to `.env`. The /// working directory is the created temporary directory. pub fn init_with_envfile(contents: impl Into>) -> Self { - let mut test_env = Self::init(); - test_env.add_envfile(".env", contents); - test_env + let mut testenv = Self::init(); + testenv.add_envfile(".env", contents); + testenv } /// Add an individual envfile. @@ -289,14 +289,14 @@ fn reset_env(original_env: &EnvMap) { /// Create an environment to run tests in. /// /// Writes the envfiles, sets the working directory, and sets environment vars. -fn create_env(test_env: &TestEnv) { - env::set_current_dir(&test_env.work_dir).expect("setting working directory"); +fn create_env(testenv: &TestEnv) { + env::set_current_dir(&testenv.work_dir).expect("setting working directory"); - for EnvFile { path, contents } in &test_env.envfiles { + for EnvFile { path, contents } in &testenv.envfiles { create_envfile(path, contents); } - for (key, value) in &test_env.env_vars { + for (key, value) in &testenv.env_vars { env::set_var(key, value) } } From 9974e0b77550032a9a26b61723e9c658bd5ebe8b Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Wed, 17 Jul 2024 17:16:48 +0100 Subject: [PATCH 32/65] Add EnvFileBuilder conversions --- test_util/README.md | 3 +-- test_util/src/envfile.rs | 20 ++++++++++++++++++++ test_util/src/tests/envfile_builder.rs | 22 ++++++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/test_util/README.md b/test_util/README.md index fe59c622..f3dad08e 100644 --- a/test_util/README.md +++ b/test_util/README.md @@ -103,9 +103,8 @@ fn comments_ignored_in_utf8bom_envfile() { let mut efb = EnvFileBuilder::new(); efb.insert_utf8_bom(); efb.add_strln("# TEST_KEY=TEST_VAL"); - let envfile = efb.into_owned_bytes(); - let testenv = TestEnv::init_with_envfile(envfile); + let testenv = TestEnv::init_with_envfile(efb); test_in_env(&testenv, || { dotenv().expect(".env should be loaded"); diff --git a/test_util/src/envfile.rs b/test_util/src/envfile.rs index cbc0be65..4d09d6ab 100644 --- a/test_util/src/envfile.rs +++ b/test_util/src/envfile.rs @@ -138,3 +138,23 @@ impl EnvFileBuilder { self } } + +impl From for Vec { + fn from(builder: EnvFileBuilder) -> Self { + builder.into_owned_bytes() + } +} + +impl From> for EnvFileBuilder { + fn from(contents: Vec) -> Self { + Self { contents } + } +} + +impl From for EnvFileBuilder { + fn from(contents: String) -> Self { + Self { + contents: contents.into_bytes(), + } + } +} diff --git a/test_util/src/tests/envfile_builder.rs b/test_util/src/tests/envfile_builder.rs index dd4ecd5f..cb3dc84a 100644 --- a/test_util/src/tests/envfile_builder.rs +++ b/test_util/src/tests/envfile_builder.rs @@ -84,6 +84,28 @@ fn add_strln() { assert_contents_str(efb, "test\n"); } +#[test] +fn from_vec_u8() { + let vec: Vec = Vec::from(create_default_envfile()); + let efb = EnvFileBuilder::from(vec); + assert_contents_str(efb, &create_default_envfile()); +} + +#[test] +fn to_vec_u8() { + let mut efb = EnvFileBuilder::new(); + efb.add_str(create_default_envfile().as_str()); + let vec = Vec::from(efb); + let expected = create_default_envfile().into_bytes(); + assert_eq!(expected, vec); +} + +#[test] +fn from_string() { + let efb = EnvFileBuilder::from(create_default_envfile()); + assert_contents_str(efb, &create_default_envfile()); +} + fn assert_contents_empty(efb: EnvFileBuilder) { let contents = efb.into_owned_bytes(); assert!(contents.is_empty()); From 2d13009fa7dd2da1cd93543570928c8de6e77e52 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Wed, 17 Jul 2024 15:57:21 +0100 Subject: [PATCH 33/65] Fix canonicalize paths --- test_util/src/testenv.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 5ae01846..91c9e1c2 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -28,7 +28,9 @@ static ENV_LOCKER: OnceCell>> = OnceCell::new(); /// set in a `.env` file) #[derive(Debug)] pub struct TestEnv { - temp_dir: TempDir, + // Temporary directory that will be deleted on drop + _temp_dir: TempDir, + dir_path: PathBuf, work_dir: PathBuf, env_vars: EnvMap, envfiles: Vec, @@ -92,10 +94,14 @@ impl TestEnv { /// created temporary directory. pub fn init() -> Self { let tempdir = tempdir().expect("create tempdir"); - let work_dir = tempdir.path().to_owned(); + let dir_path = tempdir + .path() + .canonicalize() + .expect("canonicalize dir_path"); Self { - temp_dir: tempdir, - work_dir, + _temp_dir: tempdir, + work_dir: dir_path.clone(), + dir_path, env_vars: Default::default(), envfiles: vec![], } @@ -127,7 +133,7 @@ impl TestEnv { P: AsRef, C: Into>, { - let path = self.temp_dir.path().join(path); + let path = self.dir_path.join(path); self.assert_envfile_path_is_valid(&path); self.add_envfile_assume_valid(path, contents.into()) } @@ -214,7 +220,7 @@ impl TestEnv { /// Reference to the path of the temporary directory. pub fn temp_path(&self) -> &Path { - self.temp_dir.path() + &self.dir_path } /// Reference to the working directory the test will be run from. From 9219e808fc3aed9e50e4ca08baf86f5a972fad97 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Tue, 23 Jul 2024 10:59:32 +0100 Subject: [PATCH 34/65] Fix method signature consistancy --- test_util/src/testenv.rs | 3 ++- test_util/src/tests/testenv.rs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 91c9e1c2..706e888f 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -205,7 +205,7 @@ impl TestEnv { /// the envfile is created. /// /// Will create parent directories if they are missing. - pub fn add_child_dir(&self, path: impl AsRef) { + pub fn add_child_dir(&mut self, path: impl AsRef) -> &mut Self { let path = path.as_ref(); let child_dir = self.temp_path().join(path); if let Err(err) = fs::create_dir_all(child_dir) { @@ -216,6 +216,7 @@ impl TestEnv { err ); } + self } /// Reference to the path of the temporary directory. diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index 26290153..cc4e3cd8 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -348,14 +348,14 @@ mod add_child_dir { #[test] fn subdir() { - let testenv = TestEnv::init(); + let mut testenv = TestEnv::init(); testenv.add_child_dir("subdir"); assert_path_exists_in_testenv(&testenv, "subdir"); } #[test] fn allow_absolute_path() { - let testenv = TestEnv::init(); + let mut testenv = TestEnv::init(); let path = testenv.temp_path().join("subdir"); assert!(path.is_absolute()); testenv.add_child_dir(&path); @@ -364,7 +364,7 @@ mod add_child_dir { #[test] fn create_parents() { - let testenv = TestEnv::init(); + let mut testenv = TestEnv::init(); testenv.add_child_dir("subdir/subsubdir"); assert_path_exists_in_testenv(&testenv, "subdir/subsubdir"); } From 59b46840ca4f1804306bca8e09b380f6dda0f66a Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Tue, 23 Jul 2024 11:31:17 +0100 Subject: [PATCH 35/65] Add default existing var testenv --- test_util/src/testenv.rs | 8 ++++++++ test_util/src/tests/default_env.rs | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 706e888f..c51ddd20 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -87,6 +87,14 @@ where test_in_env(&testenv, test); } +/// Create a [`TestEnv`] without an envfile, but with the +/// default existing environment variable. +pub fn create_testenv_with_default_var() -> TestEnv { + let mut testenv = TestEnv::init(); + testenv.add_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); + testenv +} + impl TestEnv { /// Blank testing environment in a new temporary directory. /// diff --git a/test_util/src/tests/default_env.rs b/test_util/src/tests/default_env.rs index 4ac848e0..c2de3f89 100644 --- a/test_util/src/tests/default_env.rs +++ b/test_util/src/tests/default_env.rs @@ -22,3 +22,13 @@ fn envfile_loaded_vars_state() { assert_default_keys(); }); } + +#[test] +fn only_default_existing() { + let testenv = create_testenv_with_default_var(); + let envfile_path = testenv.temp_path().join(".env"); + test_in_env(&testenv, || { + assert_default_existing_var(); + assert!(!envfile_path.exists()); + }); +} From d4e7d63c3e5fd3914350cc73ab2ccc6956aedcf1 Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 30 Jul 2024 10:09:08 -0400 Subject: [PATCH 36/65] clippy::uninlined_format_args --- test_util/src/assertions.rs | 16 +++++----------- test_util/src/envfile.rs | 8 +++----- test_util/src/testenv.rs | 9 ++++----- test_util/src/tests/envfile.rs | 6 ++---- test_util/src/tests/envfile_builder.rs | 4 ++-- 5 files changed, 16 insertions(+), 27 deletions(-) diff --git a/test_util/src/assertions.rs b/test_util/src/assertions.rs index 8a169a62..3ced2dc2 100644 --- a/test_util/src/assertions.rs +++ b/test_util/src/assertions.rs @@ -28,13 +28,11 @@ pub fn assert_env_var(key: &str, expected: &str) { match env::var(key) { Ok(actual) => assert_eq!( expected, actual, - "\n\nFor Environment Variable `{}`:\n EXPECTED: `{}`\n ACTUAL: `{}`\n", - key, expected, actual + "\n\nFor Environment Variable `{key}`:\n EXPECTED: `{expected}`\n ACTUAL: `{actual}`\n", ), - Err(VarError::NotPresent) => panic!("env var `{}` not found", key), + Err(VarError::NotPresent) => panic!("env var `{key}` not found"), Err(VarError::NotUnicode(val)) => panic!( - "env var `{}` currently has invalid unicode: `{}`", - key, + "env var `{key}` currently has invalid unicode: `{}`", val.to_string_lossy() ), } @@ -43,13 +41,9 @@ pub fn assert_env_var(key: &str, expected: &str) { /// Assert environment variable is not currently set. pub fn assert_env_var_unset(key: &str) { match env::var(key) { - Ok(actual) => panic!( - "env var `{}` should not be set, currently it is: `{}`", - key, actual - ), + Ok(actual) => panic!("env var `{key}` should not be set, currently it is: `{actual}`",), Err(VarError::NotUnicode(val)) => panic!( - "env var `{}` should not be set, currently has invalid unicode: `{}`", - key, + "env var `{key}` should not be set, currently has invalid unicode: `{}`", val.to_string_lossy() ), _ => (), diff --git a/test_util/src/envfile.rs b/test_util/src/envfile.rs index 4d09d6ab..d564e1fc 100644 --- a/test_util/src/envfile.rs +++ b/test_util/src/envfile.rs @@ -8,8 +8,7 @@ use super::*; #[inline(always)] pub fn create_default_envfile() -> String { format!( - "{}={}\n{}={}", - DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE, DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE + "{DEFAULT_TEST_KEY}={DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}={DEFAULT_OVERRIDING_VALUE}", ) } @@ -17,8 +16,7 @@ pub fn create_default_envfile() -> String { #[inline(always)] pub fn create_invalid_envfile() -> String { format!( - "{}{}\n{}{}", - DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE, DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE + "{DEFAULT_TEST_KEY}{DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}{DEFAULT_OVERRIDING_VALUE}", ) } @@ -105,7 +103,7 @@ impl EnvFileBuilder { /// Add a key-value pair and newline pub fn add_key_value(&mut self, key: &str, value: &str) -> &mut Self { - self.add_strln(&format!("{}={}", key, value)) + self.add_strln(&format!("{key}={value}")) } /// Add a string without a newline diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index c51ddd20..fbfad7c5 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -218,10 +218,9 @@ impl TestEnv { let child_dir = self.temp_path().join(path); if let Err(err) = fs::create_dir_all(child_dir) { panic!( - "unable to create child directory: `{}` in `{}`: {}", + "unable to create child directory: `{}` in `{}`: {err}", path.display(), - self.temp_path().display(), - err + self.temp_path().display() ); } self @@ -267,7 +266,7 @@ impl TestEnv { panic!("key cannot be empty"); } if self.env_vars.contains_key(key) { - panic!("key already in testenv: {}", key); + panic!("key already in testenv: {key}"); } } } @@ -330,6 +329,6 @@ fn create_envfile(path: &Path, contents: &[u8]) { // call inner function if let Err(err) = create_env_file_inner(path, contents) { // handle any io::Result::Err - panic!("error creating envfile `{}`: {}", path.display(), err); + panic!("error creating envfile `{}`: {err}", path.display()); } } diff --git a/test_util/src/tests/envfile.rs b/test_util/src/tests/envfile.rs index 45239482..f488a81a 100644 --- a/test_util/src/tests/envfile.rs +++ b/test_util/src/tests/envfile.rs @@ -3,8 +3,7 @@ use super::*; #[test] fn create_default() { let expected = format!( - "{}={}\n{}={}", - DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE, DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE + "{DEFAULT_TEST_KEY}={DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}={DEFAULT_OVERRIDING_VALUE}", ); let actual = create_default_envfile(); assert_eq!( @@ -16,8 +15,7 @@ fn create_default() { #[test] fn create_invalid() { let expected = format!( - "{}{}\n{}{}", - DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE, DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE + "{DEFAULT_TEST_KEY}{DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}{DEFAULT_OVERRIDING_VALUE}", ); let actual = create_invalid_envfile(); assert_eq!( diff --git a/test_util/src/tests/envfile_builder.rs b/test_util/src/tests/envfile_builder.rs index cb3dc84a..88ce74c0 100644 --- a/test_util/src/tests/envfile_builder.rs +++ b/test_util/src/tests/envfile_builder.rs @@ -16,7 +16,7 @@ fn default_builds_empty() { fn add_key_empty_value() { let mut efb = EnvFileBuilder::new(); efb.add_key_value(DEFAULT_TEST_KEY, ""); - let expected = format!("{}=\n", DEFAULT_TEST_KEY); + let expected = format!("{DEFAULT_TEST_KEY}=\n"); assert_contents_str(efb, &expected); } @@ -24,7 +24,7 @@ fn add_key_empty_value() { fn add_key_value() { let mut efb = EnvFileBuilder::new(); efb.add_key_value(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); - let expected = format!("{}={}\n", DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); + let expected = format!("{DEFAULT_TEST_KEY}={DEFAULT_TEST_VALUE}\n"); assert_contents_str(efb, &expected); } From dc60d95294f2152cfd60372a02fa7f55a63a1410 Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 30 Jul 2024 10:16:12 -0400 Subject: [PATCH 37/65] clippy::manual_assert --- test_util/src/testenv.rs | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index fbfad7c5..047d38a7 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -201,9 +201,11 @@ impl TestEnv { .join(path.as_ref()) .canonicalize() .expect("canonicalize work_dir"); - if !self.work_dir.exists() { - panic!("work_dir does not exist: {}", self.work_dir.display()); - } + assert!( + self.work_dir.exists(), + "work_dir does not exist: {}", + self.work_dir.display() + ); self } @@ -253,21 +255,23 @@ impl TestEnv { } fn assert_envfile_path_is_valid(&self, path: &Path) { - if path == self.temp_path() { - panic!("path cannot be empty or the same as the temporary directory"); - } - if self.envfiles.iter().any(|f| f.path == path) { - panic!("envfile already in testenv: {}", path.display()); - } + assert!( + path != self.temp_path(), + "path cannot be empty or the same as the temporary directory" + ); + assert!( + !self.envfiles.iter().any(|f| f.path == path), + "envfile already in testenv: {}", + path.display() + ) } fn assert_env_var_is_valid(&self, key: &str) { - if key.is_empty() { - panic!("key cannot be empty"); - } - if self.env_vars.contains_key(key) { - panic!("key already in testenv: {key}"); - } + assert!(!key.is_empty(), "key cannot be empty"); + assert!( + !self.env_vars.contains_key(key), + "key already in testenv: {key}" + ) } } @@ -317,9 +321,7 @@ fn create_env(testenv: &TestEnv) { /// Create an envfile for use in tests. fn create_envfile(path: &Path, contents: &[u8]) { - if path.exists() { - panic!("envfile `{}` already exists", path.display()) - } + assert!(!path.exists(), "envfile `{}` already exists", path.display()); // inner function to group together io::Results fn create_env_file_inner(path: &Path, contents: &[u8]) -> io::Result<()> { let mut file = fs::File::create(path)?; From 91071274887e7d4b00e61d2dfad29f41aecc5a41 Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 30 Jul 2024 10:16:50 -0400 Subject: [PATCH 38/65] clippy::use_self --- test_util/src/testenv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 047d38a7..0ed1ab0d 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -277,7 +277,7 @@ impl TestEnv { impl Default for TestEnv { fn default() -> Self { - let mut testenv = TestEnv::init(); + let mut testenv = Self::init(); testenv.add_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); testenv.add_envfile(".env", create_default_envfile()); testenv From 4183f0c31ba251cf11b49c3f0feff67fb2786a31 Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 30 Jul 2024 10:21:11 -0400 Subject: [PATCH 39/65] clippy::cloned_instead_of_copied --- test_util/src/envfile.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_util/src/envfile.rs b/test_util/src/envfile.rs index d564e1fc..6c1d0fc9 100644 --- a/test_util/src/envfile.rs +++ b/test_util/src/envfile.rs @@ -132,7 +132,7 @@ impl EnvFileBuilder { pub fn insert_utf8_bom(&mut self) -> &mut Self { // https://www.compart.com/en/unicode/U+FEFF let bom = b"\xEF\xBB\xBF"; - self.contents.splice(0..0, bom.iter().cloned()); + self.contents.splice(0..0, bom.iter().copied()); self } } From e2642dfa06a6897a687233d9bfce8ea6b9d2bacc Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 30 Jul 2024 10:22:19 -0400 Subject: [PATCH 40/65] clippy::default_trait_access --- test_util/src/testenv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 0ed1ab0d..1f7de66e 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -110,7 +110,7 @@ impl TestEnv { _temp_dir: tempdir, work_dir: dir_path.clone(), dir_path, - env_vars: Default::default(), + env_vars: HashMap::default(), envfiles: vec![], } } From d39b060232e311c55fc32392971c238114c787d3 Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 30 Jul 2024 10:23:15 -0400 Subject: [PATCH 41/65] clippy::doc_markdown --- test_util/src/testenv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 1f7de66e..cdf174e3 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -117,7 +117,7 @@ impl TestEnv { /// Testing environment with custom envfile contents. /// - /// No pre-existing env_vars set. The envfile path is set to `.env`. The + /// No pre-existing env vars set. The envfile path is set to `.env`. The /// working directory is the created temporary directory. pub fn init_with_envfile(contents: impl Into>) -> Self { let mut testenv = Self::init(); From 91f4fa66289bc71006a2441b548913faa4fc0282 Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 30 Jul 2024 10:26:15 -0400 Subject: [PATCH 42/65] clippy::semicolon_if_nothing_returned --- test_util/src/testenv.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index cdf174e3..1b4b08f9 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -263,7 +263,7 @@ impl TestEnv { !self.envfiles.iter().any(|f| f.path == path), "envfile already in testenv: {}", path.display() - ) + ); } fn assert_env_var_is_valid(&self, key: &str) { @@ -271,7 +271,7 @@ impl TestEnv { assert!( !self.env_vars.contains_key(key), "key already in testenv: {key}" - ) + ); } } @@ -315,7 +315,7 @@ fn create_env(testenv: &TestEnv) { } for (key, value) in &testenv.env_vars { - env::set_var(key, value) + env::set_var(key, value); } } From cf8f41a89939c3c939f5a9a961fdbeddd248a37e Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 30 Jul 2024 10:34:14 -0400 Subject: [PATCH 43/65] test_util clippy::missing_const_for_fn --- test_util/src/envfile.rs | 2 +- test_util/src/testenv.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test_util/src/envfile.rs b/test_util/src/envfile.rs index 6c1d0fc9..10638711 100644 --- a/test_util/src/envfile.rs +++ b/test_util/src/envfile.rs @@ -47,7 +47,7 @@ pub struct EnvFileBuilder { } impl EnvFileBuilder { - pub fn new() -> Self { + pub const fn new() -> Self { Self { contents: Vec::new(), } diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 1b4b08f9..2cc1abb0 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -239,7 +239,7 @@ impl TestEnv { } /// Reference to environment variables that will be set **before** the test. - pub fn env_vars(&self) -> &EnvMap { + pub const fn env_vars(&self) -> &EnvMap { &self.env_vars } From da380d33fa849a3e86eacc97a4805e1008b672b7 Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 30 Jul 2024 10:36:22 -0400 Subject: [PATCH 44/65] test_util clippy::item_after_statements --- test_util/src/testenv.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 2cc1abb0..1f850f9c 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -321,13 +321,15 @@ fn create_env(testenv: &TestEnv) { /// Create an envfile for use in tests. fn create_envfile(path: &Path, contents: &[u8]) { - assert!(!path.exists(), "envfile `{}` already exists", path.display()); - // inner function to group together io::Results fn create_env_file_inner(path: &Path, contents: &[u8]) -> io::Result<()> { let mut file = fs::File::create(path)?; file.write_all(contents)?; file.sync_all() } + + assert!(!path.exists(), "envfile `{}` already exists", path.display()); + // inner function to group together io::Results + // call inner function if let Err(err) = create_env_file_inner(path, contents) { // handle any io::Result::Err From 2022513cd372bfe7415d340b1fe4e69ee3e6749e Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 30 Jul 2024 10:38:06 -0400 Subject: [PATCH 45/65] test_util clippy::inline_always --- test_util/src/envfile.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/test_util/src/envfile.rs b/test_util/src/envfile.rs index 10638711..46430d90 100644 --- a/test_util/src/envfile.rs +++ b/test_util/src/envfile.rs @@ -5,7 +5,6 @@ use super::*; /// [`DEFAULT_TEST_KEY`] set as [`DEFAULT_TEST_VALUE`] /// /// [`DEFAULT_EXISTING_KEY`] set as [`DEFAULT_OVERRIDING_VALUE`] -#[inline(always)] pub fn create_default_envfile() -> String { format!( "{DEFAULT_TEST_KEY}={DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}={DEFAULT_OVERRIDING_VALUE}", @@ -13,7 +12,6 @@ pub fn create_default_envfile() -> String { } /// Invalid due to missing `=` between key and value. -#[inline(always)] pub fn create_invalid_envfile() -> String { format!( "{DEFAULT_TEST_KEY}{DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}{DEFAULT_OVERRIDING_VALUE}", From 3fa8a6860b3a1566e148d1865333071c1d4b7d50 Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 30 Jul 2024 10:52:20 -0400 Subject: [PATCH 46/65] Add strict clippy lints to test_util crate --- test_util/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index ea775d0b..42976598 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -1,3 +1,6 @@ +#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)] +#![allow(clippy::must_use_candidate, clippy::missing_panics_doc, clippy::wildcard_imports, clippy::module_name_repetitions)] + //! Test environment setup, assertions and helpers. //! //! Setup a [`TestEnv`] and run your tests via [`test_in_env`]. The environment From 81b2d5f3981d78db55e51e5f7ad92cfba013295a Mon Sep 17 00:00:00 2001 From: Allan Zhang Date: Tue, 30 Jul 2024 11:16:22 -0400 Subject: [PATCH 47/65] rustfmt, allow clippy::should_panic_without_expect The [`should_panic_without_expect`](https://rust-lang.github.io/rust-clippy/master/index.html#/should_panic_without_expect) pedantic lint is failing CI. The lint is not that useful given our small tests. --- test_util/src/lib.rs | 8 +++++++- test_util/src/testenv.rs | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index 42976598..d45a50a0 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -1,5 +1,11 @@ #![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)] -#![allow(clippy::must_use_candidate, clippy::missing_panics_doc, clippy::wildcard_imports, clippy::module_name_repetitions)] +#![allow( + clippy::must_use_candidate, + clippy::missing_panics_doc, + clippy::wildcard_imports, + clippy::module_name_repetitions, + clippy::should_panic_without_expect +)] //! Test environment setup, assertions and helpers. //! diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 1f850f9c..96701c0d 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -327,7 +327,11 @@ fn create_envfile(path: &Path, contents: &[u8]) { file.sync_all() } - assert!(!path.exists(), "envfile `{}` already exists", path.display()); + assert!( + !path.exists(), + "envfile `{}` already exists", + path.display() + ); // inner function to group together io::Results // call inner function From b764212eaa1122c586d393e30380186676970f24 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Tue, 30 Jul 2024 17:06:48 +0100 Subject: [PATCH 48/65] Fix unnecessary assert messages --- test_util/src/tests/envfile.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/test_util/src/tests/envfile.rs b/test_util/src/tests/envfile.rs index f488a81a..073b2ca2 100644 --- a/test_util/src/tests/envfile.rs +++ b/test_util/src/tests/envfile.rs @@ -6,30 +6,21 @@ fn create_default() { "{DEFAULT_TEST_KEY}={DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}={DEFAULT_OVERRIDING_VALUE}", ); let actual = create_default_envfile(); - assert_eq!( - expected, actual, - "envfile should be created with default values" - ); + assert_eq!(expected, actual); } #[test] -fn create_invalid() { +fn create_without_equals() { let expected = format!( "{DEFAULT_TEST_KEY}{DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}{DEFAULT_OVERRIDING_VALUE}", ); let actual = create_invalid_envfile(); - assert_eq!( - expected, actual, - "envfile should be created without equals sign" - ); + assert_eq!(expected, actual); } #[test] fn create_custom() { let expected = expected_envfile(CUSTOM_VARS); let actual = create_custom_envfile(CUSTOM_VARS); - assert_eq!( - expected, actual, - "envfile should be created with custom values" - ); + assert_eq!(expected, actual); } From cf4cc4333233f05db8609869f3a22049506f08ce Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Tue, 30 Jul 2024 17:13:29 +0100 Subject: [PATCH 49/65] Update authors --- test_util/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/test_util/Cargo.toml b/test_util/Cargo.toml index 8368d21e..e392fd5f 100644 --- a/test_util/Cargo.toml +++ b/test_util/Cargo.toml @@ -3,6 +3,7 @@ name = "dotenvy_test_util" version = "0.1.0" authors = [ "Christopher Morton ", + "Allan Zhang ", ] description = "Test utilities for dotenvy" homepage = "https://github.com/allan2/dotenvy" From deef3f29a13ef1dbbb711e34692435ab0c6805e0 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Tue, 30 Jul 2024 17:16:09 +0100 Subject: [PATCH 50/65] Move dotenvy as dev-only dependency --- test_util/Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test_util/Cargo.toml b/test_util/Cargo.toml index e392fd5f..a45d5cb6 100644 --- a/test_util/Cargo.toml +++ b/test_util/Cargo.toml @@ -16,6 +16,8 @@ edition = "2021" rust-version = "1.68.0" [dependencies] -dotenvy = { path = "../dotenv", version = "0.15.7" } tempfile = "3.3.0" once_cell = "1.16.0" + +[dev-dependencies] +dotenvy = { path = "../dotenv", version = "0.15.7" } From 1efcba9eb25056d628389afa9a0a422ebaa05161 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Wed, 31 Jul 2024 10:31:30 +0100 Subject: [PATCH 51/65] Fix rustdoc dotenvy link --- test_util/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index d45a50a0..c4a682a3 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -58,6 +58,8 @@ //! // any changes to environment variables will be reset for other tests //! } //! ``` +//! +//! [`dotenvy`]: https://docs.rs/dotenvy mod assertions; mod envfile; From aa9f90765887692c320e94b5f7fca1c4549465f8 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Wed, 31 Jul 2024 10:44:33 +0100 Subject: [PATCH 52/65] Rename envfile to env_file To match env_var --- test_util/README.md | 22 ++-- test_util/src/assertions.rs | 2 +- test_util/src/{envfile.rs => env_file.rs} | 16 +-- test_util/src/lib.rs | 22 ++-- test_util/src/testenv.rs | 68 +++++------ test_util/src/tests/default_env.rs | 10 +- .../src/tests/{envfile.rs => env_file.rs} | 8 +- ...envfile_builder.rs => env_file_builder.rs} | 16 +-- test_util/src/tests/mod.rs | 26 ++--- test_util/src/tests/testenv.rs | 110 +++++++++--------- 10 files changed, 150 insertions(+), 150 deletions(-) rename test_util/src/{envfile.rs => env_file.rs} (90%) rename test_util/src/tests/{envfile.rs => env_file.rs} (71%) rename test_util/src/tests/{envfile_builder.rs => env_file_builder.rs} (84%) diff --git a/test_util/README.md b/test_util/README.md index f3dad08e..de8a3c12 100644 --- a/test_util/README.md +++ b/test_util/README.md @@ -17,7 +17,7 @@ By setting up a `TestEnv`, and running a closure via `test_in_env`. - Lock the environment from other tests - Store the existing environment variables - Add any custom env_vars to the environment -- Create any custom envfiles in the temporary directory +- Create any custom env files in the temporary directory **In the closure** you can use the assertion functions to test the environment. @@ -48,10 +48,10 @@ fn dotenv_override_existing_key() { // with an existing environment variable testenv.add_env_var(EXISTING_KEY, EXISTING_VAL); - // with an envfile that overrides it - testenv.add_envfile( + // with an env file that overrides it + testenv.add_env_file( ".env", - create_custom_envfile(&[(EXISTING_KEY, OVERRIDING_VAL)]), + create_custom_env_file(&[(EXISTING_KEY, OVERRIDING_VAL)]), ); // execute a closure in the testing environment @@ -84,14 +84,14 @@ The default `TestEnv` has 1 existing environment variable: - `DEFAULT_EXISTING_KEY` set to `DEFAULT_EXISTING_VAL` -It has an envfile `.env` that sets: +It has an env file `.env` that sets: - `DEFAULT_TEST_KEY` to `DEFAULT_TEST_VAL` - `DEFAULT_EXISTING_KEY` to `DEFAULT_OVERRIDING_VAL` ### Customised Envfile -Use the `EnvFileBuilder` to manipulate the content of an envfile. Useful +Use the `EnvFileBuilder` to manipulate the content of an env file. Useful for byte-order-mark(BOM) testing, and other edge cases. ```rust @@ -99,12 +99,12 @@ use dotenvy_test_util::*; use dotenvy::dotenv; #[test] -fn comments_ignored_in_utf8bom_envfile() { +fn comments_ignored_in_utf8bom_env_file() { let mut efb = EnvFileBuilder::new(); efb.insert_utf8_bom(); efb.add_strln("# TEST_KEY=TEST_VAL"); - let testenv = TestEnv::init_with_envfile(efb); + let testenv = TestEnv::init_with_env_file(efb); test_in_env(&testenv, || { dotenv().expect(".env should be loaded"); @@ -113,7 +113,7 @@ fn comments_ignored_in_utf8bom_envfile() { } ``` -Or use anything that can be converted to a `Vec` if your envfile is +Or use anything that can be converted to a `Vec` if your env_file is simple. ```rust @@ -122,9 +122,9 @@ use dotenvy::dotenv; #[test] fn comments_ignored() { - let envfile = "# TEST_KEY=TEST_VAL\n"; + let env_file = "# TEST_KEY=TEST_VAL\n"; - let testenv = TestEnv::init_with_envfile(envfile); + let testenv = TestEnv::init_with_env_file(env_file); test_in_env(&testenv, || { dotenv().expect(".env should be loaded"); diff --git a/test_util/src/assertions.rs b/test_util/src/assertions.rs index 3ced2dc2..402f65d4 100644 --- a/test_util/src/assertions.rs +++ b/test_util/src/assertions.rs @@ -57,7 +57,7 @@ pub fn assert_default_keys_unset() { } /// Assert default testing environment variables are set. -/// Assuming the default envfile is loaded. +/// Assuming the default env file is loaded. pub fn assert_default_keys() { assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); assert_default_existing_var(); diff --git a/test_util/src/envfile.rs b/test_util/src/env_file.rs similarity index 90% rename from test_util/src/envfile.rs rename to test_util/src/env_file.rs index 46430d90..2df5fd53 100644 --- a/test_util/src/envfile.rs +++ b/test_util/src/env_file.rs @@ -1,42 +1,42 @@ use super::*; -/// Create the default envfile contents. +/// Create the default env file contents. /// /// [`DEFAULT_TEST_KEY`] set as [`DEFAULT_TEST_VALUE`] /// /// [`DEFAULT_EXISTING_KEY`] set as [`DEFAULT_OVERRIDING_VALUE`] -pub fn create_default_envfile() -> String { +pub fn create_default_env_file() -> String { format!( "{DEFAULT_TEST_KEY}={DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}={DEFAULT_OVERRIDING_VALUE}", ) } /// Invalid due to missing `=` between key and value. -pub fn create_invalid_envfile() -> String { +pub fn create_invalid_env_file() -> String { format!( "{DEFAULT_TEST_KEY}{DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}{DEFAULT_OVERRIDING_VALUE}", ) } -/// Create an envfile with custom key-value pairs. +/// Create an env file with custom key-value pairs. /// /// ## Example /// /// ```no_run -/// # use dotenvy_test_util::create_custom_envfile; -/// let contents = create_custom_envfile(&[ +/// # use dotenvy_test_util::create_custom_env_file; +/// let contents = create_custom_env_file(&[ /// ("CUSTOM_KEY", "test_val"), /// ("ANOTHER_KEY", "another_val"), /// ]); /// assert_eq!(contents, "CUSTOM_KEY=test_val\nANOTHER_KEY=another_val\n"); /// ``` -pub fn create_custom_envfile(env_vars: &[(&str, &str)]) -> String { +pub fn create_custom_env_file(env_vars: &[(&str, &str)]) -> String { let mut efb = EnvFileBuilder::new(); efb.add_vars(env_vars); efb.into_owned_string() } -/// Advanced test-envfile constructor. +/// Advanced test-env file constructor. /// /// Represented as bytes to allow for advanced manipulation and BOM testing. #[derive(Debug, Default)] diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index c4a682a3..077bac65 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -16,11 +16,11 @@ //! - different directory layouts, //! - custom `.env` file contents, //! - multiple `.env` files, -//! - custom envfile name/path. +//! - custom env file name/path. //! -//! Use the `create_` helper functions, such as [`create_custom_envfile`], to +//! Use the `create_` helper functions, such as [`create_custom_env_file`], to //! generate the `.env` file contents. If you need more control of the -//! envfile's bytes, use the [`EnvFileBuilder`]. +//! env file's bytes, use the [`EnvFileBuilder`]. //! //! In your tests, call the [`dotenvy`] API, then make use of the `assert_` //! helpers, such as [`assert_env_var`] and [`assert_env_var_unset`], to check @@ -44,10 +44,10 @@ //! // with an existing environment variable //! testenv.add_env_var(EXISTING_KEY, EXISTING_VAL); //! -//! // with an envfile that overrides it -//! testenv.add_envfile( +//! // with an env file that overrides it +//! testenv.add_env_file( //! ".env", -//! create_custom_envfile(&[(EXISTING_KEY, OVERRIDING_VAL)]), +//! create_custom_env_file(&[(EXISTING_KEY, OVERRIDING_VAL)]), //! ); //! //! // execute a closure in the testing environment @@ -62,24 +62,24 @@ //! [`dotenvy`]: https://docs.rs/dotenvy mod assertions; -mod envfile; +mod env_file; mod testenv; #[cfg(test)] mod tests; pub use assertions::*; -pub use envfile::*; +pub use env_file::*; pub use testenv::*; -/// Default key used in envfile +/// Default key used in env file pub const DEFAULT_TEST_KEY: &str = "DEFAULT_TEST_KEY"; -/// Default value used in envfile +/// Default value used in env file pub const DEFAULT_TEST_VALUE: &str = "default_test_val"; /// Default existing key set before test is run pub const DEFAULT_EXISTING_KEY: &str = "DEFAULT_EXISTING_KEY"; /// Default existing value set before test is run pub const DEFAULT_EXISTING_VALUE: &str = "loaded_from_env"; -/// Default overriding value in envfile +/// Default overriding value in env file pub const DEFAULT_OVERRIDING_VALUE: &str = "loaded_from_file"; diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 96701c0d..bdd0638f 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -1,4 +1,4 @@ -use super::{create_default_envfile, DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE}; +use super::{create_default_env_file, DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE}; use once_cell::sync::OnceCell; use std::{ collections::HashMap, @@ -18,12 +18,12 @@ static ENV_LOCKER: OnceCell>> = OnceCell::new(); /// A test environment. /// /// Will create a new temporary directory. Use its builder methods to configure -/// the directory structure, preset variables, envfile name and contents, and +/// the directory structure, preset variables, env file name and contents, and /// the working directory to run the test from. /// /// Creation methods: -/// - [`TestEnv::init`]: blank environment (no envfile) -/// - [`TestEnv::init_with_envfile`]: blank environment with a custom `.env` +/// - [`TestEnv::init`]: blank environment (no env file) +/// - [`TestEnv::init_with_env_file`]: blank environment with a custom `.env` /// - [`TestEnv::default`]: default testing environment (1 existing var and 2 /// set in a `.env` file) #[derive(Debug)] @@ -33,7 +33,7 @@ pub struct TestEnv { dir_path: PathBuf, work_dir: PathBuf, env_vars: EnvMap, - envfiles: Vec, + env_files: Vec, } #[derive(Debug, Clone)] @@ -87,7 +87,7 @@ where test_in_env(&testenv, test); } -/// Create a [`TestEnv`] without an envfile, but with the +/// Create a [`TestEnv`] without an env file, but with the /// default existing environment variable. pub fn create_testenv_with_default_var() -> TestEnv { let mut testenv = TestEnv::init(); @@ -98,7 +98,7 @@ pub fn create_testenv_with_default_var() -> TestEnv { impl TestEnv { /// Blank testing environment in a new temporary directory. /// - /// No envfile or pre-existing variables set. The working directory is the + /// No env file or pre-existing variables set. The working directory is the /// created temporary directory. pub fn init() -> Self { let tempdir = tempdir().expect("create tempdir"); @@ -111,21 +111,21 @@ impl TestEnv { work_dir: dir_path.clone(), dir_path, env_vars: HashMap::default(), - envfiles: vec![], + env_files: vec![], } } - /// Testing environment with custom envfile contents. + /// Testing environment with custom env file contents. /// - /// No pre-existing env vars set. The envfile path is set to `.env`. The + /// No pre-existing env vars set. The env file path is set to `.env`. The /// working directory is the created temporary directory. - pub fn init_with_envfile(contents: impl Into>) -> Self { + pub fn init_with_env_file(contents: impl Into>) -> Self { let mut testenv = Self::init(); - testenv.add_envfile(".env", contents); + testenv.add_env_file(".env", contents); testenv } - /// Add an individual envfile. + /// Add an individual env file. /// /// ## Arguments /// @@ -135,15 +135,15 @@ impl TestEnv { /// ## Panics /// /// - if the path is empty or the same as the temporary directory - /// - if the envfile already exists - pub fn add_envfile(&mut self, path: P, contents: C) -> &mut Self + /// - if the env file already exists + pub fn add_env_file(&mut self, path: P, contents: C) -> &mut Self where P: AsRef, C: Into>, { let path = self.dir_path.join(path); - self.assert_envfile_path_is_valid(&path); - self.add_envfile_assume_valid(path, contents.into()) + self.assert_env_file_path_is_valid(&path); + self.add_env_file_assume_valid(path, contents.into()) } /// Add an individual environment variable. @@ -212,7 +212,7 @@ impl TestEnv { /// Create a child folder within the temporary directory. /// /// This will not change the working directory the test is run in, or where - /// the envfile is created. + /// the env file is created. /// /// Will create parent directories if they are missing. pub fn add_child_dir(&mut self, path: impl AsRef) -> &mut Self { @@ -244,24 +244,24 @@ impl TestEnv { } /// Get a reference to the environment files that will created - pub fn envfiles(&self) -> &[EnvFile] { - &self.envfiles + pub fn env_files(&self) -> &[EnvFile] { + &self.env_files } - fn add_envfile_assume_valid(&mut self, path: PathBuf, contents: Vec) -> &mut Self { - let envfile = EnvFile { path, contents }; - self.envfiles.push(envfile); + fn add_env_file_assume_valid(&mut self, path: PathBuf, contents: Vec) -> &mut Self { + let env_file = EnvFile { path, contents }; + self.env_files.push(env_file); self } - fn assert_envfile_path_is_valid(&self, path: &Path) { + fn assert_env_file_path_is_valid(&self, path: &Path) { assert!( path != self.temp_path(), "path cannot be empty or the same as the temporary directory" ); assert!( - !self.envfiles.iter().any(|f| f.path == path), - "envfile already in testenv: {}", + !self.env_files.iter().any(|f| f.path == path), + "env_file already in testenv: {}", path.display() ); } @@ -279,7 +279,7 @@ impl Default for TestEnv { fn default() -> Self { let mut testenv = Self::init(); testenv.add_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); - testenv.add_envfile(".env", create_default_envfile()); + testenv.add_env_file(".env", create_default_env_file()); testenv } } @@ -306,12 +306,12 @@ fn reset_env(original_env: &EnvMap) { /// Create an environment to run tests in. /// -/// Writes the envfiles, sets the working directory, and sets environment vars. +/// Writes the env files, sets the working directory, and sets environment vars. fn create_env(testenv: &TestEnv) { env::set_current_dir(&testenv.work_dir).expect("setting working directory"); - for EnvFile { path, contents } in &testenv.envfiles { - create_envfile(path, contents); + for EnvFile { path, contents } in &testenv.env_files { + create_env_file(path, contents); } for (key, value) in &testenv.env_vars { @@ -319,8 +319,8 @@ fn create_env(testenv: &TestEnv) { } } -/// Create an envfile for use in tests. -fn create_envfile(path: &Path, contents: &[u8]) { +/// Create an env file for use in tests. +fn create_env_file(path: &Path, contents: &[u8]) { fn create_env_file_inner(path: &Path, contents: &[u8]) -> io::Result<()> { let mut file = fs::File::create(path)?; file.write_all(contents)?; @@ -329,7 +329,7 @@ fn create_envfile(path: &Path, contents: &[u8]) { assert!( !path.exists(), - "envfile `{}` already exists", + "env_file `{}` already exists", path.display() ); // inner function to group together io::Results @@ -337,6 +337,6 @@ fn create_envfile(path: &Path, contents: &[u8]) { // call inner function if let Err(err) = create_env_file_inner(path, contents) { // handle any io::Result::Err - panic!("error creating envfile `{}`: {err}", path.display()); + panic!("error creating env_file `{}`: {err}", path.display()); } } diff --git a/test_util/src/tests/default_env.rs b/test_util/src/tests/default_env.rs index c2de3f89..e501ded1 100644 --- a/test_util/src/tests/default_env.rs +++ b/test_util/src/tests/default_env.rs @@ -9,13 +9,13 @@ fn vars_state() { } #[test] -fn envfile_exists() { +fn env_file_exists() { let testenv = TestEnv::default(); - assert_envfiles_in_testenv(&testenv); + assert_env_files_in_testenv(&testenv); } #[test] -fn envfile_loaded_vars_state() { +fn env_file_loaded_vars_state() { test_in_default_env(|| { dotenvy::dotenv().expect(DOTENV_EXPECT); // dotenv() does not override existing var @@ -26,9 +26,9 @@ fn envfile_loaded_vars_state() { #[test] fn only_default_existing() { let testenv = create_testenv_with_default_var(); - let envfile_path = testenv.temp_path().join(".env"); + let env_file_path = testenv.temp_path().join(".env"); test_in_env(&testenv, || { assert_default_existing_var(); - assert!(!envfile_path.exists()); + assert!(!env_file_path.exists()); }); } diff --git a/test_util/src/tests/envfile.rs b/test_util/src/tests/env_file.rs similarity index 71% rename from test_util/src/tests/envfile.rs rename to test_util/src/tests/env_file.rs index 073b2ca2..23d919b2 100644 --- a/test_util/src/tests/envfile.rs +++ b/test_util/src/tests/env_file.rs @@ -5,7 +5,7 @@ fn create_default() { let expected = format!( "{DEFAULT_TEST_KEY}={DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}={DEFAULT_OVERRIDING_VALUE}", ); - let actual = create_default_envfile(); + let actual = create_default_env_file(); assert_eq!(expected, actual); } @@ -14,13 +14,13 @@ fn create_without_equals() { let expected = format!( "{DEFAULT_TEST_KEY}{DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}{DEFAULT_OVERRIDING_VALUE}", ); - let actual = create_invalid_envfile(); + let actual = create_invalid_env_file(); assert_eq!(expected, actual); } #[test] fn create_custom() { - let expected = expected_envfile(CUSTOM_VARS); - let actual = create_custom_envfile(CUSTOM_VARS); + let expected = expected_env_file(CUSTOM_VARS); + let actual = create_custom_env_file(CUSTOM_VARS); assert_eq!(expected, actual); } diff --git a/test_util/src/tests/envfile_builder.rs b/test_util/src/tests/env_file_builder.rs similarity index 84% rename from test_util/src/tests/envfile_builder.rs rename to test_util/src/tests/env_file_builder.rs index 88ce74c0..e39dcd30 100644 --- a/test_util/src/tests/envfile_builder.rs +++ b/test_util/src/tests/env_file_builder.rs @@ -33,7 +33,7 @@ fn add_multiple_key_values() { let mut efb = EnvFileBuilder::new(); efb.add_key_value(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); efb.add_key_value(DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE); - let expected = expected_envfile(&[ + let expected = expected_env_file(&[ (DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE), (DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE), ]); @@ -44,7 +44,7 @@ fn add_multiple_key_values() { fn add_vars() { let mut efb = EnvFileBuilder::new(); efb.add_vars(CUSTOM_VARS); - let expected = expected_envfile(CUSTOM_VARS); + let expected = expected_env_file(CUSTOM_VARS); assert_contents_str(efb, &expected); } @@ -86,24 +86,24 @@ fn add_strln() { #[test] fn from_vec_u8() { - let vec: Vec = Vec::from(create_default_envfile()); + let vec: Vec = Vec::from(create_default_env_file()); let efb = EnvFileBuilder::from(vec); - assert_contents_str(efb, &create_default_envfile()); + assert_contents_str(efb, &create_default_env_file()); } #[test] fn to_vec_u8() { let mut efb = EnvFileBuilder::new(); - efb.add_str(create_default_envfile().as_str()); + efb.add_str(create_default_env_file().as_str()); let vec = Vec::from(efb); - let expected = create_default_envfile().into_bytes(); + let expected = create_default_env_file().into_bytes(); assert_eq!(expected, vec); } #[test] fn from_string() { - let efb = EnvFileBuilder::from(create_default_envfile()); - assert_contents_str(efb, &create_default_envfile()); + let efb = EnvFileBuilder::from(create_default_env_file()); + assert_contents_str(efb, &create_default_env_file()); } fn assert_contents_empty(efb: EnvFileBuilder) { diff --git a/test_util/src/tests/mod.rs b/test_util/src/tests/mod.rs index 55921981..56cc1149 100644 --- a/test_util/src/tests/mod.rs +++ b/test_util/src/tests/mod.rs @@ -3,8 +3,8 @@ use std::path::Path; use super::*; mod default_env; -mod envfile; -mod envfile_builder; +mod env_file; +mod env_file_builder; mod testenv; const CUSTOM_VARS: &[(&str, &str)] = &[ @@ -14,17 +14,17 @@ const CUSTOM_VARS: &[(&str, &str)] = &[ const DOTENV_EXPECT: &str = "TestEnv should have .env file"; -fn assert_envfiles_in_testenv(testenv: &TestEnv) { - let files = testenv.envfiles(); +fn assert_env_files_in_testenv(testenv: &TestEnv) { + let files = testenv.env_files(); test_in_env(testenv, || { for EnvFile { path, contents } in files { - assert_envfile(path, contents); + assert_env_file(path, contents); } }); } -fn assert_envfile(path: &Path, expected: &[u8]) { +fn assert_env_file(path: &Path, expected: &[u8]) { assert!(path.exists(), "{} should exist in testenv", path.display()); let actual = std::fs::read(path) @@ -51,13 +51,13 @@ fn assert_path_exists_in_testenv(testenv: &TestEnv, path: impl AsRef) { assert!(path.exists(), "{} should exist in testenv", path.display()); } -fn expected_envfile(env_vars: &[(&str, &str)]) -> String { - let mut envfile = String::new(); +fn expected_env_file(env_vars: &[(&str, &str)]) -> String { + let mut env_file = String::new(); for (key, value) in env_vars { - envfile.push_str(key); - envfile.push('='); - envfile.push_str(value); - envfile.push('\n'); + env_file.push_str(key); + env_file.push('='); + env_file.push_str(value); + env_file.push('\n'); } - envfile + env_file } diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index cc4e3cd8..1b906791 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -11,12 +11,12 @@ mod init { } #[test] - fn no_envfile() { + fn no_env_file() { let init_testenv = TestEnv::init(); - let envfile_path = init_testenv.temp_path().join(".env"); + let env_file_path = init_testenv.temp_path().join(".env"); test_in_env(&init_testenv, || { - assert!(!envfile_path.exists()); + assert!(!env_file_path.exists()); assert!(dotenv().is_err()); }); } @@ -34,30 +34,30 @@ mod init { } #[test] - fn envfiles_are_empty() { + fn env_files_are_empty() { let testenv = TestEnv::init(); - assert!(testenv.envfiles().is_empty()); + assert!(testenv.env_files().is_empty()); } } -mod init_with_envfile { +mod init_with_env_file { use super::*; #[test] - fn default_envfile_vars_state() { - let testenv = init_default_envfile_testenv(); + fn default_env_file_vars_state() { + let testenv = init_default_env_file_testenv(); assert_default_keys_not_set_in_testenv(&testenv); } #[test] - fn default_envfile_exists() { - let testenv = init_default_envfile_testenv(); - assert_envfiles_in_testenv(&testenv); + fn default_env_file_exists() { + let testenv = init_default_env_file_testenv(); + assert_env_files_in_testenv(&testenv); } #[test] - fn default_envfile_loaded_vars_state() { - let testenv = init_default_envfile_testenv(); + fn default_env_file_loaded_vars_state() { + let testenv = init_default_env_file_testenv(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); // dotenv() does not override existing var @@ -68,8 +68,8 @@ mod init_with_envfile { } #[test] - fn custom_envfile_vars_state() { - let testenv = init_custom_envfile_testenv(); + fn custom_env_file_vars_state() { + let testenv = init_custom_env_file_testenv(); test_in_env(&testenv, || { assert_default_keys_unset(); for (key, _) in CUSTOM_VARS { @@ -79,14 +79,14 @@ mod init_with_envfile { } #[test] - fn custom_envfile_exists() { - let testenv = init_custom_envfile_testenv(); - assert_envfiles_in_testenv(&testenv); + fn custom_env_file_exists() { + let testenv = init_custom_env_file_testenv(); + assert_env_files_in_testenv(&testenv); } #[test] - fn custom_envfile_loaded_vars_state() { - let testenv = init_custom_envfile_testenv(); + fn custom_env_file_loaded_vars_state() { + let testenv = init_custom_env_file_testenv(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); assert_default_keys_unset(); @@ -95,14 +95,14 @@ mod init_with_envfile { } #[test] - fn empty_envfile_exists() { - let testenv = init_empty_envfile_testenv(); - assert_envfiles_in_testenv(&testenv); + fn empty_env_file_exists() { + let testenv = init_empty_env_file_testenv(); + assert_env_files_in_testenv(&testenv); } #[test] - fn empty_envfile_loaded_vars_state() { - let testenv = init_empty_envfile_testenv(); + fn empty_env_file_loaded_vars_state() { + let testenv = init_empty_env_file_testenv(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); assert_default_keys_unset(); @@ -110,80 +110,80 @@ mod init_with_envfile { } #[test] - fn custom_bom_envfile_exists() { - let testenv = init_custom_bom_envfile_testenv(); - assert_envfiles_in_testenv(&testenv); + fn custom_bom_env_file_exists() { + let testenv = init_custom_bom_env_file_testenv(); + assert_env_files_in_testenv(&testenv); } #[test] - fn custom_bom_envfile_loaded_vars_state() { - let testenv = init_custom_bom_envfile_testenv(); + fn custom_bom_env_file_loaded_vars_state() { + let testenv = init_custom_bom_env_file_testenv(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); }); } - fn init_default_envfile_testenv() -> TestEnv { - let envfile = create_default_envfile(); - TestEnv::init_with_envfile(envfile) + fn init_default_env_file_testenv() -> TestEnv { + let env_file = create_default_env_file(); + TestEnv::init_with_env_file(env_file) } - fn init_custom_envfile_testenv() -> TestEnv { - let envfile = create_custom_envfile(CUSTOM_VARS); - TestEnv::init_with_envfile(envfile) + fn init_custom_env_file_testenv() -> TestEnv { + let env_file = create_custom_env_file(CUSTOM_VARS); + TestEnv::init_with_env_file(env_file) } - fn init_empty_envfile_testenv() -> TestEnv { - TestEnv::init_with_envfile([]) + fn init_empty_env_file_testenv() -> TestEnv { + TestEnv::init_with_env_file([]) } - fn init_custom_bom_envfile_testenv() -> TestEnv { + fn init_custom_bom_env_file_testenv() -> TestEnv { let mut efb = EnvFileBuilder::new(); efb.add_key_value(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); efb.insert_utf8_bom(); - let envfile = efb.into_owned_string(); - TestEnv::init_with_envfile(envfile) + let env_file = efb.into_owned_string(); + TestEnv::init_with_env_file(env_file) } } -mod add_envfile { +mod add_env_file { use super::*; #[test] #[should_panic] fn panics_add_twice() { let mut testenv = TestEnv::init(); - testenv.add_envfile(".env", create_default_envfile()); - testenv.add_envfile(".env", create_custom_envfile(CUSTOM_VARS)); + testenv.add_env_file(".env", create_default_env_file()); + testenv.add_env_file(".env", create_custom_env_file(CUSTOM_VARS)); } #[test] #[should_panic] fn panics_same_path_as_init() { - let mut testenv = TestEnv::init_with_envfile(create_default_envfile()); - testenv.add_envfile(".env", create_default_envfile()); + let mut testenv = TestEnv::init_with_env_file(create_default_env_file()); + testenv.add_env_file(".env", create_default_env_file()); } #[test] #[should_panic] fn panics_same_path_as_default() { let mut testenv = TestEnv::default(); - testenv.add_envfile(".env", create_invalid_envfile()); + testenv.add_env_file(".env", create_invalid_env_file()); } #[test] #[should_panic] fn panics_path() { let mut testenv = TestEnv::init(); - testenv.add_envfile("", create_default_envfile()); + testenv.add_env_file("", create_default_env_file()); } #[test] fn allow_empty_contents() { let mut testenv = TestEnv::init(); - testenv.add_envfile(".env", []); - assert_envfiles_in_testenv(&testenv); + testenv.add_env_file(".env", []); + assert_env_files_in_testenv(&testenv); } #[test] @@ -191,16 +191,16 @@ mod add_envfile { let mut testenv = TestEnv::init(); let path = testenv.temp_path().join(".env"); assert!(path.is_absolute()); - testenv.add_envfile(&path, create_default_envfile()); - assert_envfiles_in_testenv(&testenv); + testenv.add_env_file(&path, create_default_env_file()); + assert_env_files_in_testenv(&testenv); } #[test] fn two_files() { let mut testenv = TestEnv::init(); - testenv.add_envfile(".env", create_default_envfile()); - testenv.add_envfile(".env.local", create_custom_envfile(CUSTOM_VARS)); - assert_envfiles_in_testenv(&testenv); + testenv.add_env_file(".env", create_default_env_file()); + testenv.add_env_file(".env.local", create_custom_env_file(CUSTOM_VARS)); + assert_env_files_in_testenv(&testenv); } } From ce6e00bcc91828a2a0211607ecf8ea7b4f911a22 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Wed, 31 Jul 2024 10:51:46 +0100 Subject: [PATCH 53/65] Add is_empty impl to EnvFileBuilder --- test_util/src/env_file.rs | 5 +++++ test_util/src/tests/env_file_builder.rs | 9 ++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test_util/src/env_file.rs b/test_util/src/env_file.rs index 2df5fd53..d7753ff3 100644 --- a/test_util/src/env_file.rs +++ b/test_util/src/env_file.rs @@ -84,6 +84,11 @@ impl EnvFileBuilder { &self.contents } + /// Returns true when the contents of the builder is empty. + pub fn is_empty(&self) -> bool { + self.contents.is_empty() + } + /// Add a slice of key-value pairs, separated by newlines. /// /// Includes a trailing newline. diff --git a/test_util/src/tests/env_file_builder.rs b/test_util/src/tests/env_file_builder.rs index e39dcd30..cd9b7c9d 100644 --- a/test_util/src/tests/env_file_builder.rs +++ b/test_util/src/tests/env_file_builder.rs @@ -3,13 +3,13 @@ use super::*; #[test] fn new_builds_empty() { let efb = EnvFileBuilder::new(); - assert_contents_empty(efb); + assert!(efb.is_empty()); } #[test] fn default_builds_empty() { let efb = EnvFileBuilder::default(); - assert_contents_empty(efb); + assert!(efb.is_empty()); } #[test] @@ -106,11 +106,6 @@ fn from_string() { assert_contents_str(efb, &create_default_env_file()); } -fn assert_contents_empty(efb: EnvFileBuilder) { - let contents = efb.into_owned_bytes(); - assert!(contents.is_empty()); -} - fn assert_contents_str(efb: EnvFileBuilder, expected: &str) { let contents = efb.into_owned_string(); assert_eq!(expected, contents,); From 05c98188d62411f252005fc9e0f52b2d67105cb5 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Wed, 31 Jul 2024 11:16:17 +0100 Subject: [PATCH 54/65] Add more trait impls to EnvFileBuilder - Clone - PartialEq - Eq - PartialEq to String and &str - PartialEq to Vec and &[u8] - AsRef<[u8]> --- test_util/src/env_file.rs | 56 ++++++++++++++++++++++++- test_util/src/tests/env_file_builder.rs | 27 +++++------- 2 files changed, 66 insertions(+), 17 deletions(-) diff --git a/test_util/src/env_file.rs b/test_util/src/env_file.rs index d7753ff3..bd640f56 100644 --- a/test_util/src/env_file.rs +++ b/test_util/src/env_file.rs @@ -39,7 +39,7 @@ pub fn create_custom_env_file(env_vars: &[(&str, &str)]) -> String { /// Advanced test-env file constructor. /// /// Represented as bytes to allow for advanced manipulation and BOM testing. -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct EnvFileBuilder { contents: Vec, } @@ -159,3 +159,57 @@ impl From for EnvFileBuilder { } } } + +impl AsRef<[u8]> for EnvFileBuilder { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl PartialEq for EnvFileBuilder { + fn eq(&self, other: &String) -> bool { + self.as_bytes() == other.as_bytes() + } +} + +impl PartialEq for EnvFileBuilder { + fn eq(&self, other: &str) -> bool { + self.as_bytes() == other.as_bytes() + } +} + +impl PartialEq> for EnvFileBuilder { + fn eq(&self, other: &Vec) -> bool { + self.as_bytes() == other + } +} + +impl PartialEq<[u8]> for EnvFileBuilder { + fn eq(&self, other: &[u8]) -> bool { + self.as_bytes() == other + } +} + +impl PartialEq for String { + fn eq(&self, other: &EnvFileBuilder) -> bool { + self.as_bytes() == other.as_bytes() + } +} + +impl PartialEq for &str { + fn eq(&self, other: &EnvFileBuilder) -> bool { + self.as_bytes() == other.as_bytes() + } +} + +impl PartialEq for Vec { + fn eq(&self, other: &EnvFileBuilder) -> bool { + self == other.as_bytes() + } +} + +impl PartialEq for &[u8] { + fn eq(&self, other: &EnvFileBuilder) -> bool { + *self == other.as_bytes() + } +} diff --git a/test_util/src/tests/env_file_builder.rs b/test_util/src/tests/env_file_builder.rs index cd9b7c9d..9fbd0fae 100644 --- a/test_util/src/tests/env_file_builder.rs +++ b/test_util/src/tests/env_file_builder.rs @@ -17,7 +17,7 @@ fn add_key_empty_value() { let mut efb = EnvFileBuilder::new(); efb.add_key_value(DEFAULT_TEST_KEY, ""); let expected = format!("{DEFAULT_TEST_KEY}=\n"); - assert_contents_str(efb, &expected); + assert_eq!(expected, efb); } #[test] @@ -25,7 +25,7 @@ fn add_key_value() { let mut efb = EnvFileBuilder::new(); efb.add_key_value(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); let expected = format!("{DEFAULT_TEST_KEY}={DEFAULT_TEST_VALUE}\n"); - assert_contents_str(efb, &expected); + assert_eq!(expected, efb); } #[test] @@ -37,7 +37,7 @@ fn add_multiple_key_values() { (DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE), (DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE), ]); - assert_contents_str(efb, &expected); + assert_eq!(expected, efb); } #[test] @@ -45,28 +45,28 @@ fn add_vars() { let mut efb = EnvFileBuilder::new(); efb.add_vars(CUSTOM_VARS); let expected = expected_env_file(CUSTOM_VARS); - assert_contents_str(efb, &expected); + assert_eq!(expected, efb); } #[test] fn add_str() { let mut efb = EnvFileBuilder::new(); efb.add_str("test"); - assert_contents_str(efb, "test"); + assert_eq!("test", efb); } #[test] fn add_bytes() { let mut efb = EnvFileBuilder::new(); efb.add_bytes(b"test"); - assert_contents_str(efb, "test"); + assert_eq!("test", efb); } #[test] fn add_byte() { let mut efb = EnvFileBuilder::new(); efb.add_byte(b't'); - assert_contents_str(efb, "t"); + assert_eq!("t", efb); } #[test] @@ -74,21 +74,21 @@ fn insert_utf8_bom() { let mut efb = EnvFileBuilder::new(); efb.add_str("test"); efb.insert_utf8_bom(); - assert_contents_str(efb, "\u{FEFF}test"); + assert_eq!("\u{FEFF}test", efb); } #[test] fn add_strln() { let mut efb = EnvFileBuilder::new(); efb.add_strln("test"); - assert_contents_str(efb, "test\n"); + assert_eq!("test\n", efb); } #[test] fn from_vec_u8() { let vec: Vec = Vec::from(create_default_env_file()); let efb = EnvFileBuilder::from(vec); - assert_contents_str(efb, &create_default_env_file()); + assert_eq!(create_default_env_file(), efb); } #[test] @@ -103,10 +103,5 @@ fn to_vec_u8() { #[test] fn from_string() { let efb = EnvFileBuilder::from(create_default_env_file()); - assert_contents_str(efb, &create_default_env_file()); -} - -fn assert_contents_str(efb: EnvFileBuilder, expected: &str) { - let contents = efb.into_owned_string(); - assert_eq!(expected, contents,); + assert_eq!(create_default_env_file(), efb); } From 129d68f249a50b1524ae7cd2cccc02ae69f2a74a Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Thu, 1 Aug 2024 12:30:18 +0100 Subject: [PATCH 55/65] Fix testenv reset --- test_util/src/testenv.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index bdd0638f..30912bc8 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -59,6 +59,7 @@ where reset_env(&original_env); create_env(testenv); test(); + reset_env(&original_env); // drop the lock } From 6fa82bceaaf3cae90d48c6025184ed1e48875c1d Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Thu, 1 Aug 2024 12:33:28 +0100 Subject: [PATCH 56/65] Remove default testenv Let users of the test utils customize their own defaults --- dotenv/tests/integration/dotenv.rs | 21 ++++- test_util/README.md | 26 ------ test_util/src/assertions.rs | 23 +---- test_util/src/env_file.rs | 38 -------- test_util/src/lib.rs | 25 ++---- test_util/src/testenv.rs | 41 +-------- test_util/src/tests/default_env.rs | 34 ------- test_util/src/tests/env_file.rs | 26 ------ test_util/src/tests/env_file_builder.rs | 29 +++--- test_util/src/tests/mod.rs | 55 +++++++++--- test_util/src/tests/testenv.rs | 114 +++++++++++------------- 11 files changed, 134 insertions(+), 298 deletions(-) delete mode 100644 test_util/src/tests/default_env.rs delete mode 100644 test_util/src/tests/env_file.rs diff --git a/dotenv/tests/integration/dotenv.rs b/dotenv/tests/integration/dotenv.rs index 3c95d826..909905e0 100644 --- a/dotenv/tests/integration/dotenv.rs +++ b/dotenv/tests/integration/dotenv.rs @@ -1,10 +1,23 @@ use dotenvy_test_util::*; +const TEST_KEY: &str = "TEST_KEY"; +const TEST_VALUE: &str = "test_val"; +const EXISTING_KEY: &str = "EXISTING_KEY"; +const EXISTING_VALUE: &str = "loaded_from_env"; + +const TEST_ENV_FILE: &str = r" +TEST_KEY=test_val +EXISTING_KEY=loaded_from_file +"; + #[test] -fn dotenv_ok_default_env() { - test_in_default_env(|| { +fn dotenv_ok() { + let mut testenv = TestEnv::init(); + testenv.add_env_file(".env", TEST_ENV_FILE); + testenv.add_env_var(EXISTING_KEY, EXISTING_VALUE); + test_in_env(&testenv, || { dotenvy::dotenv().ok(); - assert_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); - assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); + assert_env_var(TEST_KEY, TEST_VALUE); + assert_env_var(EXISTING_KEY, EXISTING_VALUE); }); } diff --git a/test_util/README.md b/test_util/README.md index de8a3c12..2c0378a7 100644 --- a/test_util/README.md +++ b/test_util/README.md @@ -63,32 +63,6 @@ fn dotenv_override_existing_key() { } ``` -### Default TestEnv - -Use the default `TestEnv` for simple tests. - -```rust -use dotenvy_test_util::*; -use dotenvy::dotenv; - -#[test] -fn dotenv_works() { - test_in_default_env(|| { - dotenv().expect(".env should be loaded"); - assert_env_var(DEFAULT_KEY, DEFAULT_VAL); - }) -} -``` - -The default `TestEnv` has 1 existing environment variable: - -- `DEFAULT_EXISTING_KEY` set to `DEFAULT_EXISTING_VAL` - -It has an env file `.env` that sets: - -- `DEFAULT_TEST_KEY` to `DEFAULT_TEST_VAL` -- `DEFAULT_EXISTING_KEY` to `DEFAULT_OVERRIDING_VAL` - ### Customised Envfile Use the `EnvFileBuilder` to manipulate the content of an env file. Useful diff --git a/test_util/src/assertions.rs b/test_util/src/assertions.rs index 402f65d4..dc67153f 100644 --- a/test_util/src/assertions.rs +++ b/test_util/src/assertions.rs @@ -1,4 +1,3 @@ -use super::*; use std::env::{self, VarError}; /// Assert multiple environment variables are set and have the expected @@ -13,8 +12,8 @@ use std::env::{self, VarError}; /// ```no_run /// # use dotenvy_test_util::assert_env_vars; /// assert_env_vars(&[ -/// ("DEFAULT_TEST_KEY", "default_test_val"), -/// ("DEFAULT_EXISTING_KEY", "loaded_from_env"), +/// ("TEST_KEY", "test_val"), +/// ("EXISTING_KEY", "loaded_from_env"), /// ]); /// ``` pub fn assert_env_vars(vars: &[(&str, &str)]) { @@ -49,21 +48,3 @@ pub fn assert_env_var_unset(key: &str) { _ => (), } } - -/// Assert default testing environment variables are not set. -pub fn assert_default_keys_unset() { - assert_env_var_unset(DEFAULT_EXISTING_KEY); - assert_env_var_unset(DEFAULT_TEST_KEY); -} - -/// Assert default testing environment variables are set. -/// Assuming the default env file is loaded. -pub fn assert_default_keys() { - assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); - assert_default_existing_var(); -} - -/// Assert default existing environment variable is set. -pub fn assert_default_existing_var() { - assert_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); -} diff --git a/test_util/src/env_file.rs b/test_util/src/env_file.rs index bd640f56..d66929e7 100644 --- a/test_util/src/env_file.rs +++ b/test_util/src/env_file.rs @@ -1,41 +1,3 @@ -use super::*; - -/// Create the default env file contents. -/// -/// [`DEFAULT_TEST_KEY`] set as [`DEFAULT_TEST_VALUE`] -/// -/// [`DEFAULT_EXISTING_KEY`] set as [`DEFAULT_OVERRIDING_VALUE`] -pub fn create_default_env_file() -> String { - format!( - "{DEFAULT_TEST_KEY}={DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}={DEFAULT_OVERRIDING_VALUE}", - ) -} - -/// Invalid due to missing `=` between key and value. -pub fn create_invalid_env_file() -> String { - format!( - "{DEFAULT_TEST_KEY}{DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}{DEFAULT_OVERRIDING_VALUE}", - ) -} - -/// Create an env file with custom key-value pairs. -/// -/// ## Example -/// -/// ```no_run -/// # use dotenvy_test_util::create_custom_env_file; -/// let contents = create_custom_env_file(&[ -/// ("CUSTOM_KEY", "test_val"), -/// ("ANOTHER_KEY", "another_val"), -/// ]); -/// assert_eq!(contents, "CUSTOM_KEY=test_val\nANOTHER_KEY=another_val\n"); -/// ``` -pub fn create_custom_env_file(env_vars: &[(&str, &str)]) -> String { - let mut efb = EnvFileBuilder::new(); - efb.add_vars(env_vars); - efb.into_owned_string() -} - /// Advanced test-env file constructor. /// /// Represented as bytes to allow for advanced manipulation and BOM testing. diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index 077bac65..23401813 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -18,13 +18,12 @@ //! - multiple `.env` files, //! - custom env file name/path. //! -//! Use the `create_` helper functions, such as [`create_custom_env_file`], to -//! generate the `.env` file contents. If you need more control of the -//! env file's bytes, use the [`EnvFileBuilder`]. +//! Customize your env files using [`EnvFileBuilder`]. //! -//! In your tests, call the [`dotenvy`] API, then make use of the `assert_` -//! helpers, such as [`assert_env_var`] and [`assert_env_var_unset`], to check -//! the state of the environment. +//! In your tests, call your environment altering functions such as the +//! [`dotenvy`] API, then make use of the `assert_` helpers, such as +//! [`assert_env_var`] and [`assert_env_var_unset`], to check the state of +//! the environment. //! //! ## Example //! @@ -47,7 +46,7 @@ //! // with an env file that overrides it //! testenv.add_env_file( //! ".env", -//! create_custom_env_file(&[(EXISTING_KEY, OVERRIDING_VAL)]), +//! format!("{EXISTING_KEY}={OVERRIDING_VAL}"), //! ); //! //! // execute a closure in the testing environment @@ -71,15 +70,3 @@ mod tests; pub use assertions::*; pub use env_file::*; pub use testenv::*; - -/// Default key used in env file -pub const DEFAULT_TEST_KEY: &str = "DEFAULT_TEST_KEY"; -/// Default value used in env file -pub const DEFAULT_TEST_VALUE: &str = "default_test_val"; - -/// Default existing key set before test is run -pub const DEFAULT_EXISTING_KEY: &str = "DEFAULT_EXISTING_KEY"; -/// Default existing value set before test is run -pub const DEFAULT_EXISTING_VALUE: &str = "loaded_from_env"; -/// Default overriding value in env file -pub const DEFAULT_OVERRIDING_VALUE: &str = "loaded_from_file"; diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 30912bc8..584913d2 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -1,4 +1,3 @@ -use super::{create_default_env_file, DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE}; use once_cell::sync::OnceCell; use std::{ collections::HashMap, @@ -24,8 +23,6 @@ static ENV_LOCKER: OnceCell>> = OnceCell::new(); /// Creation methods: /// - [`TestEnv::init`]: blank environment (no env file) /// - [`TestEnv::init_with_env_file`]: blank environment with a custom `.env` -/// - [`TestEnv::default`]: default testing environment (1 existing var and 2 -/// set in a `.env` file) #[derive(Debug)] pub struct TestEnv { // Temporary directory that will be deleted on drop @@ -63,39 +60,6 @@ where // drop the lock } -/// Run a test closure within the default test environment. -/// -/// Resets the environment variables, creates the default [`TestEnv`], then runs -/// the test closure. Ensures only one thread has access to the process -/// environment. -/// -/// The default testing environment sets an existing environment variable -/// `DEFAULT_EXISTING_KEY`, which is set to `loaded_from_env`. It also creates a -/// `.env` file with the two lines: -/// -/// ```ini -/// DEFAULT_TEST_KEY=default_test_val -/// DEFAULT_EXISTING_KEY=loaded_from_file -/// ``` -/// -/// Notice that file has the potential to override `DEFAULT_EXISTING_KEY` depending -/// on the what's being tested. -pub fn test_in_default_env(test: F) -where - F: FnOnce(), -{ - let testenv = TestEnv::default(); - test_in_env(&testenv, test); -} - -/// Create a [`TestEnv`] without an env file, but with the -/// default existing environment variable. -pub fn create_testenv_with_default_var() -> TestEnv { - let mut testenv = TestEnv::init(); - testenv.add_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); - testenv -} - impl TestEnv { /// Blank testing environment in a new temporary directory. /// @@ -278,10 +242,7 @@ impl TestEnv { impl Default for TestEnv { fn default() -> Self { - let mut testenv = Self::init(); - testenv.add_env_var(DEFAULT_EXISTING_KEY, DEFAULT_EXISTING_VALUE); - testenv.add_env_file(".env", create_default_env_file()); - testenv + Self::init() } } diff --git a/test_util/src/tests/default_env.rs b/test_util/src/tests/default_env.rs deleted file mode 100644 index e501ded1..00000000 --- a/test_util/src/tests/default_env.rs +++ /dev/null @@ -1,34 +0,0 @@ -use super::*; - -#[test] -fn vars_state() { - test_in_default_env(|| { - assert_env_var_unset(DEFAULT_TEST_KEY); - assert_default_existing_var(); - }); -} - -#[test] -fn env_file_exists() { - let testenv = TestEnv::default(); - assert_env_files_in_testenv(&testenv); -} - -#[test] -fn env_file_loaded_vars_state() { - test_in_default_env(|| { - dotenvy::dotenv().expect(DOTENV_EXPECT); - // dotenv() does not override existing var - assert_default_keys(); - }); -} - -#[test] -fn only_default_existing() { - let testenv = create_testenv_with_default_var(); - let env_file_path = testenv.temp_path().join(".env"); - test_in_env(&testenv, || { - assert_default_existing_var(); - assert!(!env_file_path.exists()); - }); -} diff --git a/test_util/src/tests/env_file.rs b/test_util/src/tests/env_file.rs deleted file mode 100644 index 23d919b2..00000000 --- a/test_util/src/tests/env_file.rs +++ /dev/null @@ -1,26 +0,0 @@ -use super::*; - -#[test] -fn create_default() { - let expected = format!( - "{DEFAULT_TEST_KEY}={DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}={DEFAULT_OVERRIDING_VALUE}", - ); - let actual = create_default_env_file(); - assert_eq!(expected, actual); -} - -#[test] -fn create_without_equals() { - let expected = format!( - "{DEFAULT_TEST_KEY}{DEFAULT_TEST_VALUE}\n{DEFAULT_EXISTING_KEY}{DEFAULT_OVERRIDING_VALUE}", - ); - let actual = create_invalid_env_file(); - assert_eq!(expected, actual); -} - -#[test] -fn create_custom() { - let expected = expected_env_file(CUSTOM_VARS); - let actual = create_custom_env_file(CUSTOM_VARS); - assert_eq!(expected, actual); -} diff --git a/test_util/src/tests/env_file_builder.rs b/test_util/src/tests/env_file_builder.rs index 9fbd0fae..33108a2e 100644 --- a/test_util/src/tests/env_file_builder.rs +++ b/test_util/src/tests/env_file_builder.rs @@ -15,28 +15,25 @@ fn default_builds_empty() { #[test] fn add_key_empty_value() { let mut efb = EnvFileBuilder::new(); - efb.add_key_value(DEFAULT_TEST_KEY, ""); - let expected = format!("{DEFAULT_TEST_KEY}=\n"); + efb.add_key_value(TEST_KEY, ""); + let expected = format!("{TEST_KEY}=\n"); assert_eq!(expected, efb); } #[test] fn add_key_value() { let mut efb = EnvFileBuilder::new(); - efb.add_key_value(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); - let expected = format!("{DEFAULT_TEST_KEY}={DEFAULT_TEST_VALUE}\n"); + efb.add_key_value(TEST_KEY, TEST_VALUE); + let expected = format!("{TEST_KEY}={TEST_VALUE}\n"); assert_eq!(expected, efb); } #[test] fn add_multiple_key_values() { let mut efb = EnvFileBuilder::new(); - efb.add_key_value(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); - efb.add_key_value(DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE); - let expected = expected_env_file(&[ - (DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE), - (DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE), - ]); + efb.add_key_value(TEST_KEY, TEST_VALUE); + efb.add_key_value(EXISTING_KEY, OVERRIDING_VALUE); + let expected = expected_env_file(&[(TEST_KEY, TEST_VALUE), (EXISTING_KEY, OVERRIDING_VALUE)]); assert_eq!(expected, efb); } @@ -86,22 +83,22 @@ fn add_strln() { #[test] fn from_vec_u8() { - let vec: Vec = Vec::from(create_default_env_file()); + let vec: Vec = Vec::from(create_test_env_file()); let efb = EnvFileBuilder::from(vec); - assert_eq!(create_default_env_file(), efb); + assert_eq!(create_test_env_file(), efb); } #[test] fn to_vec_u8() { let mut efb = EnvFileBuilder::new(); - efb.add_str(create_default_env_file().as_str()); + efb.add_str(create_test_env_file().as_str()); let vec = Vec::from(efb); - let expected = create_default_env_file().into_bytes(); + let expected = create_test_env_file().into_bytes(); assert_eq!(expected, vec); } #[test] fn from_string() { - let efb = EnvFileBuilder::from(create_default_env_file()); - assert_eq!(create_default_env_file(), efb); + let efb = EnvFileBuilder::from(create_test_env_file()); + assert_eq!(create_test_env_file(), efb); } diff --git a/test_util/src/tests/mod.rs b/test_util/src/tests/mod.rs index 56cc1149..645dbfc9 100644 --- a/test_util/src/tests/mod.rs +++ b/test_util/src/tests/mod.rs @@ -1,12 +1,19 @@ +#![allow(dead_code)] + use std::path::Path; use super::*; -mod default_env; -mod env_file; mod env_file_builder; mod testenv; +const TEST_KEY: &str = "TEST_KEY"; +const TEST_VALUE: &str = "test_val"; + +const EXISTING_KEY: &str = "EXISTING_KEY"; +const EXISTING_VALUE: &str = "loaded_from_env"; +const OVERRIDING_VALUE: &str = "loaded_from_file"; + const CUSTOM_VARS: &[(&str, &str)] = &[ ("CUSTOM_KEY_1", "CUSTOM_VALUE_1"), ("CUSTOM_KEY_2", "CUSTOM_VALUE_2"), @@ -14,7 +21,7 @@ const CUSTOM_VARS: &[(&str, &str)] = &[ const DOTENV_EXPECT: &str = "TestEnv should have .env file"; -fn assert_env_files_in_testenv(testenv: &TestEnv) { +fn test_env_files(testenv: &TestEnv) { let files = testenv.env_files(); test_in_env(testenv, || { @@ -24,6 +31,24 @@ fn assert_env_files_in_testenv(testenv: &TestEnv) { }); } +fn test_keys_not_set(testenv: &TestEnv) { + test_in_env(testenv, assert_test_keys_unset); +} + +fn test_env_vars(testenv: &TestEnv, vars: &[(&str, &str)]) { + test_in_env(testenv, || assert_env_vars(vars)); +} + +fn test_path_exists(testenv: &TestEnv, path: impl AsRef) { + let path = testenv.temp_path().join(path.as_ref()); + assert!(path.exists(), "{} should exist in testenv", path.display()); +} + +fn assert_test_keys_unset() { + assert_env_var_unset(TEST_KEY); + assert_env_var_unset(EXISTING_KEY); +} + fn assert_env_file(path: &Path, expected: &[u8]) { assert!(path.exists(), "{} should exist in testenv", path.display()); @@ -38,22 +63,24 @@ fn assert_env_file(path: &Path, expected: &[u8]) { ); } -fn assert_default_keys_not_set_in_testenv(testenv: &TestEnv) { - test_in_env(testenv, assert_default_keys_unset); -} - -fn assert_env_vars_in_testenv(testenv: &TestEnv, vars: &[(&str, &str)]) { - test_in_env(testenv, || assert_env_vars(vars)); +fn expected_env_file(env_vars: &[(&str, &str)]) -> String { + let mut env_file = String::new(); + for (key, value) in env_vars { + env_file.push_str(key); + env_file.push('='); + env_file.push_str(value); + env_file.push('\n'); + } + env_file } -fn assert_path_exists_in_testenv(testenv: &TestEnv, path: impl AsRef) { - let path = testenv.temp_path().join(path.as_ref()); - assert!(path.exists(), "{} should exist in testenv", path.display()); +fn create_test_env_file() -> String { + format!("{TEST_KEY}={TEST_VALUE}\n{EXISTING_KEY}={OVERRIDING_VALUE}") } -fn expected_env_file(env_vars: &[(&str, &str)]) -> String { +fn create_custom_env_file() -> String { let mut env_file = String::new(); - for (key, value) in env_vars { + for (key, value) in CUSTOM_VARS { env_file.push_str(key); env_file.push('='); env_file.push_str(value); diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index 1b906791..8f605062 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -2,20 +2,28 @@ use super::*; use dotenvy::dotenv; mod init { + use std::collections::HashMap; + use super::*; #[test] fn vars_state() { - let init_testenv = TestEnv::init(); - assert_default_keys_not_set_in_testenv(&init_testenv); + let testenv = TestEnv::init(); + let existing_vars: HashMap = std::env::vars().collect(); + dbg!("existing vars: {:?}", &existing_vars); + test_in_env(&testenv, || { + for (key, value) in &existing_vars { + assert_env_var(key, value); + } + }); } #[test] fn no_env_file() { - let init_testenv = TestEnv::init(); - let env_file_path = init_testenv.temp_path().join(".env"); + let testenv = TestEnv::init(); + let env_file_path = testenv.temp_path().join(".env"); - test_in_env(&init_testenv, || { + test_in_env(&testenv, || { assert!(!env_file_path.exists()); assert!(dotenv().is_err()); }); @@ -44,26 +52,26 @@ mod init_with_env_file { use super::*; #[test] - fn default_env_file_vars_state() { - let testenv = init_default_env_file_testenv(); - assert_default_keys_not_set_in_testenv(&testenv); + fn env_file_vars_state() { + let testenv = init_test_env_file_testenv(); + test_keys_not_set(&testenv); } #[test] - fn default_env_file_exists() { - let testenv = init_default_env_file_testenv(); - assert_env_files_in_testenv(&testenv); + fn env_file_exists() { + let testenv = init_test_env_file_testenv(); + test_env_files(&testenv); } #[test] - fn default_env_file_loaded_vars_state() { - let testenv = init_default_env_file_testenv(); + fn env_file_loaded_vars_state() { + let testenv = init_test_env_file_testenv(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); // dotenv() does not override existing var // but existing key is not set in this testenv - assert_env_var(DEFAULT_EXISTING_KEY, DEFAULT_OVERRIDING_VALUE); - assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); + assert_env_var(EXISTING_KEY, OVERRIDING_VALUE); + assert_env_var(TEST_KEY, TEST_VALUE); }); } @@ -71,7 +79,7 @@ mod init_with_env_file { fn custom_env_file_vars_state() { let testenv = init_custom_env_file_testenv(); test_in_env(&testenv, || { - assert_default_keys_unset(); + assert_test_keys_unset(); for (key, _) in CUSTOM_VARS { assert_env_var_unset(key); } @@ -81,7 +89,7 @@ mod init_with_env_file { #[test] fn custom_env_file_exists() { let testenv = init_custom_env_file_testenv(); - assert_env_files_in_testenv(&testenv); + test_env_files(&testenv); } #[test] @@ -89,7 +97,7 @@ mod init_with_env_file { let testenv = init_custom_env_file_testenv(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); - assert_default_keys_unset(); + assert_test_keys_unset(); assert_env_vars(CUSTOM_VARS); }); } @@ -97,7 +105,7 @@ mod init_with_env_file { #[test] fn empty_env_file_exists() { let testenv = init_empty_env_file_testenv(); - assert_env_files_in_testenv(&testenv); + test_env_files(&testenv); } #[test] @@ -105,14 +113,14 @@ mod init_with_env_file { let testenv = init_empty_env_file_testenv(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); - assert_default_keys_unset(); + assert_test_keys_unset(); }); } #[test] fn custom_bom_env_file_exists() { let testenv = init_custom_bom_env_file_testenv(); - assert_env_files_in_testenv(&testenv); + test_env_files(&testenv); } #[test] @@ -120,17 +128,17 @@ mod init_with_env_file { let testenv = init_custom_bom_env_file_testenv(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); - assert_env_var(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); + assert_env_var(TEST_KEY, TEST_VALUE); }); } - fn init_default_env_file_testenv() -> TestEnv { - let env_file = create_default_env_file(); + fn init_test_env_file_testenv() -> TestEnv { + let env_file = create_test_env_file(); TestEnv::init_with_env_file(env_file) } fn init_custom_env_file_testenv() -> TestEnv { - let env_file = create_custom_env_file(CUSTOM_VARS); + let env_file = create_custom_env_file(); TestEnv::init_with_env_file(env_file) } @@ -140,7 +148,7 @@ mod init_with_env_file { fn init_custom_bom_env_file_testenv() -> TestEnv { let mut efb = EnvFileBuilder::new(); - efb.add_key_value(DEFAULT_TEST_KEY, DEFAULT_TEST_VALUE); + efb.add_key_value(TEST_KEY, TEST_VALUE); efb.insert_utf8_bom(); let env_file = efb.into_owned_string(); TestEnv::init_with_env_file(env_file) @@ -154,36 +162,29 @@ mod add_env_file { #[should_panic] fn panics_add_twice() { let mut testenv = TestEnv::init(); - testenv.add_env_file(".env", create_default_env_file()); - testenv.add_env_file(".env", create_custom_env_file(CUSTOM_VARS)); + testenv.add_env_file(".env", create_test_env_file()); + testenv.add_env_file(".env", create_custom_env_file()); } #[test] #[should_panic] fn panics_same_path_as_init() { - let mut testenv = TestEnv::init_with_env_file(create_default_env_file()); - testenv.add_env_file(".env", create_default_env_file()); - } - - #[test] - #[should_panic] - fn panics_same_path_as_default() { - let mut testenv = TestEnv::default(); - testenv.add_env_file(".env", create_invalid_env_file()); + let mut testenv = TestEnv::init_with_env_file(create_test_env_file()); + testenv.add_env_file(".env", create_test_env_file()); } #[test] #[should_panic] fn panics_path() { let mut testenv = TestEnv::init(); - testenv.add_env_file("", create_default_env_file()); + testenv.add_env_file("", create_test_env_file()); } #[test] fn allow_empty_contents() { let mut testenv = TestEnv::init(); testenv.add_env_file(".env", []); - assert_env_files_in_testenv(&testenv); + test_env_files(&testenv); } #[test] @@ -191,16 +192,16 @@ mod add_env_file { let mut testenv = TestEnv::init(); let path = testenv.temp_path().join(".env"); assert!(path.is_absolute()); - testenv.add_env_file(&path, create_default_env_file()); - assert_env_files_in_testenv(&testenv); + testenv.add_env_file(&path, create_test_env_file()); + test_env_files(&testenv); } #[test] fn two_files() { let mut testenv = TestEnv::init(); - testenv.add_env_file(".env", create_default_env_file()); - testenv.add_env_file(".env.local", create_custom_env_file(CUSTOM_VARS)); - assert_env_files_in_testenv(&testenv); + testenv.add_env_file(".env", create_test_env_file()); + testenv.add_env_file(".env.local", create_custom_env_file()); + test_env_files(&testenv); } } @@ -215,13 +216,6 @@ mod add_env_var { testenv.add_env_var("TEST_KEY", "two_value"); } - #[test] - #[should_panic] - fn panics_same_var_as_default() { - let mut testenv = TestEnv::default(); - testenv.add_env_var(DEFAULT_EXISTING_KEY, "value"); - } - #[test] #[should_panic] fn panics_emtpy_key() { @@ -233,7 +227,7 @@ mod add_env_var { fn allow_empty_value() { let mut testenv = TestEnv::init(); testenv.add_env_var("TEST_KEY", ""); - assert_env_vars_in_testenv(&testenv, &[("TEST_KEY", "")]); + test_env_vars(&testenv, &[("TEST_KEY", "")]); } #[test] @@ -242,14 +236,14 @@ mod add_env_var { let vars = [("TEST_KEY", "one_value"), ("TEST_KEY_2", "two_value")]; testenv.add_env_var(vars[0].0, vars[0].1); testenv.add_env_var(vars[1].0, vars[1].1); - assert_env_vars_in_testenv(&testenv, &vars); + test_env_vars(&testenv, &vars); } #[test] fn owned_strings() { let mut testenv = TestEnv::init(); testenv.add_env_var("TEST_KEY".to_string(), "test_val".to_string()); - assert_env_vars_in_testenv(&testenv, &[("TEST_KEY", "test_val")]); + test_env_vars(&testenv, &[("TEST_KEY", "test_val")]); } } @@ -297,7 +291,7 @@ mod set_env_vars { const VARS: [(&str, &str); 2] = [("TEST_KEY", "one_value"), ("TEST_KEY_2", "two_value")]; fn assert_vars_in_testenv(testenv: &TestEnv) { - assert_env_vars_in_testenv(testenv, &VARS); + test_env_vars(testenv, &VARS); } } @@ -318,7 +312,7 @@ mod set_work_dir { assert!(path.is_absolute()); std::fs::create_dir_all(&path).expect("failed to create subdir"); testenv.set_work_dir(&path); - assert_path_exists_in_testenv(&testenv, "subdir"); + test_path_exists(&testenv, "subdir"); } #[test] @@ -327,7 +321,7 @@ mod set_work_dir { std::fs::create_dir_all(testenv.temp_path().join("subdir")) .expect("failed to create subdir"); testenv.set_work_dir("subdir"); - assert_path_exists_in_testenv(&testenv, "subdir"); + test_path_exists(&testenv, "subdir"); } #[test] @@ -350,7 +344,7 @@ mod add_child_dir { fn subdir() { let mut testenv = TestEnv::init(); testenv.add_child_dir("subdir"); - assert_path_exists_in_testenv(&testenv, "subdir"); + test_path_exists(&testenv, "subdir"); } #[test] @@ -359,13 +353,13 @@ mod add_child_dir { let path = testenv.temp_path().join("subdir"); assert!(path.is_absolute()); testenv.add_child_dir(&path); - assert_path_exists_in_testenv(&testenv, "subdir"); + test_path_exists(&testenv, "subdir"); } #[test] fn create_parents() { let mut testenv = TestEnv::init(); testenv.add_child_dir("subdir/subsubdir"); - assert_path_exists_in_testenv(&testenv, "subdir/subsubdir"); + test_path_exists(&testenv, "subdir/subsubdir"); } } From 4dfeed709c5b71f0856eaf1ea73fc0ca112e747c Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Thu, 1 Aug 2024 12:47:03 +0100 Subject: [PATCH 57/65] Rename EnvFileBuilder to EnvFileContents --- test_util/src/env_file.rs | 49 +++++++++++-------- test_util/src/lib.rs | 2 +- test_util/src/testenv.rs | 9 +--- .../{env_file_builder.rs => env_file.rs} | 28 +++++------ test_util/src/tests/mod.rs | 2 +- test_util/src/tests/testenv.rs | 2 +- 6 files changed, 48 insertions(+), 44 deletions(-) rename test_util/src/tests/{env_file_builder.rs => env_file.rs} (74%) diff --git a/test_util/src/env_file.rs b/test_util/src/env_file.rs index d66929e7..58f145bc 100644 --- a/test_util/src/env_file.rs +++ b/test_util/src/env_file.rs @@ -1,12 +1,21 @@ -/// Advanced test-env file constructor. +use std::path::PathBuf; + +#[derive(Debug, Clone)] +/// Simple path and byte contents representing a `.env` file +pub struct EnvFile { + pub path: PathBuf, + pub contents: Vec, +} + +/// `.env` file builder. /// /// Represented as bytes to allow for advanced manipulation and BOM testing. #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct EnvFileBuilder { +pub struct EnvFileContents { contents: Vec, } -impl EnvFileBuilder { +impl EnvFileContents { pub const fn new() -> Self { Self { contents: Vec::new(), @@ -102,19 +111,19 @@ impl EnvFileBuilder { } } -impl From for Vec { - fn from(builder: EnvFileBuilder) -> Self { +impl From for Vec { + fn from(builder: EnvFileContents) -> Self { builder.into_owned_bytes() } } -impl From> for EnvFileBuilder { +impl From> for EnvFileContents { fn from(contents: Vec) -> Self { Self { contents } } } -impl From for EnvFileBuilder { +impl From for EnvFileContents { fn from(contents: String) -> Self { Self { contents: contents.into_bytes(), @@ -122,56 +131,56 @@ impl From for EnvFileBuilder { } } -impl AsRef<[u8]> for EnvFileBuilder { +impl AsRef<[u8]> for EnvFileContents { fn as_ref(&self) -> &[u8] { self.as_bytes() } } -impl PartialEq for EnvFileBuilder { +impl PartialEq for EnvFileContents { fn eq(&self, other: &String) -> bool { self.as_bytes() == other.as_bytes() } } -impl PartialEq for EnvFileBuilder { +impl PartialEq for EnvFileContents { fn eq(&self, other: &str) -> bool { self.as_bytes() == other.as_bytes() } } -impl PartialEq> for EnvFileBuilder { +impl PartialEq> for EnvFileContents { fn eq(&self, other: &Vec) -> bool { self.as_bytes() == other } } -impl PartialEq<[u8]> for EnvFileBuilder { +impl PartialEq<[u8]> for EnvFileContents { fn eq(&self, other: &[u8]) -> bool { self.as_bytes() == other } } -impl PartialEq for String { - fn eq(&self, other: &EnvFileBuilder) -> bool { +impl PartialEq for String { + fn eq(&self, other: &EnvFileContents) -> bool { self.as_bytes() == other.as_bytes() } } -impl PartialEq for &str { - fn eq(&self, other: &EnvFileBuilder) -> bool { +impl PartialEq for &str { + fn eq(&self, other: &EnvFileContents) -> bool { self.as_bytes() == other.as_bytes() } } -impl PartialEq for Vec { - fn eq(&self, other: &EnvFileBuilder) -> bool { +impl PartialEq for Vec { + fn eq(&self, other: &EnvFileContents) -> bool { self == other.as_bytes() } } -impl PartialEq for &[u8] { - fn eq(&self, other: &EnvFileBuilder) -> bool { +impl PartialEq for &[u8] { + fn eq(&self, other: &EnvFileContents) -> bool { *self == other.as_bytes() } } diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index 23401813..3001b4a9 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -18,7 +18,7 @@ //! - multiple `.env` files, //! - custom env file name/path. //! -//! Customize your env files using [`EnvFileBuilder`]. +//! Customize your env files using [`EnvFileContents`]. //! //! In your tests, call your environment altering functions such as the //! [`dotenvy`] API, then make use of the `assert_` helpers, such as diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 584913d2..8e5f4ed1 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -8,6 +8,8 @@ use std::{ }; use tempfile::{tempdir, TempDir}; +use crate::EnvFile; + /// Env var convenience type. type EnvMap = HashMap; @@ -33,13 +35,6 @@ pub struct TestEnv { env_files: Vec, } -#[derive(Debug, Clone)] -/// Simple path and byte contents representing a `.env` file -pub struct EnvFile { - pub path: PathBuf, - pub contents: Vec, -} - /// Run a test closure within a test environment. /// /// Resets the environment variables, loads the [`TestEnv`], then runs the test diff --git a/test_util/src/tests/env_file_builder.rs b/test_util/src/tests/env_file.rs similarity index 74% rename from test_util/src/tests/env_file_builder.rs rename to test_util/src/tests/env_file.rs index 33108a2e..c3b8b084 100644 --- a/test_util/src/tests/env_file_builder.rs +++ b/test_util/src/tests/env_file.rs @@ -2,19 +2,19 @@ use super::*; #[test] fn new_builds_empty() { - let efb = EnvFileBuilder::new(); + let efb = EnvFileContents::new(); assert!(efb.is_empty()); } #[test] fn default_builds_empty() { - let efb = EnvFileBuilder::default(); + let efb = EnvFileContents::default(); assert!(efb.is_empty()); } #[test] fn add_key_empty_value() { - let mut efb = EnvFileBuilder::new(); + let mut efb = EnvFileContents::new(); efb.add_key_value(TEST_KEY, ""); let expected = format!("{TEST_KEY}=\n"); assert_eq!(expected, efb); @@ -22,7 +22,7 @@ fn add_key_empty_value() { #[test] fn add_key_value() { - let mut efb = EnvFileBuilder::new(); + let mut efb = EnvFileContents::new(); efb.add_key_value(TEST_KEY, TEST_VALUE); let expected = format!("{TEST_KEY}={TEST_VALUE}\n"); assert_eq!(expected, efb); @@ -30,7 +30,7 @@ fn add_key_value() { #[test] fn add_multiple_key_values() { - let mut efb = EnvFileBuilder::new(); + let mut efb = EnvFileContents::new(); efb.add_key_value(TEST_KEY, TEST_VALUE); efb.add_key_value(EXISTING_KEY, OVERRIDING_VALUE); let expected = expected_env_file(&[(TEST_KEY, TEST_VALUE), (EXISTING_KEY, OVERRIDING_VALUE)]); @@ -39,7 +39,7 @@ fn add_multiple_key_values() { #[test] fn add_vars() { - let mut efb = EnvFileBuilder::new(); + let mut efb = EnvFileContents::new(); efb.add_vars(CUSTOM_VARS); let expected = expected_env_file(CUSTOM_VARS); assert_eq!(expected, efb); @@ -47,28 +47,28 @@ fn add_vars() { #[test] fn add_str() { - let mut efb = EnvFileBuilder::new(); + let mut efb = EnvFileContents::new(); efb.add_str("test"); assert_eq!("test", efb); } #[test] fn add_bytes() { - let mut efb = EnvFileBuilder::new(); + let mut efb = EnvFileContents::new(); efb.add_bytes(b"test"); assert_eq!("test", efb); } #[test] fn add_byte() { - let mut efb = EnvFileBuilder::new(); + let mut efb = EnvFileContents::new(); efb.add_byte(b't'); assert_eq!("t", efb); } #[test] fn insert_utf8_bom() { - let mut efb = EnvFileBuilder::new(); + let mut efb = EnvFileContents::new(); efb.add_str("test"); efb.insert_utf8_bom(); assert_eq!("\u{FEFF}test", efb); @@ -76,7 +76,7 @@ fn insert_utf8_bom() { #[test] fn add_strln() { - let mut efb = EnvFileBuilder::new(); + let mut efb = EnvFileContents::new(); efb.add_strln("test"); assert_eq!("test\n", efb); } @@ -84,13 +84,13 @@ fn add_strln() { #[test] fn from_vec_u8() { let vec: Vec = Vec::from(create_test_env_file()); - let efb = EnvFileBuilder::from(vec); + let efb = EnvFileContents::from(vec); assert_eq!(create_test_env_file(), efb); } #[test] fn to_vec_u8() { - let mut efb = EnvFileBuilder::new(); + let mut efb = EnvFileContents::new(); efb.add_str(create_test_env_file().as_str()); let vec = Vec::from(efb); let expected = create_test_env_file().into_bytes(); @@ -99,6 +99,6 @@ fn to_vec_u8() { #[test] fn from_string() { - let efb = EnvFileBuilder::from(create_test_env_file()); + let efb = EnvFileContents::from(create_test_env_file()); assert_eq!(create_test_env_file(), efb); } diff --git a/test_util/src/tests/mod.rs b/test_util/src/tests/mod.rs index 645dbfc9..74b44e9e 100644 --- a/test_util/src/tests/mod.rs +++ b/test_util/src/tests/mod.rs @@ -4,7 +4,7 @@ use std::path::Path; use super::*; -mod env_file_builder; +mod env_file; mod testenv; const TEST_KEY: &str = "TEST_KEY"; diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index 8f605062..21805cf7 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -147,7 +147,7 @@ mod init_with_env_file { } fn init_custom_bom_env_file_testenv() -> TestEnv { - let mut efb = EnvFileBuilder::new(); + let mut efb = EnvFileContents::new(); efb.add_key_value(TEST_KEY, TEST_VALUE); efb.insert_utf8_bom(); let env_file = efb.into_owned_string(); From ebc2f6c113f6ee616cb3f42f87ad1af573a32876 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Thu, 1 Aug 2024 13:04:51 +0100 Subject: [PATCH 58/65] Fix testenv var state test --- test_util/src/tests/testenv.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index 21805cf7..72c0d109 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -9,13 +9,15 @@ mod init { #[test] fn vars_state() { let testenv = TestEnv::init(); - let existing_vars: HashMap = std::env::vars().collect(); - dbg!("existing vars: {:?}", &existing_vars); + let mut vars: HashMap = HashMap::new(); test_in_env(&testenv, || { - for (key, value) in &existing_vars { - assert_env_var(key, value); + for (k, v) in std::env::vars() { + vars.insert(k, v); } }); + for (k, v) in &vars { + assert_env_var(k.as_str(), v.as_str()); + } } #[test] From cef502f36020e587535d34b192130a85740c8c53 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Thu, 1 Aug 2024 13:40:38 +0100 Subject: [PATCH 59/65] Remove extra Efc methods --- test_util/src/env_file.rs | 48 ++++--------------- test_util/src/tests/env_file.rs | 83 ++++++++++++++------------------- test_util/src/tests/testenv.rs | 9 ++-- 3 files changed, 49 insertions(+), 91 deletions(-) diff --git a/test_util/src/env_file.rs b/test_util/src/env_file.rs index 58f145bc..b25a17f0 100644 --- a/test_util/src/env_file.rs +++ b/test_util/src/env_file.rs @@ -60,55 +60,27 @@ impl EnvFileContents { self.contents.is_empty() } - /// Add a slice of key-value pairs, separated by newlines. - /// - /// Includes a trailing newline. - pub fn add_vars(&mut self, env_vars: &[(&str, &str)]) -> &mut Self { - let mut many = String::new(); - for (key, value) in env_vars { - many.push_str(key); - many.push('='); - many.push_str(value); - many.push('\n'); - } - self.add_str(&many); - self - } - - /// Add a key-value pair and newline - pub fn add_key_value(&mut self, key: &str, value: &str) -> &mut Self { - self.add_strln(&format!("{key}={value}")) + /// Append a key-value pair and newline + pub fn add_var(&mut self, key: &str, value: &str) -> &mut Self { + self.push_str(&format!("{key}={value}\n")) } - /// Add a string without a newline - pub fn add_str(&mut self, s: &str) -> &mut Self { - self.add_bytes(s.as_bytes()) + /// Apeend a string + pub fn push_str(&mut self, s: &str) -> &mut Self { + self.push_bytes(s.as_bytes()) } - /// Add a string with a newline - pub fn add_strln(&mut self, line: &str) -> &mut Self { - self.add_str(line).add_byte(b'\n') - } - - /// Add a byte slice - pub fn add_bytes(&mut self, bytes: &[u8]) -> &mut Self { + /// Append a byte slice + pub fn push_bytes(&mut self, bytes: &[u8]) -> &mut Self { self.contents.extend_from_slice(bytes); self } - /// Add a single byte - pub fn add_byte(&mut self, byte: u8) -> &mut Self { + /// Append a single byte + pub fn push(&mut self, byte: u8) -> &mut Self { self.contents.push(byte); self } - - /// Insert the UTF-8 Byte Order Mark at the beginning of the file - pub fn insert_utf8_bom(&mut self) -> &mut Self { - // https://www.compart.com/en/unicode/U+FEFF - let bom = b"\xEF\xBB\xBF"; - self.contents.splice(0..0, bom.iter().copied()); - self - } } impl From for Vec { diff --git a/test_util/src/tests/env_file.rs b/test_util/src/tests/env_file.rs index c3b8b084..e54c7964 100644 --- a/test_util/src/tests/env_file.rs +++ b/test_util/src/tests/env_file.rs @@ -2,103 +2,88 @@ use super::*; #[test] fn new_builds_empty() { - let efb = EnvFileContents::new(); - assert!(efb.is_empty()); + let efc = EnvFileContents::new(); + assert!(efc.is_empty()); } #[test] fn default_builds_empty() { - let efb = EnvFileContents::default(); - assert!(efb.is_empty()); + let efc = EnvFileContents::default(); + assert!(efc.is_empty()); } #[test] fn add_key_empty_value() { - let mut efb = EnvFileContents::new(); - efb.add_key_value(TEST_KEY, ""); + let mut efc = EnvFileContents::new(); + efc.add_var(TEST_KEY, ""); let expected = format!("{TEST_KEY}=\n"); - assert_eq!(expected, efb); + assert_eq!(expected, efc); } #[test] fn add_key_value() { - let mut efb = EnvFileContents::new(); - efb.add_key_value(TEST_KEY, TEST_VALUE); + let mut efc = EnvFileContents::new(); + efc.add_var(TEST_KEY, TEST_VALUE); let expected = format!("{TEST_KEY}={TEST_VALUE}\n"); - assert_eq!(expected, efb); + assert_eq!(expected, efc); } #[test] fn add_multiple_key_values() { - let mut efb = EnvFileContents::new(); - efb.add_key_value(TEST_KEY, TEST_VALUE); - efb.add_key_value(EXISTING_KEY, OVERRIDING_VALUE); + let mut efc = EnvFileContents::new(); + efc.add_var(TEST_KEY, TEST_VALUE); + efc.add_var(EXISTING_KEY, OVERRIDING_VALUE); let expected = expected_env_file(&[(TEST_KEY, TEST_VALUE), (EXISTING_KEY, OVERRIDING_VALUE)]); - assert_eq!(expected, efb); -} - -#[test] -fn add_vars() { - let mut efb = EnvFileContents::new(); - efb.add_vars(CUSTOM_VARS); - let expected = expected_env_file(CUSTOM_VARS); - assert_eq!(expected, efb); + assert_eq!(expected, efc); } #[test] fn add_str() { - let mut efb = EnvFileContents::new(); - efb.add_str("test"); - assert_eq!("test", efb); + let mut efc = EnvFileContents::new(); + efc.push_str("test"); + assert_eq!("test", efc); } #[test] fn add_bytes() { - let mut efb = EnvFileContents::new(); - efb.add_bytes(b"test"); - assert_eq!("test", efb); + let mut efc = EnvFileContents::new(); + efc.push_bytes(b"test"); + assert_eq!("test", efc); } #[test] fn add_byte() { - let mut efb = EnvFileContents::new(); - efb.add_byte(b't'); - assert_eq!("t", efb); + let mut efc = EnvFileContents::new(); + efc.push(b't'); + assert_eq!("t", efc); } #[test] fn insert_utf8_bom() { - let mut efb = EnvFileContents::new(); - efb.add_str("test"); - efb.insert_utf8_bom(); - assert_eq!("\u{FEFF}test", efb); -} - -#[test] -fn add_strln() { - let mut efb = EnvFileContents::new(); - efb.add_strln("test"); - assert_eq!("test\n", efb); + let mut efc = EnvFileContents::new(); + efc.push_bytes(&[0xEF, 0xBB, 0xBF]); + efc.push_str("test"); + assert_eq!("\u{FEFF}test", efc); } #[test] fn from_vec_u8() { let vec: Vec = Vec::from(create_test_env_file()); - let efb = EnvFileContents::from(vec); - assert_eq!(create_test_env_file(), efb); + let efc = EnvFileContents::from(vec); + assert_eq!(create_test_env_file(), efc); } #[test] fn to_vec_u8() { - let mut efb = EnvFileContents::new(); - efb.add_str(create_test_env_file().as_str()); - let vec = Vec::from(efb); + let mut efc = EnvFileContents::new(); + efc.push_str(create_test_env_file().as_str()); + let vec = Vec::from(efc); let expected = create_test_env_file().into_bytes(); assert_eq!(expected, vec); } #[test] fn from_string() { - let efb = EnvFileContents::from(create_test_env_file()); - assert_eq!(create_test_env_file(), efb); + let efc = EnvFileContents::from(create_test_env_file()); + assert_eq!(create_test_env_file(), efc); } diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index 72c0d109..bfe2b76f 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -149,10 +149,11 @@ mod init_with_env_file { } fn init_custom_bom_env_file_testenv() -> TestEnv { - let mut efb = EnvFileContents::new(); - efb.add_key_value(TEST_KEY, TEST_VALUE); - efb.insert_utf8_bom(); - let env_file = efb.into_owned_string(); + let mut efc = EnvFileContents::new(); + let bom = b"\xEF\xBB\xBF"; + efc.push_bytes(bom); + efc.add_var(TEST_KEY, TEST_VALUE); + let env_file = efc.into_owned_string(); TestEnv::init_with_env_file(env_file) } } From 3a9f931b02c684110caa0b7a7d4c155e88738f15 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Thu, 1 Aug 2024 13:44:11 +0100 Subject: [PATCH 60/65] Rename TestEnv::init to new To better match Rust API guidelines --- dotenv/tests/integration/dotenv.rs | 2 +- test_util/src/lib.rs | 2 +- test_util/src/testenv.rs | 12 ++-- test_util/src/tests/testenv.rs | 98 +++++++++++++++--------------- 4 files changed, 57 insertions(+), 57 deletions(-) diff --git a/dotenv/tests/integration/dotenv.rs b/dotenv/tests/integration/dotenv.rs index 909905e0..f0c3ef5a 100644 --- a/dotenv/tests/integration/dotenv.rs +++ b/dotenv/tests/integration/dotenv.rs @@ -12,7 +12,7 @@ EXISTING_KEY=loaded_from_file #[test] fn dotenv_ok() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.add_env_file(".env", TEST_ENV_FILE); testenv.add_env_var(EXISTING_KEY, EXISTING_VALUE); test_in_env(&testenv, || { diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index 3001b4a9..b5575bee 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -38,7 +38,7 @@ //! #[test] //! fn dotenv_override_existing_key() { //! // setup testing environment -//! let mut testenv = TestEnv::init(); +//! let mut testenv = TestEnv::new(); //! //! // with an existing environment variable //! testenv.add_env_var(EXISTING_KEY, EXISTING_VAL); diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 8e5f4ed1..ecd62aa9 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -23,8 +23,8 @@ static ENV_LOCKER: OnceCell>> = OnceCell::new(); /// the working directory to run the test from. /// /// Creation methods: -/// - [`TestEnv::init`]: blank environment (no env file) -/// - [`TestEnv::init_with_env_file`]: blank environment with a custom `.env` +/// - [`TestEnv::new`]: blank environment (no env file) +/// - [`TestEnv::new_with_env_file`]: blank environment with a custom `.env` #[derive(Debug)] pub struct TestEnv { // Temporary directory that will be deleted on drop @@ -60,7 +60,7 @@ impl TestEnv { /// /// No env file or pre-existing variables set. The working directory is the /// created temporary directory. - pub fn init() -> Self { + pub fn new() -> Self { let tempdir = tempdir().expect("create tempdir"); let dir_path = tempdir .path() @@ -79,8 +79,8 @@ impl TestEnv { /// /// No pre-existing env vars set. The env file path is set to `.env`. The /// working directory is the created temporary directory. - pub fn init_with_env_file(contents: impl Into>) -> Self { - let mut testenv = Self::init(); + pub fn new_with_env_file(contents: impl Into>) -> Self { + let mut testenv = Self::new(); testenv.add_env_file(".env", contents); testenv } @@ -237,7 +237,7 @@ impl TestEnv { impl Default for TestEnv { fn default() -> Self { - Self::init() + Self::new() } } diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index bfe2b76f..4a0675d3 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -1,14 +1,14 @@ use super::*; use dotenvy::dotenv; -mod init { +mod new { use std::collections::HashMap; use super::*; #[test] fn vars_state() { - let testenv = TestEnv::init(); + let testenv = TestEnv::new(); let mut vars: HashMap = HashMap::new(); test_in_env(&testenv, || { for (k, v) in std::env::vars() { @@ -22,7 +22,7 @@ mod init { #[test] fn no_env_file() { - let testenv = TestEnv::init(); + let testenv = TestEnv::new(); let env_file_path = testenv.temp_path().join(".env"); test_in_env(&testenv, || { @@ -33,41 +33,41 @@ mod init { #[test] fn work_dir_is_temp() { - let testenv = TestEnv::init(); + let testenv = TestEnv::new(); assert_eq!(testenv.work_dir(), testenv.temp_path()); } #[test] fn env_vars_are_empty() { - let testenv = TestEnv::init(); + let testenv = TestEnv::new(); assert!(testenv.env_vars().is_empty()); } #[test] fn env_files_are_empty() { - let testenv = TestEnv::init(); + let testenv = TestEnv::new(); assert!(testenv.env_files().is_empty()); } } -mod init_with_env_file { +mod new_with_env_file { use super::*; #[test] fn env_file_vars_state() { - let testenv = init_test_env_file_testenv(); + let testenv = new_test_env_file_testenv(); test_keys_not_set(&testenv); } #[test] fn env_file_exists() { - let testenv = init_test_env_file_testenv(); + let testenv = new_test_env_file_testenv(); test_env_files(&testenv); } #[test] fn env_file_loaded_vars_state() { - let testenv = init_test_env_file_testenv(); + let testenv = new_test_env_file_testenv(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); // dotenv() does not override existing var @@ -79,7 +79,7 @@ mod init_with_env_file { #[test] fn custom_env_file_vars_state() { - let testenv = init_custom_env_file_testenv(); + let testenv = new_custom_env_file_testenv(); test_in_env(&testenv, || { assert_test_keys_unset(); for (key, _) in CUSTOM_VARS { @@ -90,13 +90,13 @@ mod init_with_env_file { #[test] fn custom_env_file_exists() { - let testenv = init_custom_env_file_testenv(); + let testenv = new_custom_env_file_testenv(); test_env_files(&testenv); } #[test] fn custom_env_file_loaded_vars_state() { - let testenv = init_custom_env_file_testenv(); + let testenv = new_custom_env_file_testenv(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); assert_test_keys_unset(); @@ -106,13 +106,13 @@ mod init_with_env_file { #[test] fn empty_env_file_exists() { - let testenv = init_empty_env_file_testenv(); + let testenv = new_empty_env_file_testenv(); test_env_files(&testenv); } #[test] fn empty_env_file_loaded_vars_state() { - let testenv = init_empty_env_file_testenv(); + let testenv = new_empty_env_file_testenv(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); assert_test_keys_unset(); @@ -121,40 +121,40 @@ mod init_with_env_file { #[test] fn custom_bom_env_file_exists() { - let testenv = init_custom_bom_env_file_testenv(); + let testenv = new_custom_bom_env_file_testenv(); test_env_files(&testenv); } #[test] fn custom_bom_env_file_loaded_vars_state() { - let testenv = init_custom_bom_env_file_testenv(); + let testenv = new_custom_bom_env_file_testenv(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); assert_env_var(TEST_KEY, TEST_VALUE); }); } - fn init_test_env_file_testenv() -> TestEnv { + fn new_test_env_file_testenv() -> TestEnv { let env_file = create_test_env_file(); - TestEnv::init_with_env_file(env_file) + TestEnv::new_with_env_file(env_file) } - fn init_custom_env_file_testenv() -> TestEnv { + fn new_custom_env_file_testenv() -> TestEnv { let env_file = create_custom_env_file(); - TestEnv::init_with_env_file(env_file) + TestEnv::new_with_env_file(env_file) } - fn init_empty_env_file_testenv() -> TestEnv { - TestEnv::init_with_env_file([]) + fn new_empty_env_file_testenv() -> TestEnv { + TestEnv::new_with_env_file([]) } - fn init_custom_bom_env_file_testenv() -> TestEnv { + fn new_custom_bom_env_file_testenv() -> TestEnv { let mut efc = EnvFileContents::new(); let bom = b"\xEF\xBB\xBF"; efc.push_bytes(bom); efc.add_var(TEST_KEY, TEST_VALUE); let env_file = efc.into_owned_string(); - TestEnv::init_with_env_file(env_file) + TestEnv::new_with_env_file(env_file) } } @@ -164,35 +164,35 @@ mod add_env_file { #[test] #[should_panic] fn panics_add_twice() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.add_env_file(".env", create_test_env_file()); testenv.add_env_file(".env", create_custom_env_file()); } #[test] #[should_panic] - fn panics_same_path_as_init() { - let mut testenv = TestEnv::init_with_env_file(create_test_env_file()); + fn panics_same_path_as_new() { + let mut testenv = TestEnv::new_with_env_file(create_test_env_file()); testenv.add_env_file(".env", create_test_env_file()); } #[test] #[should_panic] fn panics_path() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.add_env_file("", create_test_env_file()); } #[test] fn allow_empty_contents() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.add_env_file(".env", []); test_env_files(&testenv); } #[test] fn allow_absolute_path() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); let path = testenv.temp_path().join(".env"); assert!(path.is_absolute()); testenv.add_env_file(&path, create_test_env_file()); @@ -201,7 +201,7 @@ mod add_env_file { #[test] fn two_files() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.add_env_file(".env", create_test_env_file()); testenv.add_env_file(".env.local", create_custom_env_file()); test_env_files(&testenv); @@ -214,7 +214,7 @@ mod add_env_var { #[test] #[should_panic] fn panics_add_twice() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.add_env_var("TEST_KEY", "one_value"); testenv.add_env_var("TEST_KEY", "two_value"); } @@ -222,20 +222,20 @@ mod add_env_var { #[test] #[should_panic] fn panics_emtpy_key() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.add_env_var("", "value"); } #[test] fn allow_empty_value() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.add_env_var("TEST_KEY", ""); test_env_vars(&testenv, &[("TEST_KEY", "")]); } #[test] fn two_vars() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); let vars = [("TEST_KEY", "one_value"), ("TEST_KEY_2", "two_value")]; testenv.add_env_var(vars[0].0, vars[0].1); testenv.add_env_var(vars[1].0, vars[1].1); @@ -244,7 +244,7 @@ mod add_env_var { #[test] fn owned_strings() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.add_env_var("TEST_KEY".to_string(), "test_val".to_string()); test_env_vars(&testenv, &[("TEST_KEY", "test_val")]); } @@ -256,7 +256,7 @@ mod set_env_vars { #[test] #[should_panic] fn panics_double_key() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); let mut vars = VARS.to_vec(); vars.push(VARS[0]); testenv.set_env_vars(&vars); @@ -265,27 +265,27 @@ mod set_env_vars { #[test] #[should_panic] fn panics_empty_key() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.set_env_vars(&[("", "value")]); } #[test] fn from_tuples_slice() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.set_env_vars(VARS.as_slice()); assert_vars_in_testenv(&testenv); } #[test] fn from_tuples_ref() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.set_env_vars(&VARS); assert_vars_in_testenv(&testenv); } #[test] fn from_vec_slice() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); let vec = VARS.to_vec(); testenv.set_env_vars(vec.as_slice()); assert_vars_in_testenv(&testenv); @@ -304,13 +304,13 @@ mod set_work_dir { #[test] #[should_panic] fn panics_non_existing() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.set_work_dir("subdir"); } #[test] fn allow_absolute_path() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); let path = testenv.temp_path().join("subdir"); assert!(path.is_absolute()); std::fs::create_dir_all(&path).expect("failed to create subdir"); @@ -320,7 +320,7 @@ mod set_work_dir { #[test] fn relative_path() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); std::fs::create_dir_all(testenv.temp_path().join("subdir")) .expect("failed to create subdir"); testenv.set_work_dir("subdir"); @@ -329,7 +329,7 @@ mod set_work_dir { #[test] fn in_testenv() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); std::fs::create_dir_all(testenv.temp_path().join("subdir")) .expect("failed to create subdir"); testenv.set_work_dir("subdir"); @@ -345,14 +345,14 @@ mod add_child_dir { #[test] fn subdir() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.add_child_dir("subdir"); test_path_exists(&testenv, "subdir"); } #[test] fn allow_absolute_path() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); let path = testenv.temp_path().join("subdir"); assert!(path.is_absolute()); testenv.add_child_dir(&path); @@ -361,7 +361,7 @@ mod add_child_dir { #[test] fn create_parents() { - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::new(); testenv.add_child_dir("subdir/subsubdir"); test_path_exists(&testenv, "subdir/subsubdir"); } From 16fd9a8047106f07267c9a3be8e186fec5e22355 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Thu, 1 Aug 2024 13:46:30 +0100 Subject: [PATCH 61/65] Rename helper functions --- test_util/src/tests/testenv.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index 4a0675d3..89fa1f64 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -55,19 +55,19 @@ mod new_with_env_file { #[test] fn env_file_vars_state() { - let testenv = new_test_env_file_testenv(); + let testenv = testenv_with_test_env_file(); test_keys_not_set(&testenv); } #[test] fn env_file_exists() { - let testenv = new_test_env_file_testenv(); + let testenv = testenv_with_test_env_file(); test_env_files(&testenv); } #[test] fn env_file_loaded_vars_state() { - let testenv = new_test_env_file_testenv(); + let testenv = testenv_with_test_env_file(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); // dotenv() does not override existing var @@ -79,7 +79,7 @@ mod new_with_env_file { #[test] fn custom_env_file_vars_state() { - let testenv = new_custom_env_file_testenv(); + let testenv = testenv_with_custom_env_file(); test_in_env(&testenv, || { assert_test_keys_unset(); for (key, _) in CUSTOM_VARS { @@ -90,13 +90,13 @@ mod new_with_env_file { #[test] fn custom_env_file_exists() { - let testenv = new_custom_env_file_testenv(); + let testenv = testenv_with_custom_env_file(); test_env_files(&testenv); } #[test] fn custom_env_file_loaded_vars_state() { - let testenv = new_custom_env_file_testenv(); + let testenv = testenv_with_custom_env_file(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); assert_test_keys_unset(); @@ -106,13 +106,13 @@ mod new_with_env_file { #[test] fn empty_env_file_exists() { - let testenv = new_empty_env_file_testenv(); + let testenv = testenv_with_empty_env_file(); test_env_files(&testenv); } #[test] fn empty_env_file_loaded_vars_state() { - let testenv = new_empty_env_file_testenv(); + let testenv = testenv_with_empty_env_file(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); assert_test_keys_unset(); @@ -121,34 +121,34 @@ mod new_with_env_file { #[test] fn custom_bom_env_file_exists() { - let testenv = new_custom_bom_env_file_testenv(); + let testenv = testenv_with_custom_bom_env_file(); test_env_files(&testenv); } #[test] fn custom_bom_env_file_loaded_vars_state() { - let testenv = new_custom_bom_env_file_testenv(); + let testenv = testenv_with_custom_bom_env_file(); test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); assert_env_var(TEST_KEY, TEST_VALUE); }); } - fn new_test_env_file_testenv() -> TestEnv { + fn testenv_with_test_env_file() -> TestEnv { let env_file = create_test_env_file(); TestEnv::new_with_env_file(env_file) } - fn new_custom_env_file_testenv() -> TestEnv { + fn testenv_with_custom_env_file() -> TestEnv { let env_file = create_custom_env_file(); TestEnv::new_with_env_file(env_file) } - fn new_empty_env_file_testenv() -> TestEnv { + fn testenv_with_empty_env_file() -> TestEnv { TestEnv::new_with_env_file([]) } - fn new_custom_bom_env_file_testenv() -> TestEnv { + fn testenv_with_custom_bom_env_file() -> TestEnv { let mut efc = EnvFileContents::new(); let bom = b"\xEF\xBB\xBF"; efc.push_bytes(bom); From 06c3af8a08b389dd7a5a7200105b04f5f1089fbf Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Thu, 1 Aug 2024 20:05:41 +0100 Subject: [PATCH 62/65] Add Error type to test utilites --- dotenv/tests/integration/dotenv.rs | 10 +- test_util/README.md | 22 +- test_util/src/env_file.rs | 14 +- test_util/src/error.rs | 84 +++++++ test_util/src/lib.rs | 15 +- test_util/src/testenv.rs | 188 ++++++++------- test_util/src/tests/mod.rs | 17 +- test_util/src/tests/testenv.rs | 371 +++++++++++++++++------------ 8 files changed, 442 insertions(+), 279 deletions(-) create mode 100644 test_util/src/error.rs diff --git a/dotenv/tests/integration/dotenv.rs b/dotenv/tests/integration/dotenv.rs index f0c3ef5a..dfe58057 100644 --- a/dotenv/tests/integration/dotenv.rs +++ b/dotenv/tests/integration/dotenv.rs @@ -11,13 +11,13 @@ EXISTING_KEY=loaded_from_file "; #[test] -fn dotenv_ok() { - let mut testenv = TestEnv::new(); - testenv.add_env_file(".env", TEST_ENV_FILE); - testenv.add_env_var(EXISTING_KEY, EXISTING_VALUE); +fn dotenv_ok() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + testenv.add_env_file(".env", TEST_ENV_FILE)?; + testenv.add_env_var(EXISTING_KEY, EXISTING_VALUE)?; test_in_env(&testenv, || { dotenvy::dotenv().ok(); assert_env_var(TEST_KEY, TEST_VALUE); assert_env_var(EXISTING_KEY, EXISTING_VALUE); - }); + }) } diff --git a/test_util/README.md b/test_util/README.md index 2c0378a7..12af5d7a 100644 --- a/test_util/README.md +++ b/test_util/README.md @@ -41,24 +41,24 @@ const EXISTING_VAL: &str = "test_val"; const OVERRIDING_VAL: &str = "overriding_val"; #[test] -fn dotenv_override_existing_key() { +fn dotenv_override_existing_key() -> Result<(), Error> { // setup testing environment - let mut testenv = TestEnv::init(); + let mut testenv = TestEnv::init()?; // with an existing environment variable - testenv.add_env_var(EXISTING_KEY, EXISTING_VAL); + testenv.add_env_var(EXISTING_KEY, EXISTING_VAL)?; // with an env file that overrides it testenv.add_env_file( ".env", create_custom_env_file(&[(EXISTING_KEY, OVERRIDING_VAL)]), - ); + )?; // execute a closure in the testing environment test_in_env(&testenv, || { dotenv_override().expect(".env should be loaded"); assert_env_var(EXISTING_KEY, OVERRIDING_VAL); - }); + }) // any changes to environment variables will be reset for other tests } ``` @@ -73,17 +73,17 @@ use dotenvy_test_util::*; use dotenvy::dotenv; #[test] -fn comments_ignored_in_utf8bom_env_file() { +fn comments_ignored_in_utf8bom_env_file() -> Result<(), Error> { let mut efb = EnvFileBuilder::new(); efb.insert_utf8_bom(); efb.add_strln("# TEST_KEY=TEST_VAL"); - let testenv = TestEnv::init_with_env_file(efb); + let testenv = TestEnv::init_with_env_file(efb)?; test_in_env(&testenv, || { dotenv().expect(".env should be loaded"); assert_env_var_unset("TEST_KEY"); - }); + }) } ``` @@ -95,15 +95,15 @@ use dotenvy_test_util::*; use dotenvy::dotenv; #[test] -fn comments_ignored() { +fn comments_ignored() -> Result<(), Error> { let env_file = "# TEST_KEY=TEST_VAL\n"; - let testenv = TestEnv::init_with_env_file(env_file); + let testenv = TestEnv::init_with_env_file(env_file)?; test_in_env(&testenv, || { dotenv().expect(".env should be loaded"); assert_env_var_unset("TEST_KEY"); - }); + }) } ``` diff --git a/test_util/src/env_file.rs b/test_util/src/env_file.rs index b25a17f0..1d5be385 100644 --- a/test_util/src/env_file.rs +++ b/test_util/src/env_file.rs @@ -1,5 +1,7 @@ use std::path::PathBuf; +use crate::Error; + #[derive(Debug, Clone)] /// Simple path and byte contents representing a `.env` file pub struct EnvFile { @@ -29,11 +31,11 @@ impl EnvFileContents { /// Build a string from the contents of the builder. /// - /// ## Panics + /// ## Errors /// /// If the contents of the builder is not valid UTF-8. - pub fn build_string(&self) -> String { - String::from_utf8(self.contents.clone()).expect("valid UTF-8") + pub fn build_string(&self) -> Result { + Ok(String::from_utf8(self.contents.clone())?) } /// Transform the builder into a byte vector. @@ -43,11 +45,11 @@ impl EnvFileContents { /// Transform the builder into a string. /// - /// ## Panics + /// ## Errors /// /// If the contents of the builder is not valid UTF-8. - pub fn into_owned_string(self) -> String { - String::from_utf8(self.contents).expect("valid UTF-8") + pub fn into_owned_string(self) -> Result { + Ok(String::from_utf8(self.contents)?) } /// Get a reference to the contents of the builder. diff --git a/test_util/src/error.rs b/test_util/src/error.rs new file mode 100644 index 00000000..6b867675 --- /dev/null +++ b/test_util/src/error.rs @@ -0,0 +1,84 @@ +use std::error::Error as StdError; +use std::path::PathBuf; +use std::string::FromUtf8Error; +use std::{fmt, io}; + +#[derive(Debug)] +pub enum Error { + CanonicalizingPath(PathBuf, io::Error), + CreatingChildDir(PathBuf, io::Error), + CreatingEnvFile(PathBuf, io::Error), + CreatingTempDir(io::Error), + EnvFileConflict(PathBuf), + EnvFilePathSameAsTempDir, + InvalidUtf8(FromUtf8Error), + KeyConflict(String), + KeyEmpty, + PathNotFound(PathBuf), + SettingCurrentDir(PathBuf, io::Error), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::CanonicalizingPath(path, err) => { + write!(f, "canonicalizing path `{}`: {err}", path.display()) + } + Self::CreatingChildDir(path, err) => { + write!(f, "creating child directory `{}`: {err}", path.display()) + } + Self::CreatingEnvFile(path, err) => { + write!(f, "creating env file `{}`: {err}", path.display()) + } + Self::CreatingTempDir(err) => { + write!(f, "creating temporary directory: {err}") + } + Self::EnvFileConflict(path) => { + write!( + f, + "env file path `{}` already in test environment", + path.display() + ) + } + Self::EnvFilePathSameAsTempDir => { + write!( + f, + "env file path cannot be the same as the temporary directory" + ) + } + Self::InvalidUtf8(err) => write!(f, "invalid utf8: {err}"), + Self::KeyConflict(key) => { + write!(f, "key `{key}` already in test environment") + } + Self::KeyEmpty => write!(f, "key cannot be empty"), + Self::PathNotFound(path) => write!(f, "path not found: {}", path.display()), + Self::SettingCurrentDir(path, err) => { + write!( + f, + "setting current directory to `{}`: {err}", + path.display() + ) + } + } + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Self::InvalidUtf8(err) => Some(err), + Self::CanonicalizingPath(_, err) + | Self::CreatingChildDir(_, err) + | Self::CreatingEnvFile(_, err) + | Self::CreatingTempDir(err) + | Self::SettingCurrentDir(_, err) => Some(err), + _ => None, + } + } +} + +impl From for Error { + fn from(err: FromUtf8Error) -> Self { + Self::InvalidUtf8(err) + } +} diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index b5575bee..74cda18d 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -3,8 +3,7 @@ clippy::must_use_candidate, clippy::missing_panics_doc, clippy::wildcard_imports, - clippy::module_name_repetitions, - clippy::should_panic_without_expect + clippy::module_name_repetitions )] //! Test environment setup, assertions and helpers. @@ -36,24 +35,24 @@ //! const OVERRIDING_VAL: &str = "overriding_val"; //! //! #[test] -//! fn dotenv_override_existing_key() { +//! fn dotenv_override_existing_key() -> Result<(), Error> { //! // setup testing environment -//! let mut testenv = TestEnv::new(); +//! let mut testenv = TestEnv::new()?; //! //! // with an existing environment variable -//! testenv.add_env_var(EXISTING_KEY, EXISTING_VAL); +//! testenv.add_env_var(EXISTING_KEY, EXISTING_VAL)?; //! //! // with an env file that overrides it //! testenv.add_env_file( //! ".env", //! format!("{EXISTING_KEY}={OVERRIDING_VAL}"), -//! ); +//! )?; //! //! // execute a closure in the testing environment //! test_in_env(&testenv, || { //! dotenv_override().expect(".env should be loaded"); //! assert_env_var(EXISTING_KEY, OVERRIDING_VAL); -//! }); +//! }) //! // any changes to environment variables will be reset for other tests //! } //! ``` @@ -62,6 +61,7 @@ mod assertions; mod env_file; +mod error; mod testenv; #[cfg(test)] @@ -69,4 +69,5 @@ mod tests; pub use assertions::*; pub use env_file::*; +pub use error::*; pub use testenv::*; diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index ecd62aa9..6c7651a4 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -8,7 +8,7 @@ use std::{ }; use tempfile::{tempdir, TempDir}; -use crate::EnvFile; +use crate::{EnvFile, Error}; /// Env var convenience type. type EnvMap = HashMap; @@ -39,7 +39,11 @@ pub struct TestEnv { /// /// Resets the environment variables, loads the [`TestEnv`], then runs the test /// closure. Ensures only one thread has access to the process environment. -pub fn test_in_env(testenv: &TestEnv, test: F) +/// +/// ## Errors +/// +/// - if the test fails +pub fn test_in_env(testenv: &TestEnv, test: F) -> Result<(), Error> where F: FnOnce(), { @@ -49,9 +53,10 @@ where let original_env = locker.lock().unwrap_or_else(PoisonError::into_inner); // we reset the environment anyway upon acquiring the lock reset_env(&original_env); - create_env(testenv); + create_env(testenv)?; test(); reset_env(&original_env); + Ok(()) // drop the lock } @@ -60,29 +65,37 @@ impl TestEnv { /// /// No env file or pre-existing variables set. The working directory is the /// created temporary directory. - pub fn new() -> Self { - let tempdir = tempdir().expect("create tempdir"); - let dir_path = tempdir - .path() - .canonicalize() - .expect("canonicalize dir_path"); - Self { + /// + /// ## Errors + /// + /// - if creating the temporary directory fails + /// - if canonicalizing the temporary directory's path fails + pub fn new() -> Result { + let tempdir = tempdir().map_err(Error::CreatingTempDir)?; + let dir_path = canonicalize_path(tempdir.path())?; + let testenv = Self { _temp_dir: tempdir, work_dir: dir_path.clone(), dir_path, env_vars: HashMap::default(), env_files: vec![], - } + }; + Ok(testenv) } /// Testing environment with custom env file contents. /// /// No pre-existing env vars set. The env file path is set to `.env`. The /// working directory is the created temporary directory. - pub fn new_with_env_file(contents: impl Into>) -> Self { - let mut testenv = Self::new(); - testenv.add_env_file(".env", contents); - testenv + /// + /// ## Errors + /// + /// - if creating the temporary directory fails + /// - if canonicalizing the temporary directory's path fails + pub fn new_with_env_file(contents: impl Into>) -> Result { + let mut testenv = Self::new()?; + testenv.add_env_file(".env", contents)?; + Ok(testenv) } /// Add an individual env file. @@ -92,18 +105,19 @@ impl TestEnv { /// - `path`: relative from the temporary directory /// - `contents`: bytes or string /// - /// ## Panics + /// ## Errors /// /// - if the path is empty or the same as the temporary directory /// - if the env file already exists - pub fn add_env_file(&mut self, path: P, contents: C) -> &mut Self + pub fn add_env_file(&mut self, path: P, contents: C) -> Result<&mut Self, Error> where P: AsRef, C: Into>, { let path = self.dir_path.join(path); - self.assert_env_file_path_is_valid(&path); - self.add_env_file_assume_valid(path, contents.into()) + self.check_env_file_path_is_valid(&path)?; + self.add_env_file_assume_valid(path, contents.into()); + Ok(self) } /// Add an individual environment variable. @@ -111,19 +125,19 @@ impl TestEnv { /// This adds more pre-existing environment variables to the process before /// any tests are run. /// - /// ## Panics + /// ## Errors /// /// - if the env var already exists in the testenv /// - if the key is empty - pub fn add_env_var(&mut self, key: K, value: V) -> &mut Self + pub fn add_env_var(&mut self, key: K, value: V) -> Result<&mut Self, Error> where K: Into, V: Into, { let key = key.into(); - self.assert_env_var_is_valid(&key); + self.check_env_var_is_valid(&key)?; self.env_vars.insert(key, value.into()); - self + Ok(self) } /// Set all the pre-existing environment variables. @@ -132,15 +146,15 @@ impl TestEnv { /// test is run. This overrides any previous env vars added to the /// [`TestEnv`]. /// - /// ## Panics + /// ## Errors /// /// - if an env var is set twice /// - if a key is empty - pub fn set_env_vars(&mut self, env_vars: &[(&str, &str)]) -> &mut Self { + pub fn set_env_vars(&mut self, env_vars: &[(&str, &str)]) -> Result<&mut Self, Error> { for &(key, value) in env_vars { - self.add_env_var(key, value); + self.add_env_var(key, value)?; } - self + Ok(self) } /// Set the working directory the test will run from. @@ -152,21 +166,17 @@ impl TestEnv { /// /// - `path`: relative from the temporary directory /// - /// ## Panics + /// ## Errors /// /// - if the path does not exist - pub fn set_work_dir(&mut self, path: impl AsRef) -> &mut Self { - self.work_dir = self - .temp_path() - .join(path.as_ref()) - .canonicalize() - .expect("canonicalize work_dir"); - assert!( - self.work_dir.exists(), - "work_dir does not exist: {}", - self.work_dir.display() - ); - self + /// - if canonicalizing the path fails + pub fn set_work_dir(&mut self, path: impl AsRef) -> Result<&mut Self, Error> { + let path = self.dir_path.join(path); + if !path.exists() { + return Err(Error::PathNotFound(path)); + } + self.work_dir = canonicalize_path(path)?; + Ok(self) } /// Create a child folder within the temporary directory. @@ -175,17 +185,15 @@ impl TestEnv { /// the env file is created. /// /// Will create parent directories if they are missing. - pub fn add_child_dir(&mut self, path: impl AsRef) -> &mut Self { + /// + /// ## Errors + /// + /// - if creating the directory fails + pub fn add_child_dir(&mut self, path: impl AsRef) -> Result<&mut Self, Error> { let path = path.as_ref(); let child_dir = self.temp_path().join(path); - if let Err(err) = fs::create_dir_all(child_dir) { - panic!( - "unable to create child directory: `{}` in `{}`: {err}", - path.display(), - self.temp_path().display() - ); - } - self + fs::create_dir_all(&child_dir).map_err(|err| Error::CreatingChildDir(child_dir, err))?; + Ok(self) } /// Reference to the path of the temporary directory. @@ -214,30 +222,24 @@ impl TestEnv { self } - fn assert_env_file_path_is_valid(&self, path: &Path) { - assert!( - path != self.temp_path(), - "path cannot be empty or the same as the temporary directory" - ); - assert!( - !self.env_files.iter().any(|f| f.path == path), - "env_file already in testenv: {}", - path.display() - ); - } - - fn assert_env_var_is_valid(&self, key: &str) { - assert!(!key.is_empty(), "key cannot be empty"); - assert!( - !self.env_vars.contains_key(key), - "key already in testenv: {key}" - ); + fn check_env_file_path_is_valid(&self, path: &Path) -> Result<(), Error> { + if path == self.temp_path() { + return Err(Error::EnvFilePathSameAsTempDir); + } + if self.env_files.iter().any(|f| f.path == path) { + return Err(Error::EnvFileConflict(path.to_owned())); + } + Ok(()) } -} -impl Default for TestEnv { - fn default() -> Self { - Self::new() + fn check_env_var_is_valid(&self, key: &str) -> Result<(), Error> { + if key.is_empty() { + return Err(Error::KeyEmpty); + } + if self.env_vars.contains_key(key) { + return Err(Error::KeyConflict(key.to_owned())); + } + Ok(()) } } @@ -264,36 +266,42 @@ fn reset_env(original_env: &EnvMap) { /// Create an environment to run tests in. /// /// Writes the env files, sets the working directory, and sets environment vars. -fn create_env(testenv: &TestEnv) { - env::set_current_dir(&testenv.work_dir).expect("setting working directory"); +fn create_env(testenv: &TestEnv) -> Result<(), Error> { + env::set_current_dir(&testenv.work_dir) + .map_err(|err| Error::SettingCurrentDir(testenv.work_dir.clone(), err))?; for EnvFile { path, contents } in &testenv.env_files { - create_env_file(path, contents); + create_env_file(path, contents)?; } for (key, value) in &testenv.env_vars { env::set_var(key, value); } + + Ok(()) } /// Create an env file for use in tests. -fn create_env_file(path: &Path, contents: &[u8]) { - fn create_env_file_inner(path: &Path, contents: &[u8]) -> io::Result<()> { - let mut file = fs::File::create(path)?; - file.write_all(contents)?; - file.sync_all() +fn create_env_file(path: &Path, contents: &[u8]) -> Result<(), Error> { + if path.exists() { + return Err(Error::EnvFileConflict(path.to_owned())); } - assert!( - !path.exists(), - "env_file `{}` already exists", - path.display() - ); - // inner function to group together io::Results + create_env_file_inner(path, contents) + .map_err(|err| Error::CreatingEnvFile(path.to_owned(), err))?; - // call inner function - if let Err(err) = create_env_file_inner(path, contents) { - // handle any io::Result::Err - panic!("error creating env_file `{}`: {err}", path.display()); - } + Ok(()) +} + +// inner function to group together io::Results +fn create_env_file_inner(path: &Path, contents: &[u8]) -> io::Result<()> { + let mut file = fs::File::create(path)?; + file.write_all(contents)?; + file.sync_all() +} + +fn canonicalize_path(path: impl AsRef) -> Result { + let path = path.as_ref(); + path.canonicalize() + .map_err(|err| Error::CanonicalizingPath(path.to_owned(), err)) } diff --git a/test_util/src/tests/mod.rs b/test_util/src/tests/mod.rs index 74b44e9e..1ae0b7da 100644 --- a/test_util/src/tests/mod.rs +++ b/test_util/src/tests/mod.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - use std::path::Path; use super::*; @@ -11,7 +9,6 @@ const TEST_KEY: &str = "TEST_KEY"; const TEST_VALUE: &str = "test_val"; const EXISTING_KEY: &str = "EXISTING_KEY"; -const EXISTING_VALUE: &str = "loaded_from_env"; const OVERRIDING_VALUE: &str = "loaded_from_file"; const CUSTOM_VARS: &[(&str, &str)] = &[ @@ -21,25 +18,25 @@ const CUSTOM_VARS: &[(&str, &str)] = &[ const DOTENV_EXPECT: &str = "TestEnv should have .env file"; -fn test_env_files(testenv: &TestEnv) { +fn test_env_files(testenv: &TestEnv) -> Result<(), Error> { let files = testenv.env_files(); test_in_env(testenv, || { for EnvFile { path, contents } in files { assert_env_file(path, contents); } - }); + }) } -fn test_keys_not_set(testenv: &TestEnv) { - test_in_env(testenv, assert_test_keys_unset); +fn test_keys_not_set(testenv: &TestEnv) -> Result<(), Error> { + test_in_env(testenv, assert_test_keys_unset) } -fn test_env_vars(testenv: &TestEnv, vars: &[(&str, &str)]) { - test_in_env(testenv, || assert_env_vars(vars)); +fn test_env_vars(testenv: &TestEnv, vars: &[(&str, &str)]) -> Result<(), Error> { + test_in_env(testenv, || assert_env_vars(vars)) } -fn test_path_exists(testenv: &TestEnv, path: impl AsRef) { +fn assert_path_exists(testenv: &TestEnv, path: impl AsRef) { let path = testenv.temp_path().join(path.as_ref()); assert!(path.exists(), "{} should exist in testenv", path.display()); } diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index 89fa1f64..8d38ff09 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -7,46 +7,50 @@ mod new { use super::*; #[test] - fn vars_state() { - let testenv = TestEnv::new(); + fn vars_state() -> Result<(), Error> { + let testenv = TestEnv::new()?; let mut vars: HashMap = HashMap::new(); test_in_env(&testenv, || { for (k, v) in std::env::vars() { vars.insert(k, v); } - }); + })?; for (k, v) in &vars { assert_env_var(k.as_str(), v.as_str()); } + Ok(()) } #[test] - fn no_env_file() { - let testenv = TestEnv::new(); + fn no_env_file() -> Result<(), Error> { + let testenv = TestEnv::new()?; let env_file_path = testenv.temp_path().join(".env"); test_in_env(&testenv, || { assert!(!env_file_path.exists()); assert!(dotenv().is_err()); - }); + }) } #[test] - fn work_dir_is_temp() { - let testenv = TestEnv::new(); + fn work_dir_is_temp() -> Result<(), Error> { + let testenv = TestEnv::new()?; assert_eq!(testenv.work_dir(), testenv.temp_path()); + Ok(()) } #[test] - fn env_vars_are_empty() { - let testenv = TestEnv::new(); + fn env_vars_are_empty() -> Result<(), Error> { + let testenv = TestEnv::new()?; assert!(testenv.env_vars().is_empty()); + Ok(()) } #[test] - fn env_files_are_empty() { - let testenv = TestEnv::new(); + fn env_files_are_empty() -> Result<(), Error> { + let testenv = TestEnv::new()?; assert!(testenv.env_files().is_empty()); + Ok(()) } } @@ -54,106 +58,106 @@ mod new_with_env_file { use super::*; #[test] - fn env_file_vars_state() { - let testenv = testenv_with_test_env_file(); - test_keys_not_set(&testenv); + fn env_file_vars_state() -> Result<(), Error> { + let testenv = testenv_with_test_env_file()?; + test_keys_not_set(&testenv) } #[test] - fn env_file_exists() { - let testenv = testenv_with_test_env_file(); - test_env_files(&testenv); + fn env_file_exists() -> Result<(), Error> { + let testenv = testenv_with_test_env_file()?; + test_env_files(&testenv) } #[test] - fn env_file_loaded_vars_state() { - let testenv = testenv_with_test_env_file(); + fn env_file_loaded_vars_state() -> Result<(), Error> { + let testenv = testenv_with_test_env_file()?; test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); // dotenv() does not override existing var // but existing key is not set in this testenv assert_env_var(EXISTING_KEY, OVERRIDING_VALUE); assert_env_var(TEST_KEY, TEST_VALUE); - }); + }) } #[test] - fn custom_env_file_vars_state() { - let testenv = testenv_with_custom_env_file(); + fn custom_env_file_vars_state() -> Result<(), Error> { + let testenv = testenv_with_custom_env_file()?; test_in_env(&testenv, || { assert_test_keys_unset(); for (key, _) in CUSTOM_VARS { assert_env_var_unset(key); } - }); + }) } #[test] - fn custom_env_file_exists() { - let testenv = testenv_with_custom_env_file(); - test_env_files(&testenv); + fn custom_env_file_exists() -> Result<(), Error> { + let testenv = testenv_with_custom_env_file()?; + test_env_files(&testenv) } #[test] - fn custom_env_file_loaded_vars_state() { - let testenv = testenv_with_custom_env_file(); + fn custom_env_file_loaded_vars_state() -> Result<(), Error> { + let testenv = testenv_with_custom_env_file()?; test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); assert_test_keys_unset(); assert_env_vars(CUSTOM_VARS); - }); + }) } #[test] - fn empty_env_file_exists() { - let testenv = testenv_with_empty_env_file(); - test_env_files(&testenv); + fn empty_env_file_exists() -> Result<(), Error> { + let testenv = testenv_with_empty_env_file()?; + test_env_files(&testenv) } #[test] - fn empty_env_file_loaded_vars_state() { - let testenv = testenv_with_empty_env_file(); + fn empty_env_file_loaded_vars_state() -> Result<(), Error> { + let testenv = testenv_with_empty_env_file()?; test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); assert_test_keys_unset(); - }); + }) } #[test] - fn custom_bom_env_file_exists() { - let testenv = testenv_with_custom_bom_env_file(); - test_env_files(&testenv); + fn custom_bom_env_file_exists() -> Result<(), Error> { + let testenv = testenv_with_custom_bom_env_file()?; + test_env_files(&testenv) } #[test] - fn custom_bom_env_file_loaded_vars_state() { - let testenv = testenv_with_custom_bom_env_file(); + fn custom_bom_env_file_loaded_vars_state() -> Result<(), Error> { + let testenv = testenv_with_custom_bom_env_file()?; test_in_env(&testenv, || { dotenv().expect(DOTENV_EXPECT); assert_env_var(TEST_KEY, TEST_VALUE); - }); + }) } - fn testenv_with_test_env_file() -> TestEnv { + fn testenv_with_test_env_file() -> Result { let env_file = create_test_env_file(); TestEnv::new_with_env_file(env_file) } - fn testenv_with_custom_env_file() -> TestEnv { + fn testenv_with_custom_env_file() -> Result { let env_file = create_custom_env_file(); TestEnv::new_with_env_file(env_file) } - fn testenv_with_empty_env_file() -> TestEnv { + fn testenv_with_empty_env_file() -> Result { TestEnv::new_with_env_file([]) } - fn testenv_with_custom_bom_env_file() -> TestEnv { + fn testenv_with_custom_bom_env_file() -> Result { let mut efc = EnvFileContents::new(); let bom = b"\xEF\xBB\xBF"; efc.push_bytes(bom); efc.add_var(TEST_KEY, TEST_VALUE); - let env_file = efc.into_owned_string(); + let env_file = efc.into_owned_string()?; TestEnv::new_with_env_file(env_file) } } @@ -162,49 +166,73 @@ mod add_env_file { use super::*; #[test] - #[should_panic] - fn panics_add_twice() { - let mut testenv = TestEnv::new(); - testenv.add_env_file(".env", create_test_env_file()); - testenv.add_env_file(".env", create_custom_env_file()); + fn errors_add_twice() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + testenv.add_env_file(".env", create_test_env_file())?; + test_add_env_file_conflict(&mut testenv, ".env"); + Ok(()) } #[test] - #[should_panic] - fn panics_same_path_as_new() { - let mut testenv = TestEnv::new_with_env_file(create_test_env_file()); - testenv.add_env_file(".env", create_test_env_file()); + fn errors_same_path_as_new_with_env_file() -> Result<(), Error> { + let mut testenv = TestEnv::new_with_env_file(create_test_env_file())?; + test_add_env_file_conflict(&mut testenv, ".env"); + Ok(()) } #[test] - #[should_panic] - fn panics_path() { - let mut testenv = TestEnv::new(); - testenv.add_env_file("", create_test_env_file()); + fn errors_empty_path() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + let err = testenv + .add_env_file("", create_test_env_file()) + .unwrap_err(); + assert!(matches!(err, Error::EnvFilePathSameAsTempDir)); + Ok(()) } #[test] - fn allow_empty_contents() { - let mut testenv = TestEnv::new(); - testenv.add_env_file(".env", []); - test_env_files(&testenv); + fn allow_empty_contents() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + testenv.add_env_file(".env", [])?; + test_env_files(&testenv) } #[test] - fn allow_absolute_path() { - let mut testenv = TestEnv::new(); + fn allow_absolute_path() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; let path = testenv.temp_path().join(".env"); assert!(path.is_absolute()); - testenv.add_env_file(&path, create_test_env_file()); - test_env_files(&testenv); + testenv.add_env_file(&path, create_test_env_file())?; + test_env_files(&testenv) } #[test] - fn two_files() { - let mut testenv = TestEnv::new(); - testenv.add_env_file(".env", create_test_env_file()); - testenv.add_env_file(".env.local", create_custom_env_file()); - test_env_files(&testenv); + fn two_files() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + testenv.add_env_file(".env", create_test_env_file())?; + testenv.add_env_file(".env.local", create_custom_env_file())?; + test_env_files(&testenv) + } + + fn test_add_env_file_conflict(testenv: &mut TestEnv, path: impl AsRef) { + let path = path.as_ref(); + let expected_path = testenv.temp_path().join(path); + let res = testenv.add_env_file(path, create_custom_env_file()); + assert_env_file_conflict(res, expected_path); + } + + fn assert_env_file_conflict( + result: Result<&mut TestEnv, Error>, + expected_path: impl AsRef, + ) { + if let Err(err) = result { + match err { + Error::EnvFileConflict(path) => assert_eq!(expected_path.as_ref(), path), + _ => panic!("unexpected error: {err}"), + } + } else { + panic!("expected error"); + } } } @@ -212,41 +240,45 @@ mod add_env_var { use super::*; #[test] - #[should_panic] - fn panics_add_twice() { - let mut testenv = TestEnv::new(); - testenv.add_env_var("TEST_KEY", "one_value"); - testenv.add_env_var("TEST_KEY", "two_value"); + fn errors_add_twice() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + testenv.add_env_var("TEST_KEY", "one_value")?; + + let res = testenv.add_env_var("TEST_KEY", "two_value"); + + assert_key_conflict(res, "TEST_KEY"); + Ok(()) } #[test] - #[should_panic] - fn panics_emtpy_key() { - let mut testenv = TestEnv::new(); - testenv.add_env_var("", "value"); + fn errors_empty_key() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + let err = testenv.add_env_var("", "value").unwrap_err(); + assert!(matches!(err, Error::KeyEmpty)); + Ok(()) } #[test] - fn allow_empty_value() { - let mut testenv = TestEnv::new(); - testenv.add_env_var("TEST_KEY", ""); - test_env_vars(&testenv, &[("TEST_KEY", "")]); + fn allow_empty_value() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + testenv.add_env_var("TEST_KEY", "")?; + test_env_vars(&testenv, &[("TEST_KEY", "")]) } #[test] - fn two_vars() { - let mut testenv = TestEnv::new(); + fn two_vars() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; let vars = [("TEST_KEY", "one_value"), ("TEST_KEY_2", "two_value")]; - testenv.add_env_var(vars[0].0, vars[0].1); - testenv.add_env_var(vars[1].0, vars[1].1); - test_env_vars(&testenv, &vars); + testenv.add_env_var(vars[0].0, vars[0].1)?; + testenv.add_env_var(vars[1].0, vars[1].1)?; + test_env_vars(&testenv, &vars) } #[test] - fn owned_strings() { - let mut testenv = TestEnv::new(); - testenv.add_env_var("TEST_KEY".to_string(), "test_val".to_string()); - test_env_vars(&testenv, &[("TEST_KEY", "test_val")]); + fn owned_strings() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + testenv.add_env_var("TEST_KEY".to_string(), "test_val".to_string())?; + test_env_vars(&testenv, &[("TEST_KEY", "test_val")]) } } @@ -254,47 +286,51 @@ mod set_env_vars { use super::*; #[test] - #[should_panic] - fn panics_double_key() { - let mut testenv = TestEnv::new(); + fn errors_double_key() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; let mut vars = VARS.to_vec(); vars.push(VARS[0]); - testenv.set_env_vars(&vars); + + let res = testenv.set_env_vars(&vars); + + assert_key_conflict(res, VARS[0].0); + Ok(()) } #[test] - #[should_panic] - fn panics_empty_key() { - let mut testenv = TestEnv::new(); - testenv.set_env_vars(&[("", "value")]); + fn errors_empty_key() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + let err = testenv.set_env_vars(&[("", "value")]).unwrap_err(); + assert!(matches!(err, Error::KeyEmpty)); + Ok(()) } #[test] - fn from_tuples_slice() { - let mut testenv = TestEnv::new(); - testenv.set_env_vars(VARS.as_slice()); - assert_vars_in_testenv(&testenv); + fn from_tuples_slice() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + testenv.set_env_vars(VARS.as_slice())?; + test_vars(&testenv) } #[test] - fn from_tuples_ref() { - let mut testenv = TestEnv::new(); - testenv.set_env_vars(&VARS); - assert_vars_in_testenv(&testenv); + fn from_tuples_ref() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + testenv.set_env_vars(&VARS)?; + test_vars(&testenv) } #[test] - fn from_vec_slice() { - let mut testenv = TestEnv::new(); + fn from_vec_slice() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; let vec = VARS.to_vec(); - testenv.set_env_vars(vec.as_slice()); - assert_vars_in_testenv(&testenv); + testenv.set_env_vars(vec.as_slice())?; + test_vars(&testenv) } const VARS: [(&str, &str); 2] = [("TEST_KEY", "one_value"), ("TEST_KEY_2", "two_value")]; - fn assert_vars_in_testenv(testenv: &TestEnv) { - test_env_vars(testenv, &VARS); + fn test_vars(testenv: &TestEnv) -> Result<(), Error> { + test_env_vars(testenv, &VARS) } } @@ -302,41 +338,60 @@ mod set_work_dir { use super::*; #[test] - #[should_panic] - fn panics_non_existing() { - let mut testenv = TestEnv::new(); - testenv.set_work_dir("subdir"); + fn errors_non_existing() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + let expected_path = testenv.temp_path().join("subdir"); + + let res = testenv.set_work_dir("subdir"); + + assert_path_not_found(res, expected_path); + Ok(()) } #[test] - fn allow_absolute_path() { - let mut testenv = TestEnv::new(); + fn allow_absolute_path() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; let path = testenv.temp_path().join("subdir"); assert!(path.is_absolute()); - std::fs::create_dir_all(&path).expect("failed to create subdir"); - testenv.set_work_dir(&path); - test_path_exists(&testenv, "subdir"); + + testenv.add_child_dir(&path)?; + testenv.set_work_dir(&path)?; + + assert_path_exists(&testenv, "subdir"); + Ok(()) } #[test] - fn relative_path() { - let mut testenv = TestEnv::new(); - std::fs::create_dir_all(testenv.temp_path().join("subdir")) - .expect("failed to create subdir"); - testenv.set_work_dir("subdir"); - test_path_exists(&testenv, "subdir"); + fn relative_path() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + + testenv.add_child_dir("subdir")?; + testenv.set_work_dir("subdir")?; + + assert_path_exists(&testenv, "subdir"); + Ok(()) } #[test] - fn in_testenv() { - let mut testenv = TestEnv::new(); - std::fs::create_dir_all(testenv.temp_path().join("subdir")) - .expect("failed to create subdir"); - testenv.set_work_dir("subdir"); + fn in_testenv() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + testenv.add_child_dir("subdir")?; + testenv.set_work_dir("subdir")?; test_in_env(&testenv, || { let current_dir = std::env::current_dir().expect("failed to get current dir"); assert_eq!(current_dir, testenv.work_dir()); - }); + }) + } + + fn assert_path_not_found(result: Result<&mut TestEnv, Error>, expected_path: impl AsRef) { + if let Err(err) = result { + match err { + Error::PathNotFound(path) => assert_eq!(expected_path.as_ref(), path), + _ => panic!("unexpected error: {err}"), + } + } else { + panic!("expected error"); + } } } @@ -344,25 +399,41 @@ mod add_child_dir { use super::*; #[test] - fn subdir() { - let mut testenv = TestEnv::new(); - testenv.add_child_dir("subdir"); - test_path_exists(&testenv, "subdir"); + fn subdir() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + testenv.add_child_dir("subdir")?; + assert_path_exists(&testenv, "subdir"); + Ok(()) } #[test] - fn allow_absolute_path() { - let mut testenv = TestEnv::new(); + fn allow_absolute_path() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; let path = testenv.temp_path().join("subdir"); assert!(path.is_absolute()); - testenv.add_child_dir(&path); - test_path_exists(&testenv, "subdir"); + + testenv.add_child_dir(&path)?; + + assert_path_exists(&testenv, "subdir"); + Ok(()) } #[test] - fn create_parents() { - let mut testenv = TestEnv::new(); - testenv.add_child_dir("subdir/subsubdir"); - test_path_exists(&testenv, "subdir/subsubdir"); + fn create_parents() -> Result<(), Error> { + let mut testenv = TestEnv::new()?; + testenv.add_child_dir("subdir/subsubdir")?; + assert_path_exists(&testenv, "subdir/subsubdir"); + Ok(()) + } +} + +fn assert_key_conflict(result: Result<&mut TestEnv, Error>, key: &str) { + if let Err(err) = result { + match err { + Error::KeyConflict(k) => assert_eq!(key, k), + _ => panic!("unexpected error: {err}"), + } + } else { + panic!("expected error"); } } From dbba2e406a4cea48e60f7b83ce5e0b3f189cb22b Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Thu, 1 Aug 2024 20:08:35 +0100 Subject: [PATCH 63/65] Fix Efb to Efc in readme --- test_util/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test_util/README.md b/test_util/README.md index 12af5d7a..f9072bcb 100644 --- a/test_util/README.md +++ b/test_util/README.md @@ -65,7 +65,7 @@ fn dotenv_override_existing_key() -> Result<(), Error> { ### Customised Envfile -Use the `EnvFileBuilder` to manipulate the content of an env file. Useful +Use the `EnvFileContents` to manipulate the content of an env file. Useful for byte-order-mark(BOM) testing, and other edge cases. ```rust @@ -74,11 +74,11 @@ use dotenvy::dotenv; #[test] fn comments_ignored_in_utf8bom_env_file() -> Result<(), Error> { - let mut efb = EnvFileBuilder::new(); - efb.insert_utf8_bom(); - efb.add_strln("# TEST_KEY=TEST_VAL"); + let mut efc = EnvFileContents::new(); + efc.push_bytes(&[0xEF, 0xBB, 0xBF]); + efc.push_str("# TEST_KEY=TEST_VAL\n"); - let testenv = TestEnv::init_with_env_file(efb)?; + let testenv = TestEnv::init_with_env_file(efc)?; test_in_env(&testenv, || { dotenv().expect(".env should be loaded"); From 9aa6b27b8d34c8226010d7efc8288b382bf3c074 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Thu, 1 Aug 2024 20:25:35 +0100 Subject: [PATCH 64/65] Add unsafety to set_var and remove_var --- test_util/src/testenv.rs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/test_util/src/testenv.rs b/test_util/src/testenv.rs index 6c7651a4..2acc1a45 100644 --- a/test_util/src/testenv.rs +++ b/test_util/src/testenv.rs @@ -50,12 +50,14 @@ where let locker = get_env_locker(); // ignore a poisoned mutex // we expect some tests may panic to indicate a failure - let original_env = locker.lock().unwrap_or_else(PoisonError::into_inner); // we reset the environment anyway upon acquiring the lock - reset_env(&original_env); - create_env(testenv)?; + let original_env = locker.lock().unwrap_or_else(PoisonError::into_inner); + // Safety: we hold the lock so no other thread can access the environment + unsafe { reset_env(&original_env) }; + setup_files(testenv)?; + unsafe { setup_env(testenv.env_vars()) }; test(); - reset_env(&original_env); + unsafe { reset_env(&original_env) }; Ok(()) // drop the lock } @@ -252,7 +254,11 @@ fn get_env_locker() -> Arc> { } /// Reset the process' env vars back to what was in `original_env`. -fn reset_env(original_env: &EnvMap) { +/// +/// ## Safety +/// +/// This function should only be called in a single-threaded context. +unsafe fn reset_env(original_env: &EnvMap) { // remove keys if they weren't in the original environment env::vars() .filter(|(key, _)| !original_env.contains_key(key)) @@ -263,10 +269,19 @@ fn reset_env(original_env: &EnvMap) { .for_each(|(key, value)| env::set_var(key, value)); } +/// ## Safety +/// +/// This function should only be called in a single-threaded context. +unsafe fn setup_env(env_vars: &EnvMap) { + for (key, value) in env_vars { + env::set_var(key, value); + } +} + /// Create an environment to run tests in. /// /// Writes the env files, sets the working directory, and sets environment vars. -fn create_env(testenv: &TestEnv) -> Result<(), Error> { +fn setup_files(testenv: &TestEnv) -> Result<(), Error> { env::set_current_dir(&testenv.work_dir) .map_err(|err| Error::SettingCurrentDir(testenv.work_dir.clone(), err))?; @@ -274,10 +289,6 @@ fn create_env(testenv: &TestEnv) -> Result<(), Error> { create_env_file(path, contents)?; } - for (key, value) in &testenv.env_vars { - env::set_var(key, value); - } - Ok(()) } From afb2ea94cdcb308cda07402010bfce8e1e280235 Mon Sep 17 00:00:00 2001 From: Christopher Morton Date: Thu, 1 Aug 2024 20:27:59 +0100 Subject: [PATCH 65/65] Remove dotenvy from test suite --- test_util/Cargo.toml | 3 --- test_util/src/lib.rs | 2 +- test_util/src/tests/mod.rs | 2 -- test_util/src/tests/testenv.rs | 42 ---------------------------------- 4 files changed, 1 insertion(+), 48 deletions(-) diff --git a/test_util/Cargo.toml b/test_util/Cargo.toml index a45d5cb6..65133121 100644 --- a/test_util/Cargo.toml +++ b/test_util/Cargo.toml @@ -18,6 +18,3 @@ rust-version = "1.68.0" [dependencies] tempfile = "3.3.0" once_cell = "1.16.0" - -[dev-dependencies] -dotenvy = { path = "../dotenv", version = "0.15.7" } diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index 74cda18d..0008d75e 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -26,7 +26,7 @@ //! //! ## Example //! -//! ```no_run +//! ```rust,ignore //! use dotenvy_test_util::*; //! use dotenvy::dotenv_override; //! diff --git a/test_util/src/tests/mod.rs b/test_util/src/tests/mod.rs index 1ae0b7da..69abda1d 100644 --- a/test_util/src/tests/mod.rs +++ b/test_util/src/tests/mod.rs @@ -16,8 +16,6 @@ const CUSTOM_VARS: &[(&str, &str)] = &[ ("CUSTOM_KEY_2", "CUSTOM_VALUE_2"), ]; -const DOTENV_EXPECT: &str = "TestEnv should have .env file"; - fn test_env_files(testenv: &TestEnv) -> Result<(), Error> { let files = testenv.env_files(); diff --git a/test_util/src/tests/testenv.rs b/test_util/src/tests/testenv.rs index 8d38ff09..2d6a0967 100644 --- a/test_util/src/tests/testenv.rs +++ b/test_util/src/tests/testenv.rs @@ -1,5 +1,4 @@ use super::*; -use dotenvy::dotenv; mod new { use std::collections::HashMap; @@ -28,7 +27,6 @@ mod new { test_in_env(&testenv, || { assert!(!env_file_path.exists()); - assert!(dotenv().is_err()); }) } @@ -69,18 +67,6 @@ mod new_with_env_file { test_env_files(&testenv) } - #[test] - fn env_file_loaded_vars_state() -> Result<(), Error> { - let testenv = testenv_with_test_env_file()?; - test_in_env(&testenv, || { - dotenv().expect(DOTENV_EXPECT); - // dotenv() does not override existing var - // but existing key is not set in this testenv - assert_env_var(EXISTING_KEY, OVERRIDING_VALUE); - assert_env_var(TEST_KEY, TEST_VALUE); - }) - } - #[test] fn custom_env_file_vars_state() -> Result<(), Error> { let testenv = testenv_with_custom_env_file()?; @@ -98,46 +84,18 @@ mod new_with_env_file { test_env_files(&testenv) } - #[test] - fn custom_env_file_loaded_vars_state() -> Result<(), Error> { - let testenv = testenv_with_custom_env_file()?; - test_in_env(&testenv, || { - dotenv().expect(DOTENV_EXPECT); - assert_test_keys_unset(); - assert_env_vars(CUSTOM_VARS); - }) - } - #[test] fn empty_env_file_exists() -> Result<(), Error> { let testenv = testenv_with_empty_env_file()?; test_env_files(&testenv) } - #[test] - fn empty_env_file_loaded_vars_state() -> Result<(), Error> { - let testenv = testenv_with_empty_env_file()?; - test_in_env(&testenv, || { - dotenv().expect(DOTENV_EXPECT); - assert_test_keys_unset(); - }) - } - #[test] fn custom_bom_env_file_exists() -> Result<(), Error> { let testenv = testenv_with_custom_bom_env_file()?; test_env_files(&testenv) } - #[test] - fn custom_bom_env_file_loaded_vars_state() -> Result<(), Error> { - let testenv = testenv_with_custom_bom_env_file()?; - test_in_env(&testenv, || { - dotenv().expect(DOTENV_EXPECT); - assert_env_var(TEST_KEY, TEST_VALUE); - }) - } - fn testenv_with_test_env_file() -> Result { let env_file = create_test_env_file(); TestEnv::new_with_env_file(env_file)