From 379141a8c7efada23a8235a9c2b29bfedc3df778 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Dec 2025 01:46:37 +0000 Subject: [PATCH] Support passing env vars into test environment in setup Co-authored-by: imjasonh <210737+imjasonh@users.noreply.github.com> --- README.md | 3 ++ src/lib.rs | 29 ++++++++++++++++-- src/run/execution.rs | 2 +- src/run/params.rs | 4 +-- tests/setup_hook.rs | 73 +++++++++++++++++++++++++++++++------------- 5 files changed, 84 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 531f4d5..36e0506 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,9 @@ use testscript_rs::testscript; fn test_my_cli() { testscript::run("testdata") .setup(|env| { + // Set environment variables for tests + env.set_env_var("MY_APP_CONFIG", "/path/to/config"); + // Compile your CLI tool std::process::Command::new("cargo") .args(["build", "--bin", "my-cli"]) diff --git a/src/lib.rs b/src/lib.rs index 21e6ab6..aafaab4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -154,6 +154,10 @@ fn run(params: &mut RunParams, test_data_glob: &str) -> Result<()> { /// /// testscript::run("testdata") /// .setup(|env| { +/// // Set environment variables for your CLI +/// env.set_env_var("MY_APP_MODE", "testing"); +/// env.set_env_var("LOG_LEVEL", "debug"); +/// /// // Compile your CLI tool /// std::process::Command::new("cargo") /// .args(["build", "--bin", "my-cli"]) @@ -203,11 +207,30 @@ impl Builder { /// Add a setup function that runs before each test script /// - /// The setup function receives a reference to the test environment and can - /// perform actions like compiling binaries or setting up test data. + /// The setup function receives a mutable reference to the test environment and can + /// perform actions like compiling binaries, setting up test data, or setting environment variables. + /// + /// # Examples + /// ```no_run + /// use testscript_rs::testscript; + /// + /// testscript::run("testdata") + /// .setup(|env| { + /// // Set an environment variable + /// env.set_env_var("FOO", "bar"); + /// + /// // Compile a binary + /// std::process::Command::new("cargo") + /// .args(["build", "--bin", "my-cli"]) + /// .status()?; + /// Ok(()) + /// }) + /// .execute() + /// .unwrap(); + /// ``` pub fn setup(mut self, func: F) -> Self where - F: Fn(&TestEnvironment) -> Result<()> + 'static, + F: Fn(&mut TestEnvironment) -> Result<()> + 'static, { self.params = self.params.setup(func); self diff --git a/src/run/execution.rs b/src/run/execution.rs index 5ceac06..95c8731 100644 --- a/src/run/execution.rs +++ b/src/run/execution.rs @@ -49,7 +49,7 @@ pub fn run_script_impl(script_path: &Path, params: &RunParams) -> Result<()> { // Run setup hook if provided if let Some(setup) = ¶ms.setup { - setup(&env)?; + setup(&mut env)?; } // Track script updates if we're in update mode diff --git a/src/run/params.rs b/src/run/params.rs index 2c3bdf0..bb8fbb8 100644 --- a/src/run/params.rs +++ b/src/run/params.rs @@ -8,7 +8,7 @@ use std::collections::HashMap; pub type CommandFn = fn(&mut TestEnvironment, &[String]) -> Result<()>; /// Type alias for a setup function -pub type SetupFn = Box Result<()>>; +pub type SetupFn = Box Result<()>>; /// Configuration parameters for running tests pub struct RunParams { @@ -73,7 +73,7 @@ impl RunParams { /// Set a setup function to run before each script pub fn setup(mut self, func: F) -> Self where - F: Fn(&TestEnvironment) -> Result<()> + 'static, + F: Fn(&mut TestEnvironment) -> Result<()> + 'static, { self.setup = Some(Box::new(func)); self diff --git a/tests/setup_hook.rs b/tests/setup_hook.rs index e75df77..487cb15 100644 --- a/tests/setup_hook.rs +++ b/tests/setup_hook.rs @@ -87,37 +87,68 @@ fn test_setup_hook_environment() { let temp_dir = TempDir::new().unwrap(); let script_path = temp_dir.path().join("env_test.txt"); - let script_content = r#"# Test setup hook can modify environment -env TEST_FROM_SETUP=should_be_set -exec echo "Value: $TEST_FROM_SETUP" -stdout "Value: hello_from_setup""#; + // Use sh -c to allow shell variable expansion + let script_content = if cfg!(windows) { + r#"# Test setup hook can modify environment (Windows) +exec cmd /c "echo Value: %TEST_FROM_SETUP%" +stdout "Value: hello_from_setup""# + } else { + r#"# Test setup hook can modify environment (Unix) +exec sh -c "echo Value: $TEST_FROM_SETUP" +stdout "Value: hello_from_setup" +"# + }; fs::write(&script_path, script_content).unwrap(); // Create RunParams with setup hook that sets an environment variable let params = RunParams::new().setup(|env| { - // The setup hook has access to the TestEnvironment but can't modify it - // This demonstrates the current API limitation - we can access but not modify - println!("Setup running in: {}", env.work_dir.display()); - - // We could write files that contain environment setup - let env_script = env.work_dir.join("setup_env.sh"); - std::fs::write(&env_script, "export TEST_FROM_SETUP=hello_from_setup")?; + // Now we can modify the environment directly in the setup hook + env.set_env_var("TEST_FROM_SETUP", "hello_from_setup"); Ok(()) }); - let mut params = params; - // Manually set the environment variable for this test - params = params.condition("TEST_FROM_SETUP", true); + let result = run_script(&script_path, ¶ms); + assert!( + result.is_ok(), + "Environment setup test should pass: {:?}", + result + ); +} + +#[test] +fn test_setup_hook_multiple_env_vars() { + let temp_dir = TempDir::new().unwrap(); + let script_path = temp_dir.path().join("multi_env_test.txt"); + + // Use sh -c to allow shell variable expansion + let script_content = if cfg!(windows) { + r#"# Test setup hook can set multiple environment variables (Windows) +exec cmd /c "echo FOO=%FOO% BAR=%BAR% BAZ=%BAZ%" +stdout "FOO=value1 BAR=value2 BAZ=value3""# + } else { + r#"# Test setup hook can set multiple environment variables (Unix) +exec sh -c "echo FOO=$FOO BAR=$BAR BAZ=$BAZ" +stdout "FOO=value1 BAR=value2 BAZ=value3" +"# + }; + + fs::write(&script_path, script_content).unwrap(); + + // Create RunParams with setup hook that sets multiple environment variables + let params = RunParams::new().setup(|env| { + env.set_env_var("FOO", "value1"); + env.set_env_var("BAR", "value2"); + env.set_env_var("BAZ", "value3"); + Ok(()) + }); let result = run_script(&script_path, ¶ms); - // This test demonstrates the current limitation - setup can't modify the running environment - if result.is_err() { - println!( - "Environment setup test failed (expected with current API): {:?}", - result - ); - } + assert!( + result.is_ok(), + "Multiple environment variables test should pass: {:?}", + result + ); } #[test]