diff --git a/docs/adr-008-environment-seam-taxonomy.md b/docs/adr-008-environment-seam-taxonomy.md index 0cb6fae26..f657c03ab 100644 --- a/docs/adr-008-environment-seam-taxonomy.md +++ b/docs/adr-008-environment-seam-taxonomy.md @@ -118,6 +118,24 @@ that need a deterministic Ninja executable use `runner::run_with_ninja_program` to supply the already-resolved program path directly, bypassing `NETSUKE_NINJA` resolution entirely rather than setting the variable for a child to read. +### BDD route selection + +`rstest-bdd` steps execute in the generated test-harness process. A behavioural +label therefore does not make a step a subprocess test. Issue #492 removes the +BDD suite's process-global environment/CWD guard and records two allowed routes: + +- **Route A — isolated child.** An end-to-end scenario invokes `netsuke` with + `assert_cmd`, clears the child environment, and supplies only the required + values through `Command::env`. +- **Route B — injected environment.** A scenario calls an in-process library + entry point with its injected environment and retains assertions over values + such as `Cli`, `Manifest`, `BuildGraph`, or rendered output. + +Route B avoids CWD changes by passing absolute manifest or configuration paths, +or by preserving `-C/--directory` as a CLI value for automatic project +discovery. An explicit relative `--config` or `NETSUKE_CONFIG` selector remains +relative to the child or harness process CWD; it is not rebased under `-C`. + ## Rationale - **Proportionate abstraction.** A trait object for a single-variable, @@ -170,11 +188,12 @@ resolution entirely rather than setting the variable for a child to read. Rejected: mutating the test process to influence a spawned `netsuke` binary reintroduces the shared-mutable-state races that injected readers and child-process configuration exist to avoid, and it is exactly the pattern - #493 removed from the BDD and integration test helpers. + #493 removed from the BDD and integration test helpers. Issue #492 also + removed BDD's process-global CWD coordination. `.config/nextest.toml` runs no serialized environment group precisely because - no sanctioned test still mutates the harness environment; `EnvLock` and - `CwdGuard` remain only for the few tests that exercise process - working-directory behaviour. + no sanctioned test still mutates the harness environment. `EnvLock` and + `CwdGuard` remain only for direct tests that exercise process + working-directory behaviour; they are not a BDD isolation mechanism. ## Implementation references diff --git a/docs/developers-guide.md b/docs/developers-guide.md index 5a3034a6c..e9fdce3ac 100644 --- a/docs/developers-guide.md +++ b/docs/developers-guide.md @@ -1417,9 +1417,10 @@ governs the non-doctest pass only, and deliberately stays small: nextest runs each test in its own process, but the codebase does not rely on that isolation for environment safety. Tests pass environment values through explicit configuration seams or configure a child with `env_clear()` followed by -`Command::env`. `EnvLock` and `CwdGuard` remain only for the few tests that -exercise process working-directory behaviour, because the in-process coverage -runner shares that state. +`Command::env`. The BDD suite carries no environment or CWD lock: its steps run +inside the generated test-harness process, not inside an `assert_cmd` child. +`EnvLock` and `CwdGuard` remain only for direct tests that deliberately +exercise process working-directory behaviour outside that suite. ### Runners not covered by this configuration @@ -1705,15 +1706,19 @@ cargo-nextest alongside every other test (see point (`world: TestWorld`) to each generated scenario test. nextest runs each generated scenario in its own process. That reinforces the -per-scenario isolation policy below rather than conflicting with it: scenario -state cannot leak across process boundaries, so the policy's requirement to -recreate state per test is enforced by the runner as well as by convention. +per-scenario isolation policy below rather than conflicting with it. The +scenario's steps still execute in that generated test-harness process, so an +in-process BDD step does not qualify for subprocess isolation. ### State and isolation policy - Scenario isolation is the default: scenario state must be recreated per test. - Shared process-wide state is avoided unless infrastructure cost requires controlled reuse. +- Route A drives an end-to-end `netsuke` child with `assert_cmd` and configures + only that child with `Command::env`. +- Route B calls a library entry point with its injected environment and keeps + assertions on in-process values such as `Cli`, `Manifest`, or `BuildGraph`. - Use `Slot` for optional or replaceable scenario values. - Use typed wrappers in `tests/bdd/types.rs` for step parameters to avoid ambiguous string-heavy signatures. @@ -1921,9 +1926,12 @@ Environment variable mutations and working-directory changes are process-global side effects that can cause data races when tests run in parallel. Tests inject environment readers where the API supports them, and configure child processes with `env_clear()` followed by `Command::env` where ambient discovery is part -of the contract. `CwdGuard` is the RAII utility for restoring a process working -directory after the few tests that exercise it. For locale-sensitive snapshot -tests, use the `EnLocalizer` scoped pattern documented in the +of the contract. BDD steps must not change either process-global value: use an +injected environment and absolute paths for in-process library assertions, or +an isolated `assert_cmd` child for end-to-end behaviour. `CwdGuard` is the RAII +utility for the few direct CWD tests that deliberately exercise it. For +locale-sensitive snapshot tests, use the `EnLocalizer` scoped pattern documented +in the [snapshot testing guide](snapshot-testing-in-netsuke-using-insta.md#locale-pinned-snapshot-tests). `src/snapshot_test_support.rs` owns output-oriented unit-test fixtures; @@ -2366,7 +2374,9 @@ let _env_lock = EnvLock::acquire(); ``` Do not use this lock to justify process-environment mutation. Environment -access must remain injected, or confined to a spawned child process. +access must remain injected, or confined to a spawned child process. BDD +scenarios must not acquire this lock: they are in-process tests, and a lock +would serialize the suite rather than isolate an ambient dependency. ### `CwdGuard` @@ -2386,6 +2396,11 @@ std::env::set_current_dir(temp.path())?; Acquire `EnvLock` and then `CwdGuard` so Rust drops them in reverse declaration order: `CwdGuard` restores the CWD first, and `EnvLock` releases second. +These direct CWD tests are the narrow exception for exercising CWD-dependent +code itself. BDD scenarios instead retain an absolute manifest path or pass +`-C/--directory` into the CLI; neither approach changes the harness process +CWD. + ### Injected and child-process environments `mutate_env_var` in `tests/bdd/helpers/env_mutation.rs` is the canonical way to @@ -2409,14 +2424,22 @@ appropriate injected seam, such as `run_with_ninja_program`, `StdlibConfig::with_path_override`, `StdlibConfig::with_home_override`, or `StdlibConfig::with_command_path_override`. End-to-end tests may call `env_clear()` and then apply values with `Command::env`, because the mutation -is confined to the child. +is confined to the child. This defines two BDD routes: Route A drives the +compiled binary with `assert_cmd` and configures only its child environment; +Route B calls library entry points with an injected environment and keeps the +scenario's in-process assertions on `Cli`, `Manifest`, `BuildGraph`, or render +state. ### Ordering rules 1. Inject environment-dependent inputs whenever the API supports them. 2. Use an isolated child process for APIs whose contract is ambient discovery. -3. Acquire `EnvLock` and then `CwdGuard` only for CWD-specific tests. -4. Never mutate the harness process environment. +3. In BDD, choose Route A for an end-to-end binary assertion or Route B for an + injected in-process library assertion. +4. Retain absolute paths or pass `-C/--directory` instead of changing the BDD + harness CWD. +5. Acquire `EnvLock` and then `CwdGuard` only for direct CWD-specific tests. +6. Never mutate the harness process environment. ### `tracing_capture` @@ -2496,14 +2519,12 @@ Table: Scenario state groups and fields | Localization state | `localization_lock`, `localization_guard`, `locale_config`, `locale_env`, `locale_cli_override`, `locale_system`, `resolved_locale`, `locale_message` | Scenario-level localizer overrides and resolution state. | | HTTP server state | `http_server`, `stdlib_url` | Test HTTP server fixture for fetch scenarios. | | Output state | `output_mode`, `simulated_no_color`, `simulated_term`, `output_prefs`, `simulated_no_emoji`, `rendered_prefix` | Accessibility and output preference resolution. | -| Environment state | `env_vars_forward`, `global_state_lock` | Child environment map and the CWD-only scenario lock. | +| Environment state | `env_vars_forward` | Child environment map for Route A scenarios. | ### Key `TestWorld` methods - `track_env_var(key, new_value)` — update `env_vars_forward` so `build_netsuke_command` can configure the scenario's child process. -- `ensure_global_state_lock()` — acquire the scenario-scoped CWD lock on first - use; subsequent calls are no-ops. ## Configuration merge architecture @@ -2681,8 +2702,9 @@ uses the bare `EnvProvider` name. Tests for injected configuration discovery should provide a map-backed `ConfigEnvProvider`. End-to-end tests of the ambient `ConfigStdEnvProvider` adapter must run in an isolated child configured with `env_clear()` followed by -`Command::env`. `EnvLock` is reserved for tests that change the process working -directory alongside `CwdGuard`; it does not justify environment mutation. +`Command::env`. BDD configuration steps use the injected route; direct CWD +tests alone may use `EnvLock` alongside `CwdGuard`. Neither guard justifies +environment mutation. Unit tests that only need to verify explicit config path precedence should test `explicit_config_path_with_env` with an injected provider instead of mutating diff --git a/src/cli/discovery_layer_tests.rs b/src/cli/discovery_layer_tests.rs index 74b64534c..da7d886cd 100644 --- a/src/cli/discovery_layer_tests.rs +++ b/src/cli/discovery_layer_tests.rs @@ -1,8 +1,7 @@ //! Tests for configuration file-layer collection. //! -//! These cover which branch the shared file-layer boundary takes — explicit path -//! versus automatic discovery — and the project-scope second pass. Selector -//! precedence and event-schema snapshots live in the tracing test module. +//! These cover explicit-versus-discovery collection and the project-scope second pass. +//! Selector precedence and event-schema snapshots live in the tracing test module. use super::*; use crate::cli::test_support::TestEnv; use anyhow::{Context, Result, ensure}; @@ -204,6 +203,27 @@ fn injected_automatic_discovery_uses_xdg_config_home() -> Result<()> { Ok(()) } +/// An explicit relative configuration file does not use the CLI directory. +#[test] +fn explicit_relative_config_does_not_use_cli_directory() -> Result<()> { + let temp = tempdir().context("create temp dir")?; + let config_path = temp.path().join("config-relative-to-process-cwd.toml"); + test_support::fs::write(&config_path, "emoji = \"always\"\n") + .context("write explicit config")?; + let cli = Cli { + config: Some("config-relative-to-process-cwd.toml".into()), + directory: Some(temp.path().to_path_buf()), + ..Cli::default() + }; + + let discovered = discover_file_layers(&cli, &TestEnv::default()); + + ensure!( + discovered.first_error().is_some(), + "explicit relative config must not load from the CLI directory" + ); + Ok(()) +} /// Discovered configuration candidates retain the outcome that their content /// warrants; an unreadable candidate is never mistaken for an absent one. #[rstest] diff --git a/tests/bdd/fixtures/mod.rs b/tests/bdd/fixtures/mod.rs index e11901444..d1715fb22 100644 --- a/tests/bdd/fixtures/mod.rs +++ b/tests/bdd/fixtures/mod.rs @@ -20,16 +20,8 @@ use std::collections::HashMap; use std::ffi::OsString; use std::path::PathBuf; use std::sync::MutexGuard; -use test_support::CwdGuard; -use test_support::env_lock::EnvLock; use test_support::http::HttpServer; -#[derive(Debug)] -struct GlobalStateGuard { - env_lock: EnvLock, - cwd_guard: CwdGuard, -} - /// Combined test world for all BDD scenarios. /// /// Non-Clone types are stored in `RefCell>` to allow interior @@ -149,28 +141,9 @@ pub struct TestWorld { // Environment state /// Values supplied to child Netsuke processes for scenario-tracked variables. pub env_vars_forward: RefCell>, - /// Scenario-scoped lock for the few remaining process-global CWD operations. - global_state_lock: RefCell>, } impl TestWorld { - /// Acquire the scenario lock before changing the process working directory. - /// - /// # Errors - /// - /// Returns an error if the current working directory cannot be captured. - pub fn ensure_global_state_lock(&self) -> std::io::Result<()> { - if self.global_state_lock.borrow().is_none() { - let env_lock = EnvLock::acquire(); - let cwd_guard = CwdGuard::acquire()?; - *self.global_state_lock.borrow_mut() = Some(GlobalStateGuard { - env_lock, - cwd_guard, - }); - } - Ok(()) - } - /// Set or remove a variable in the child-process environment specification. pub fn track_env_var(&self, key: String, forward_value: Option) { if let Some(value) = forward_value { @@ -206,14 +179,6 @@ impl Drop for TestWorld { self.localization_guard.borrow_mut().take(); self.localization_lock.borrow_mut().take(); self.env_vars_forward.borrow_mut().clear(); - if let Some(GlobalStateGuard { - env_lock, - cwd_guard, - }) = self.global_state_lock.borrow_mut().take() - { - drop(cwd_guard); - drop(env_lock); - } self.stdlib_text.clear(); } } diff --git a/tests/bdd/steps/cli.rs b/tests/bdd/steps/cli.rs index 97de684ff..05bec3c32 100644 --- a/tests/bdd/steps/cli.rs +++ b/tests/bdd/steps/cli.rs @@ -34,6 +34,14 @@ use test_support::locale_stubs::{StubEnv, StubSystemLocale}; /// Tests that do not explicitly set up configuration or environment variables /// may be affected by ambient host configuration. pub(super) fn apply_cli(world: &TestWorld, args: &CliArgs) { + apply_cli_tokens(world, build_tokens(args.as_str())); +} + +/// Apply parsed CLI argument tokens, storing the result or error in world state. +/// +/// This accepts fully formed arguments for scenarios whose temporary-resource +/// paths cannot be represented as static feature text. +pub(super) fn apply_cli_tokens(world: &TestWorld, mut tokens: Vec) { let env = world .locale_env .get() @@ -44,7 +52,6 @@ pub(super) fn apply_cli(world: &TestWorld, args: &CliArgs) { // If there's a temp_dir set and the args don't already contain an // explicit -C or --directory flag, prepend -C for config discovery. - let mut tokens = build_tokens(args.as_str()); if let Some(temp_dir) = world.temp_dir.borrow().as_ref() { let is_directory_flag = |t: &std::ffi::OsString| { t.to_str().is_some_and(|s| { diff --git a/tests/bdd/steps/configuration_discovery.rs b/tests/bdd/steps/configuration_discovery.rs index 4e7dbd571..7ff537c46 100644 --- a/tests/bdd/steps/configuration_discovery.rs +++ b/tests/bdd/steps/configuration_discovery.rs @@ -7,9 +7,12 @@ use anyhow::{Context, Result, ensure}; use cap_std::{ambient_authority, fs_utf8::Dir}; use netsuke::cli::Cli; use netsuke::cli::config::EmojiPolicy; -use rstest_bdd_macros::{given, then}; +use rstest_bdd_macros::{given, then, when}; +use std::ffi::OsString; use tempfile::tempdir; +use super::cli::apply_cli_tokens; + #[given("a temporary workspace")] fn a_temporary_workspace(world: &TestWorld) -> Result<()> { let temp = tempdir().context("failed to create temporary workspace")?; @@ -18,9 +21,7 @@ fn a_temporary_workspace(world: &TestWorld) -> Result<()> { } /// Write `content` to `file_name` inside `world`'s temp directory. -/// Set `chdir` to `true` for project-scope configs so discovery works -/// without an explicit path override. -fn write_config_file(world: &TestWorld, file_name: &str, content: &str, chdir: bool) -> Result<()> { +fn write_config_file(world: &TestWorld, file_name: &str, content: &str) -> Result<()> { let temp_dir = world .temp_dir .borrow() @@ -37,12 +38,6 @@ fn write_config_file(world: &TestWorld, file_name: &str, content: &str, chdir: b dir.write(file_name, content.as_bytes()) .with_context(|| format!("failed to write {file_name} in {temp_dir_utf8}"))?; - if chdir { - // Acquire scenario-scoped lock before process-global CWD mutation - world.ensure_global_state_lock()?; - std::env::set_current_dir(&temp_dir).context("failed to change to temp directory")?; - } - Ok(()) } @@ -59,12 +54,12 @@ emoji = "{emoji}" jobs = {jobs} "# ); - write_config_file(world, file_name.as_str(), &content, true) + write_config_file(world, file_name.as_str(), &content) } #[given("a malformed project config file {file_name:string}")] fn malformed_project_config(world: &TestWorld, file_name: FileName) -> Result<()> { - write_config_file(world, file_name.as_str(), "emoji = \"always\n", true) + write_config_file(world, file_name.as_str(), "emoji = \"always\n") } /// Returns the TOML snippet for a config file that sets only `emoji`. @@ -78,12 +73,7 @@ fn project_config_with_emoji( file_name: FileName, emoji: EmojiPolicy, ) -> Result<()> { - write_config_file( - world, - file_name.as_str(), - &emoji_config_content(emoji), - true, - ) + write_config_file(world, file_name.as_str(), &emoji_config_content(emoji)) } #[given("a project config file {file_name:string} with emoji {emoji:string} and JSON {json}")] @@ -99,7 +89,7 @@ emoji = "{emoji}" json = {json} "# ); - write_config_file(world, file_name.as_str(), &content, true) + write_config_file(world, file_name.as_str(), &content) } #[given("a project config file {file_name:string} with default targets {targets:string}")] @@ -123,7 +113,7 @@ fn project_config_with_default_targets( default_targets = {targets_toml} " ); - write_config_file(world, file_name.as_str(), &content, true) + write_config_file(world, file_name.as_str(), &content) } #[given("a custom config file {file_name:string} with emoji {emoji:string}")] @@ -132,12 +122,27 @@ fn custom_config_with_emoji( file_name: FileName, emoji: EmojiPolicy, ) -> Result<()> { - write_config_file( + write_config_file(world, file_name.as_str(), &emoji_config_content(emoji)) +} + +#[when("the CLI is parsed with the workspace config file {file_name:string}")] +fn parse_cli_with_workspace_config(world: &TestWorld, file_name: FileName) -> Result<()> { + let config_path = world + .temp_dir + .borrow() + .as_ref() + .context("temp_dir should be set")? + .path() + .join(file_name.as_str()); + apply_cli_tokens( world, - file_name.as_str(), - &emoji_config_content(emoji), - false, - ) + vec![ + OsString::from("netsuke"), + OsString::from("--config"), + config_path.into_os_string(), + ], + ); + Ok(()) } #[given("the environment variable {var_name:string} is set to {value:string}")] diff --git a/tests/bdd/steps/ir.rs b/tests/bdd/steps/ir.rs index 26db08050..86e9a53b6 100644 --- a/tests/bdd/steps/ir.rs +++ b/tests/bdd/steps/ir.rs @@ -213,23 +213,10 @@ fn graph_target_implicit_deps(world: &TestWorld, target: &str, paths: &str) -> R /// Compile a manifest file to IR, storing result or error in state. fn compile_manifest_impl(world: &TestWorld, path: &str) { - // Convert relative test data paths to absolute to avoid issues when CWD - // is changed by parallel tests. Also lock CWD to the project root so - // that glob patterns inside the manifest resolve correctly. + // The manifest path is absolute, while the process CWD stays at the + // project root, so relative glob patterns continue to resolve correctly. let resolved = if std::path::Path::new(path).is_relative() && path.starts_with("tests/") { let manifest_dir = env!("CARGO_MANIFEST_DIR"); - if let Err(error) = world.ensure_global_state_lock() { - let outcome = Err(format!("failed to capture current directory: {error}")); - store_parse_outcome(&world.build_graph, &world.generation_error, outcome); - return; - } - if let Err(e) = std::env::set_current_dir(manifest_dir) { - let outcome = Err(format!( - "failed to set current directory to {manifest_dir} for path {path}: {e}" - )); - store_parse_outcome(&world.build_graph, &world.generation_error, outcome); - return; - } std::path::Path::new(manifest_dir) .join(path) .to_string_lossy() diff --git a/tests/bdd/steps/manifest/mod.rs b/tests/bdd/steps/manifest/mod.rs index cb049d331..43a51a0f7 100644 --- a/tests/bdd/steps/manifest/mod.rs +++ b/tests/bdd/steps/manifest/mod.rs @@ -68,32 +68,12 @@ pub(super) fn get_string_from_string_or_list( } fn parse_manifest_inner(world: &TestWorld, path: &ManifestPath) { - // Convert relative test data paths to absolute to avoid issues when CWD is - // changed by parallel tests. Also lock CWD to the project root so that - // glob patterns inside the manifest resolve correctly. + // Convert relative test data paths to absolute while the process CWD stays + // at the project root, where relative glob patterns resolve correctly. let manifest_path = if std::path::Path::new(path.as_str()).is_relative() && path.as_str().starts_with("tests/") { let manifest_dir = env!("CARGO_MANIFEST_DIR"); - // Hold the env lock and set CWD to the project root so that relative - // glob patterns (e.g. `tests/data/glob_files/*.txt`) resolve correctly. - if let Err(error) = world.ensure_global_state_lock() { - store_parse_outcome( - &world.manifest, - &world.manifest_error, - Err(format!("failed to capture current directory: {error}")), - ); - return; - } - // EnvLock is held; safe to mutate CWD for this scenario. - if let Err(e) = std::env::set_current_dir(manifest_dir) { - store_parse_outcome( - &world.manifest, - &world.manifest_error, - Err(format!("failed to set CWD to {manifest_dir}: {e}")), - ); - return; - } std::path::Path::new(manifest_dir) .join(path.as_str()) .to_string_lossy() diff --git a/tests/config_discovery_e2e_tests.rs b/tests/config_discovery_e2e_tests.rs index f9eff69d9..a6224595c 100644 --- a/tests/config_discovery_e2e_tests.rs +++ b/tests/config_discovery_e2e_tests.rs @@ -7,9 +7,17 @@ use anyhow::{Context, Result, ensure}; use assert_cmd::cargo::cargo_bin_cmd; use camino::{Utf8Path, Utf8PathBuf}; +use proptest::prelude::*; +use serde_json::Value; use tempfile::{TempDir, tempdir}; use test_support::fs as test_fs; +#[derive(Clone, Copy)] +enum ExplicitSelector { + Cli, + Environment, +} + fn workspace(context: &str) -> Result { let temp = tempdir().with_context(|| format!("create workspace for {context}"))?; test_fs::copy("tests/data/minimal.yml", temp.path().join("Netsukefile")) @@ -61,3 +69,86 @@ fn malformed_discovered_config_fails_the_binary_workflow() -> Result<()> { ); Ok(()) } + +/// Assert that an explicit relative selector stays anchored to the child CWD. +fn assert_explicit_relative_config_ignores_directory_anchor( + selector: ExplicitSelector, + project_name: &str, + config_name: &str, +) -> Result<()> { + let outer = tempdir().context("create invoking directory")?; + let project = outer.path().join(project_name); + test_fs::create_dir(&project).context("create directory-anchored project")?; + test_fs::copy("tests/data/minimal.yml", project.join("Netsukefile")) + .context("write project manifest")?; + test_fs::write(outer.path().join(config_name), "json = true\n") + .context("write invoking-directory config")?; + test_fs::write(project.join(config_name), "json = false\n") + .context("write directory-anchored config")?; + + let outer_path = utf8_workspace_path(&outer)?; + let mut command = isolated_netsuke_command(&outer_path); + command.args(["-C", project_name]); + match selector { + ExplicitSelector::Cli => { + command.args(["--config", config_name]); + } + ExplicitSelector::Environment => { + command.env("NETSUKE_CONFIG", config_name); + } + } + let output = command + .arg("generate") + .output() + .context("run generate with an explicit relative config")?; + + ensure!( + output.status.success(), + "generate should succeed: {output:?}" + ); + let document: Value = serde_json::from_slice(&output.stdout) + .context("explicit process-CWD config should enable JSON output")?; + ensure!( + document + .pointer("/result/content") + .and_then(Value::as_str) + .is_some(), + "JSON output should contain the generated Ninja artefact: {document}", + ); + Ok(()) +} + +/// An explicit relative CLI selector stays anchored to the child process CWD. +#[test] +fn cli_explicit_relative_config_ignores_directory_anchor() -> Result<()> { + assert_explicit_relative_config_ignores_directory_anchor( + ExplicitSelector::Cli, + "project", + "relative.toml", + ) +} + +proptest! { + #![proptest_config(ProptestConfig::with_cases(32))] + + /// Generated selector names and anchors preserve process-CWD resolution. + #[test] + fn explicit_relative_config_never_rebases_under_directory( + selector_is_env in any::(), + project_name in "[a-z]{1,12}", + config_stem in "[a-z]{1,12}", + ) { + let selector = if selector_is_env { + ExplicitSelector::Environment + } else { + ExplicitSelector::Cli + }; + let config_name = format!("{config_stem}.toml"); + let result = assert_explicit_relative_config_ignores_directory_anchor( + selector, + &project_name, + &config_name, + ); + prop_assert!(result.is_ok(), "{result:?}"); + } +} diff --git a/tests/features/configuration_discovery.feature b/tests/features/configuration_discovery.feature index 2961d2d31..cf3d78efa 100644 --- a/tests/features/configuration_discovery.feature +++ b/tests/features/configuration_discovery.feature @@ -39,7 +39,7 @@ Feature: Configuration file discovery and precedence Given a temporary workspace And a project config file ".netsuke.toml" with emoji "never" And a custom config file "custom.toml" with emoji "always" - When the CLI is parsed with "--config custom.toml" + When the CLI is parsed with the workspace config file "custom.toml" Then parsing succeeds And the emoji policy is "always" @@ -58,7 +58,7 @@ Feature: Configuration file discovery and precedence And a custom config file "cli.toml" with emoji "always" And a custom config file "env.toml" with emoji "never" And the environment variable "NETSUKE_CONFIG" points to "env.toml" - When the CLI is parsed with "--config cli.toml" + When the CLI is parsed with the workspace config file "cli.toml" Then parsing succeeds And the emoji policy is "always"