Context
Netsuke has three shapes for the same idea — read-only environment access:
| Site |
Shape |
src/locale_resolution.rs:19 |
bespoke trait EnvProvider { fn var(&self, key: &str) -> Option<String>; } with a SystemEnv impl |
src/output_mode.rs:53 |
closure |key| env::var(key).ok() passed to resolve_with |
src/output_prefs.rs:192,237 |
the same closure passed to resolve_from_theme_with and resolve_with |
src/theme.rs (via resolve_theme) |
receives the same closure |
The AGENTS.md testing mandate accepts a narrow closure seam where a trait object would be disproportionate, so these seams are not defective and stay as they are. An earlier version of this issue proposed converging all four sites onto mockable::Env; that proposal was withdrawn (see the amendment note below and the discussion in the comments).
What remains is a genuine defect in the test double: test_support::locale_stubs::StubEnv is permissive. A stub that returns None for any undeclared key hides exactly the change a test double should catch — were the code under test altered to read a differently-named variable, through a rename, a typo, or a new precedence rung, the permissive stub would quietly answer None and the test would still pass while asserting nothing about the new read.
Required work
- Retain
locale_resolution::EnvProvider and SystemEnv.
- Retain the
read_env closure seams in output_mode, output_prefs, and theme.
- Retain
test_support::locale_stubs::StubEnv.
- Make
StubEnv strict:
- it returns values only for keys the test explicitly declared;
- a declared key may be explicitly unset, so "declared but unset" is distinguishable from "never expected to be read" and reports
None without panicking;
- any read of an undeclared key panics, and the panic message names the offending key.
Acceptance criteria
StubEnv cannot be constructed in a permissive state: every constructor requires the test to declare what may be read.
- A read of a declared, set key returns the declared value; a read of a declared, unset key returns
None.
- A read of an undeclared key panics with a message naming the key.
- The strictness is validated by tests covering all three behaviours, including redeclaration semantics (the most recent declaration for a key wins).
make check-fmt, make lint, and make test pass.
Amendment history: this issue originally required replacing EnvProvider and StubEnv with a shared mockable::Env/MockEnv injection across locale, output mode, output preferences, and theme resolution. That convergence proposal was withdrawn — the repository's established seams are proportionate for these single-variable sites — and the body above has been rewritten so every section states the remaining requirement: strictness of StubEnv, not replacement.
Context
Netsuke has three shapes for the same idea — read-only environment access:
src/locale_resolution.rs:19trait EnvProvider { fn var(&self, key: &str) -> Option<String>; }with aSystemEnvimplsrc/output_mode.rs:53|key| env::var(key).ok()passed toresolve_withsrc/output_prefs.rs:192,237resolve_from_theme_withandresolve_withsrc/theme.rs(viaresolve_theme)The AGENTS.md testing mandate accepts a narrow closure seam where a trait object would be disproportionate, so these seams are not defective and stay as they are. An earlier version of this issue proposed converging all four sites onto
mockable::Env; that proposal was withdrawn (see the amendment note below and the discussion in the comments).What remains is a genuine defect in the test double:
test_support::locale_stubs::StubEnvis permissive. A stub that returnsNonefor any undeclared key hides exactly the change a test double should catch — were the code under test altered to read a differently-named variable, through a rename, a typo, or a new precedence rung, the permissive stub would quietly answerNoneand the test would still pass while asserting nothing about the new read.Required work
locale_resolution::EnvProviderandSystemEnv.read_envclosure seams inoutput_mode,output_prefs, andtheme.test_support::locale_stubs::StubEnv.StubEnvstrict:Nonewithout panicking;Acceptance criteria
StubEnvcannot be constructed in a permissive state: every constructor requires the test to declare what may be read.None.make check-fmt,make lint, andmake testpass.Amendment history: this issue originally required replacing
EnvProviderandStubEnvwith a sharedmockable::Env/MockEnvinjection across locale, output mode, output preferences, and theme resolution. That convergence proposal was withdrawn — the repository's established seams are proportionate for these single-variable sites — and the body above has been rewritten so every section states the remaining requirement: strictness ofStubEnv, not replacement.