diff --git a/app/scripts/e2e-run-session.sh b/app/scripts/e2e-run-session.sh index 0644c01e1..195d6d6ff 100755 --- a/app/scripts/e2e-run-session.sh +++ b/app/scripts/e2e-run-session.sh @@ -135,6 +135,7 @@ trap cleanup EXIT export VITE_BACKEND_URL="http://127.0.0.1:${E2E_MOCK_PORT}" export BACKEND_URL="http://127.0.0.1:${E2E_MOCK_PORT}" +export OPENHUMAN_E2E_MODE="1" export APPIUM_PORT export CEF_CDP_PORT diff --git a/src/openhuman/test_support/rpc.rs b/src/openhuman/test_support/rpc.rs index 04a4fa352..959f304d7 100644 --- a/src/openhuman/test_support/rpc.rs +++ b/src/openhuman/test_support/rpc.rs @@ -16,6 +16,8 @@ use crate::openhuman::config::{clear_active_user, default_root_openhuman_dir}; use crate::openhuman::cron; use crate::rpc::RpcOutcome; +const E2E_MODE_ENV_VAR: &str = "OPENHUMAN_E2E_MODE"; + /// Wipe summary returned to the caller for debug visibility. #[derive(Debug, Serialize)] pub struct ResetSummary { @@ -25,6 +27,19 @@ pub struct ResetSummary { pub active_user_cleared: bool, } +fn ensure_e2e_mode_enabled() -> Result<(), String> { + ensure_e2e_mode_value(std::env::var(E2E_MODE_ENV_VAR).ok().as_deref()) +} + +fn ensure_e2e_mode_value(raw: Option<&str>) -> Result<(), String> { + match raw.map(str::trim) { + Some("1" | "true" | "TRUE" | "yes" | "YES") => Ok(()), + _ => Err(format!( + "test_reset is disabled unless {E2E_MODE_ENV_VAR} is set to one of: 1, true, TRUE, yes, YES" + )), + } +} + /// Reset persistent state to the "fresh install" baseline. /// /// Errors at any individual wipe step short-circuit and surface back to the @@ -32,6 +47,11 @@ pub struct ResetSummary { /// downstream tests pass on contaminated state. pub async fn reset() -> Result, String> { log::debug!("[test_reset] entry"); + ensure_e2e_mode_enabled().map_err(|e| { + log::debug!("[test_reset] rejected: {e}"); + e + })?; + let mut config = Config::load_or_init() .await .map_err(|e| format!("test_reset: failed to load config: {e}"))?; @@ -101,3 +121,46 @@ pub async fn reset_json() -> Result { "previously_authenticated": outcome.value.api_key_was_set, })) } + +#[cfg(test)] +mod tests { + use super::{ensure_e2e_mode_value, reset, E2E_MODE_ENV_VAR}; + use std::sync::{Mutex, OnceLock}; + + static E2E_MODE_ENV_LOCK: OnceLock> = OnceLock::new(); + + fn env_lock() -> std::sync::MutexGuard<'static, ()> { + E2E_MODE_ENV_LOCK + .get_or_init(|| Mutex::new(())) + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()) + } + + #[tokio::test] + async fn reset_rejects_when_e2e_mode_unset() { + let _guard = env_lock(); + let prior = std::env::var(E2E_MODE_ENV_VAR).ok(); + std::env::remove_var(E2E_MODE_ENV_VAR); + + let err = reset() + .await + .expect_err("unset E2E mode must reject test_reset"); + + match prior { + Some(value) => std::env::set_var(E2E_MODE_ENV_VAR, value), + None => std::env::remove_var(E2E_MODE_ENV_VAR), + } + + assert!( + err.contains("OPENHUMAN_E2E_MODE") && err.contains("is set to one of"), + "unexpected guard error: {err}" + ); + } + + #[test] + fn reset_guard_accepts_explicit_e2e_mode() { + ensure_e2e_mode_value(Some("1")).expect("1 enables E2E mode"); + ensure_e2e_mode_value(Some("true")).expect("true enables E2E mode"); + ensure_e2e_mode_value(Some("yes")).expect("yes enables E2E mode"); + } +}