use super::*;
static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
fn with_var<T>(name: &str, value: Option<&str>, body: impl FnOnce() -> T) -> T {
let _guard = ENV_LOCK
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let previous = std::env::var_os(name);
match value {
Some(value) => std::env::set_var(name, value),
None => std::env::remove_var(name),
}
let outcome = body();
match previous {
Some(previous) => std::env::set_var(name, previous),
None => std::env::remove_var(name),
}
outcome
}
const PROBE: &str = "RUNNING_PROCESS_ENV_VARS_PROBE";
#[test]
fn an_owned_flag_is_on_only_for_a_recognised_affirmative() {
for on in ["1", "true", "TRUE", " True ", "yes", "on"] {
assert!(
with_var(PROBE, Some(on), || flag_owned(PROBE)),
"{on:?} must turn an owned switch on"
);
}
for off in ["", "0", "false", "no", "off", "OFF", " 0 "] {
assert!(
!with_var(PROBE, Some(off), || flag_owned(PROBE)),
"{off:?} must leave an owned switch off"
);
}
for unknown in ["maybe", "2", "y", "enabled", "tru"] {
assert!(
!with_var(PROBE, Some(unknown), || flag_owned(PROBE)),
"{unknown:?} is not a spelling we defined, so the switch stays off"
);
}
assert!(!with_var(PROBE, None, || flag_owned(PROBE)), "unset is off");
}
#[test]
fn a_foreign_flag_is_off_only_for_a_recognised_negative() {
for off in ["", "0", "false", "no", "off", " OFF "] {
assert!(
!with_var(PROBE, Some(off), || flag_foreign(PROBE)),
"{off:?} must turn a foreign switch off"
);
}
for on in ["1", "true", "yes", "on", "maybe", "2", "enabled"] {
assert!(
with_var(PROBE, Some(on), || flag_foreign(PROBE)),
"{on:?} is not a falsy spelling, so the switch reads as on"
);
}
}
#[test]
fn an_unset_foreign_flag_is_off() {
assert!(!with_var(PROBE, None, || flag_foreign(PROBE)));
}
#[test]
fn the_two_flag_semantics_differ_only_on_unrecognised_values() {
for agreed in ["1", "true", "yes", "on", "0", "false", "no", "off", ""] {
assert_eq!(
with_var(PROBE, Some(agreed), || flag_owned(PROBE)),
with_var(PROBE, Some(agreed), || flag_foreign(PROBE)),
"{agreed:?} is a recognised spelling; both semantics must agree"
);
}
for disputed in ["maybe", "2", "enabled"] {
assert!(
!with_var(PROBE, Some(disputed), || flag_owned(PROBE)),
"owned: unknown is off"
);
assert!(
with_var(PROBE, Some(disputed), || flag_foreign(PROBE)),
"foreign: unknown is on"
);
}
}
#[test]
fn an_exact_value_guard_refuses_plausible_misspellings() {
assert!(with_var(BROKER_ALLOW_PRIVILEGED.name, Some("1"), || {
BROKER_ALLOW_PRIVILEGED.is_set()
}));
for refused in ["true", "yes", "on", "TRUE", " 1 "] {
assert!(
!with_var(BROKER_ALLOW_PRIVILEGED.name, Some(refused), || {
BROKER_ALLOW_PRIVILEGED.is_set()
}),
"{refused:?} must not open a privilege guard"
);
}
}
#[test]
fn scanning_a_value_agrees_with_reading_the_variable() {
for value in ["1", "true", "maybe", "0", "false", "off", ""] {
assert_eq!(
value_is_affirmative_foreign(value),
with_var(PROBE, Some(value), || flag_foreign(PROBE)),
"{value:?} must read the same scanned as it does read directly"
);
}
}
#[test]
fn declarations_are_sorted_and_unique() {
let names: Vec<&str> = DECLARED.iter().map(|var| var.name).collect();
let mut sorted = names.clone();
sorted.sort_unstable();
assert_eq!(names, sorted, "declarations must stay alphabetical");
let mut unique = sorted.clone();
unique.dedup();
assert_eq!(unique, sorted, "a variable must be declared once");
}
#[test]
fn declarations_are_documented() {
for var in DECLARED {
assert!(!var.name.is_empty());
assert!(
!var.default.is_empty(),
"{} has no documented default",
var.name
);
assert!(!var.summary.is_empty(), "{} has no summary", var.name);
match var.owner {
Owner::Crate => assert!(
var.name.starts_with("RUNNING_PROCESS_"),
"{} is declared as ours but is not namespaced",
var.name
),
Owner::Foreign => assert!(!var.name.is_empty()),
}
}
}
#[test]
fn declaration_table_covers_every_variable() {
let declared: std::collections::BTreeSet<&str> = DECLARED.iter().map(|var| var.name).collect();
let mut undeclared: std::collections::BTreeSet<String> = Default::default();
for path in source_files() {
let text = std::fs::read_to_string(&path).expect("read source file");
for found in literal_env_names(&text) {
if !declared.contains(found.as_str()) {
undeclared.insert(format!("{found} (in {})", path.display()));
}
}
}
assert!(
undeclared.is_empty(),
"these variables are read but not declared in DECLARED:\n {}",
undeclared.into_iter().collect::<Vec<_>>().join("\n ")
);
}
const NOT_ENVIRONMENT_READS: &[&str] = &[
"RUNNING_PROCESS_TEST_CLIENT_ONLY_ENV",
"RUNNING_PROCESS_TEST_DAEMON_ONLY_ENV",
"RUNNING_PROCESS_MATERIALIZE_CANARY",
"RUNNING_PROCESS_BASELINE_CANARY",
"RUNNING_PROCESS_ENV_VARS_PROBE",
];
const PREFIX: &str = "RUNNING_PROCESS_";
const READS: &[&str] = &["env::var(\"", "env::var_os(\""];
fn literal_env_names(text: &str) -> Vec<String> {
let mut found = Vec::new();
for read in READS {
let mut index = 0;
while let Some(start) = text[index..].find(read) {
let open = index + start + read.len();
let Some(len) = text[open..].find('"') else {
break;
};
let name = &text[open..open + len];
let is_a_name = !name.is_empty()
&& name != PREFIX
&& name
.chars()
.all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_');
if is_a_name && !NOT_ENVIRONMENT_READS.contains(&name) {
found.push(name.to_owned());
}
index = open + len;
}
}
found
}
fn source_files() -> Vec<std::path::PathBuf> {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let mut files = Vec::new();
let mut stack = vec![root];
while let Some(dir) = stack.pop() {
let Ok(entries) = std::fs::read_dir(&dir) else {
continue;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
stack.push(path);
} else if path.extension().is_some_and(|ext| ext == "rs") {
files.push(path);
}
}
}
assert!(!files.is_empty(), "the crate must have sources to scan");
files
}
#[test]
fn an_unset_flag_matches_its_declared_default() {
for var in DECLARED {
let declared_on = match var.kind {
EnvKind::OwnedFlag | EnvKind::ForeignFlag | EnvKind::OptOutFlag => {
describes_an_enabled_default(var.default)
}
EnvKind::ExactValue(_) => describes_an_enabled_default(var.default),
_ => continue,
};
let actually_on = with_var(var.name, None, || var.is_set());
assert_eq!(
actually_on,
declared_on,
"{}: declared default {:?} says {}, but reading it unset gives {}",
var.name,
var.default,
if declared_on { "on" } else { "off" },
actually_on
);
}
}
fn describes_an_enabled_default(default: &str) -> bool {
match default {
"broker-owned bind is used" => true,
"privileged startup is refused"
| "the broker is used"
| "processes are tracked"
| "the guard does not run"
| "the process is not a daemon"
| "a dev-build daemon relocates itself"
| "not running under GitHub Actions" => false,
other => panic!(
"default {other:?} is not a phrasing this check recognises; \
add it rather than letting the assertion pass vacuously"
),
}
}
#[test]
fn zero_means_what_each_numeric_variable_declares() {
let mut checked = 0;
for var in DECLARED {
let EnvKind::Number {
zero_selects_default,
} = var.kind
else {
continue;
};
checked += 1;
let sentinel = std::time::Duration::from_millis(9_999);
let read = with_var(var.name, Some("0"), || var.millis_or(sentinel));
if zero_selects_default {
assert_eq!(
read, sentinel,
"{}: zero must fall back to the caller's default",
var.name
);
} else {
assert_eq!(
read,
std::time::Duration::ZERO,
"{}: zero must be honoured as zero",
var.name
);
}
}
assert!(checked >= 8, "expected the numeric variables to be checked");
}
#[test]
fn an_unparseable_number_falls_back_to_the_default() {
let sentinel = std::time::Duration::from_millis(4_242);
for var in DECLARED {
if !matches!(var.kind, EnvKind::Number { .. }) {
continue;
}
for junk in ["", " ", "abc", "12ms", "-1", "1.5"] {
assert_eq!(
with_var(var.name, Some(junk), || var.millis_or(sentinel)),
sentinel,
"{}: {junk:?} is not a number and must not be read as one",
var.name
);
}
}
}
#[test]
fn a_number_is_read_through_surrounding_whitespace() {
let sentinel = std::time::Duration::from_millis(1);
assert_eq!(
with_var(CLIENT_RPC_TIMEOUT_MS.name, Some(" 250 "), || {
CLIENT_RPC_TIMEOUT_MS.millis_or(sentinel)
}),
std::time::Duration::from_millis(250)
);
}
#[test]
fn a_path_is_taken_verbatim_and_an_empty_one_is_absent() {
assert_eq!(
with_var(MANIFEST_DIR.name, Some("/tmp/manifests"), || MANIFEST_DIR
.path()),
Some(std::path::PathBuf::from("/tmp/manifests"))
);
assert_eq!(
with_var(MANIFEST_DIR.name, Some(""), || MANIFEST_DIR.path()),
None
);
assert_eq!(
with_var(MANIFEST_DIR.name, None, || MANIFEST_DIR.path()),
None
);
}
#[test]
fn empty_text_is_absent() {
assert_eq!(
with_var(DAEMON_SCOPE.name, Some("dev"), || DAEMON_SCOPE.text()),
Some("dev".to_owned())
);
assert_eq!(
with_var(DAEMON_SCOPE.name, Some(""), || DAEMON_SCOPE.text()),
None
);
}
#[test]
fn no_literal_environment_read_lives_outside_this_module() {
let mut offenders: Vec<String> = Vec::new();
for path in source_files() {
let name = path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or_default();
if name == "env_vars.rs" {
continue;
}
let text = std::fs::read_to_string(&path).expect("read source file");
for read in READS {
let mut index = 0;
while let Some(start) = text[index..].find(read) {
let open = index + start + read.len();
let Some(len) = text[open..].find('"') else {
break;
};
let named = &text[open..open + len];
if !NOT_ENVIRONMENT_READS.contains(&named) {
offenders.push(format!("{}: {read}{named}\")", path.display()));
}
index = open + len;
}
}
}
offenders.sort();
assert!(
offenders.is_empty(),
"read these through `crate::env_vars` instead, so the variable is \
declared and parsed one way:\n {}",
offenders.join("\n ")
);
}