Story: Injectable environment provider in ores.platform for test-safe, parallel-safe env var access
Table of Contents
This page documents a story in Sprint 25. It captures the goal, current status, acceptance criteria, and the tasks that compose it.
Goal
Any code needing environment variables (starting with environment_mapper_factory's consumers, and ores.testing's env-clearing test helpers) can be tested with fully isolated, parallel-safe environment state, instead of mutating the real process environment.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent sprint | Sprint 25 |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-08-07 |
Acceptance
- A short prior-art survey is written up (in this story's Notes, or a linked knowledge doc) before design work starts: how established C++ codebases/test frameworks solve "swap real environment access for a fake one in tests" – e.g. GoogleTest's ScopedEnvironmentVariable, Abseil's flag/env abstractions, dependency-injected config-provider patterns in other languages' test frameworks (Python's monkeypatch, Rust's temp-env crate), and any prior discussion/precedent already in this codebase (e.g. how database_helper or other scoped test fixtures solve analogous global-state problems). The survey should name at least 2-3 concrete approaches with their tradeoffs (thread-local override vs. DI'd interface vs. process-per-test isolation) so the design task below picks from evaluated options rather than inventing from scratch.
- ores.platform exposes an environment-provider abstraction (e.g. a virtual interface or a thread-local/DI-swappable default) that ores::platform::environment::environment's get/set/unset calls go through; a test-only provider implementation exists for injecting fake values scoped to a test (RAII or fixture-based) with no real setenv/unsetenv/getenv calls and no cross-test/cross-thread interference; ores::testing::scoped_env_unset (or its replacement) is migrated to use the new provider instead of mutating real process environment; the 11 domain-service parser tests migrated in the parser-boilerplate task continue to pass, now via the injected-provider path.
Tasks
| Task | State | Start | End | Description |
|---|---|---|---|---|
| Research prior art for swappable environment-access abstractions in tests | DONE | 2026-08-07 | 2026-08-07 | Survey how established C++ codebases and test frameworks solve the general problem this story addresses: swapping real process-environment access for injected fake values in tests, safely under parallel test execution. Cover at minimum GoogleTest's ScopedEnvironmentVariable, Abseil's config/flag abstractions, and equivalent patterns in other languages' test frameworks (Python's monkeypatch, Rust's temp-env) as points of comparison, plus any existing precedent already in this codebase for solving analogous global-state problems in tests (e.g. database_helper, scoped_database_helper). Write up findings with at least 2-3 named concrete approaches and their tradeoffs (thread-local override vs. DI'd interface vs. process-per-test isolation). |
| Design and implement a swappable environment-provider abstraction in ores.platform | DONE | 2026-08-07 | 2026-08-07 | Based on the prior-art research task's findings, design and implement a swappable environment-provider abstraction that ores::platform::environment::environment's get/set/unset go through, so tests can inject isolated fake environment state instead of mutating real process-global environment. Migrate ores::testing::scoped_env_unset (or replace it) to use the new provider. Migrate the 11 domain-service parser tests that currently use scoped_env_unset to the new mechanism. |
Decisions
- Approach 1 (provider interface with thread_local scoped injection) was selected over Approach 2 (DI'd interface wired at construction) and Approach 3 (global override table). Rationale recorded in the research task's Notes.
- boost::program_options reads env vars from the real process environment
(
std::getenv), not throughenvironment::get_value. Thescoped_environment_overrideguard must therefore sync the real environment in addition to swapping the provider — a pure in-memory fake is insufficient for this codebase's parser tests. - The provider swap uses
thread_localstorage for per-test isolation; the real-environment sync remains process-global. Under Catch2's sequential execution model this is safe; if tests are ever run in parallel, the real-env sync would need a process-wide mutex or a move away fromboost::program_options's directgetenvusage. scoped_env_unsetis superseded and deleted — all 11 consumers were migrated toscoped_environment_override; no remaining call-sites exist.