1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! Shared thread-local seam for reading process-environment variables in a way
//! that unit tests can override hermetically.
//!
//! Production code that must stay testable reads env through [`env_var_seamed`]
//! instead of [`std::env::var`]. Under `cfg(not(test))` that is exactly
//! `std::env::var(key).ok()`. Under `cfg(test)` the process environment is
//! structurally invisible: reads come from a per-thread override map instead,
//! so ambient shell configuration (a developer's exported `HARN_LLM_MODEL`, a
//! CI wrapper's provider default) can never seed state a test did not ask for,
//! and no cross-test serialization lock is needed for the seamed variables.
//!
//! Tests inject values through [`TestEnvGuard`], obtained from
//! [`test_env_guard`]. The guard clears this thread's overrides on both
//! creation and drop, giving each test hermetic edges: neither ambient
//! configuration nor a sibling test's leftovers leak in, and nothing leaks out.
//!
//! # Visibility across threads
//!
//! The override map is thread-local, so a value set on the test thread is
//! invisible to work the test spawns on other threads (e.g. a multi-threaded
//! Tokio runtime). Code exercised on a worker thread that must observe a
//! test-provided value has to keep reading real process env under a
//! process-global lock instead. The base-URL resolver
//! [`crate::llm_config::resolve_base_url`] is deliberately left on
//! [`std::env::var`] for this reason.
//!
//! # Convergence
//!
//! This module is the sole owner of the keyed-override seam pattern. Two
//! domain-local seams predated it and now converge here, reading through
//! [`env_var_seamed`] and layering any domain lifecycle over [`test_env_guard`]
//! rather than duplicating the thread-local map:
//! - `egress`'s `EgressTestEnvGuard` (the `HARN_EGRESS_*` family) owns a
//! [`TestEnvGuard`] and adds an egress-policy reset on its own lifecycle.
//! - `stdlib::sandbox::handler_env`'s `HandlerSandboxTestGuard` (the single
//! `HARN_HANDLER_SANDBOX` variable) owns a [`TestEnvGuard`] purely to keep
//! the single-variable `set(value)` ergonomics; it adds no extra behavior.
/// Reads one process-environment variable through the test seam.
///
/// - `cfg(not(test))`: `std::env::var(key).ok()`.
/// - `cfg(test)`: the calling thread's override map only; the process
/// environment is not consulted.
pub(crate) fn env_var_seamed(key: &str) -> Option<String> {
#[cfg(test)]
{
overrides::get(key)
}
// Another crate's tests opt in through `vm-test-support`; feature
// unification can also turn it on for a product binary, so the override
// map only answers while a guard is alive on this thread.
#[cfg(all(not(test), feature = "vm-test-support"))]
{
if overrides::active() {
overrides::get(key)
} else {
std::env::var(key).ok()
}
}
#[cfg(not(any(test, feature = "vm-test-support")))]
{
std::env::var(key).ok()
}
}
#[cfg(any(test, feature = "vm-test-support"))]
mod overrides {
use std::cell::{Cell, RefCell};
use std::collections::BTreeMap;
thread_local! {
static OVERRIDES: RefCell<BTreeMap<String, String>> =
const { RefCell::new(BTreeMap::new()) };
static GUARDS: Cell<usize> = const { Cell::new(0) };
}
#[cfg_attr(test, allow(dead_code))]
pub(super) fn active() -> bool {
GUARDS.with(|guards| guards.get() > 0)
}
pub(super) fn enter() {
GUARDS.with(|guards| guards.set(guards.get() + 1));
}
pub(super) fn exit() {
GUARDS.with(|guards| guards.set(guards.get().saturating_sub(1)));
}
pub(super) fn get(key: &str) -> Option<String> {
OVERRIDES.with(|overrides| overrides.borrow().get(key).cloned())
}
pub fn set(key: &str, value: &str) {
OVERRIDES.with(|overrides| {
overrides
.borrow_mut()
.insert(key.to_owned(), value.to_owned());
});
}
pub(super) fn clear() {
OVERRIDES.with(|overrides| overrides.borrow_mut().clear());
}
}
/// Gives a test a clean, hermetic env universe for the seamed variables: on
/// both creation and drop it clears this thread's overrides, so neither ambient
/// configuration nor a sibling test's leftovers can leak in, and nothing leaks
/// out. All seamed state is thread-keyed, so no cross-test serialization is
/// needed and the guard is safe to hold across `await` points on a
/// single-threaded runtime.
#[cfg(any(test, feature = "vm-test-support"))]
#[must_use]
pub fn test_env_guard() -> TestEnvGuard {
overrides::clear();
overrides::enter();
TestEnvGuard {}
}
/// Guard returned by [`test_env_guard`]. Injects env values for this thread via
/// [`TestEnvGuard::set`] and clears all overrides on drop.
#[cfg(any(test, feature = "vm-test-support"))]
pub struct TestEnvGuard {}
#[cfg(any(test, feature = "vm-test-support"))]
impl TestEnvGuard {
/// Sets an environment variable for this thread only, visible to
/// [`env_var_seamed`] readers on the same thread.
pub fn set(&self, key: &str, value: &str) {
overrides::set(key, value);
}
}
#[cfg(any(test, feature = "vm-test-support"))]
impl Drop for TestEnvGuard {
fn drop(&mut self) {
overrides::clear();
overrides::exit();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn seam_reads_overrides_and_clears_on_drop() {
{
let env = test_env_guard();
env.set("HARN_LLM_PROVIDER", "mock");
assert_eq!(env_var_seamed("HARN_LLM_PROVIDER").as_deref(), Some("mock"));
// A key never set reads as absent regardless of ambient env.
assert_eq!(env_var_seamed("HARN_LLM_MODEL"), None);
}
// Dropping the guard clears the thread's overrides.
assert_eq!(env_var_seamed("HARN_LLM_PROVIDER"), None);
}
#[test]
fn fresh_guard_clears_prior_overrides() {
let first = test_env_guard();
first.set("HARN_DEFAULT_PROVIDER", "openai");
drop(first);
let second = test_env_guard();
assert_eq!(env_var_seamed("HARN_DEFAULT_PROVIDER"), None);
drop(second);
}
}