commit 376a1d4c8e8dc0042c9f9b3575dae9b4c3b8d3a3
parent ec38a578584b4bb9d2e37ea6943474ce220fa089
Author: triesap <tyson@radroots.org>
Date: Fri, 3 Jul 2026 21:10:55 +0000
rhi: make remote auth tests deterministic
- route bearer token reads through one explicit resolver
- add test hook state for remote auth token presence
- pin recovered replay missing-auth tests to deterministic hooks
- preserve RHI env-var validation before token reads
Diffstat:
1 file changed, 79 insertions(+), 3 deletions(-)
diff --git a/src/features/trade_validation_receipt.rs b/src/features/trade_validation_receipt.rs
@@ -399,9 +399,7 @@ fn remote_http_auth_token(
TradeValidationReceiptRemoteHttpAuth::NoAuth => Ok(None),
TradeValidationReceiptRemoteHttpAuth::BearerTokenEnv { env_var } => {
let env_var = validate_rhi_secret_env_var_name(env_var)?;
- let value = std::env::var(env_var).map_err(|_| {
- TradeValidationReceiptJobError::RemoteHttpAuthTokenMissing(env_var.to_owned())
- })?;
+ let value = remote_http_auth_env_var_value(env_var)?;
let token = value.trim();
if token.is_empty() {
return Err(TradeValidationReceiptJobError::RemoteHttpAuthTokenMissing(
@@ -413,6 +411,15 @@ fn remote_http_auth_token(
}
}
+fn remote_http_auth_env_var_value(env_var: &str) -> Result<String, TradeValidationReceiptJobError> {
+ #[cfg(test)]
+ if let Some(value) = remote_http_auth_env_var_value_test_hook(env_var) {
+ return value;
+ }
+ std::env::var(env_var)
+ .map_err(|_| TradeValidationReceiptJobError::RemoteHttpAuthTokenMissing(env_var.to_owned()))
+}
+
fn validate_job_execution_policy(
prover_policy: &TradeValidationReceiptProverPolicy,
request: &RadrootsTradeTransitionProofRequestV1,
@@ -2786,6 +2793,7 @@ struct TradeValidationReceiptTestHooks {
std::collections::VecDeque<Result<RadrootsNostrEvent, TradeValidationReceiptJobError>>,
fetch_events_results:
std::collections::VecDeque<Result<Vec<RadrootsNostrEvent>, TradeValidationReceiptJobError>>,
+ remote_http_auth_env_values: std::collections::BTreeMap<String, Option<String>>,
publish_event_results:
std::collections::VecDeque<Result<String, TradeValidationReceiptJobError>>,
#[cfg(feature = "sp1_verify")]
@@ -2813,6 +2821,23 @@ fn trade_validation_receipt_test_hooks()
}
#[cfg(test)]
+fn remote_http_auth_env_var_value_test_hook(
+ env_var: &str,
+) -> Option<Result<String, TradeValidationReceiptJobError>> {
+ trade_validation_receipt_test_hooks()
+ .lock()
+ .unwrap_or_else(std::sync::PoisonError::into_inner)
+ .remote_http_auth_env_values
+ .get(env_var)
+ .map(|value| match value {
+ Some(value) => Ok(value.clone()),
+ None => Err(TradeValidationReceiptJobError::RemoteHttpAuthTokenMissing(
+ env_var.to_owned(),
+ )),
+ })
+}
+
+#[cfg(test)]
fn pop_fetch_event_by_id_hook() -> Option<Result<RadrootsNostrEvent, TradeValidationReceiptJobError>>
{
trade_validation_receipt_test_hooks()
@@ -3922,6 +3947,22 @@ mod tests {
policy
}
+ fn set_remote_http_auth_env_missing(env_var: &str) {
+ trade_validation_receipt_test_hooks()
+ .lock()
+ .unwrap_or_else(std::sync::PoisonError::into_inner)
+ .remote_http_auth_env_values
+ .insert(env_var.to_string(), None);
+ }
+
+ fn set_remote_http_auth_env_value(env_var: &str, value: &str) {
+ trade_validation_receipt_test_hooks()
+ .lock()
+ .unwrap_or_else(std::sync::PoisonError::into_inner)
+ .remote_http_auth_env_values
+ .insert(env_var.to_string(), Some(value.to_string()));
+ }
+
#[cfg(feature = "sp1_verify")]
fn remote_response(
status: RadrootsSp1TradeRemoteProverStatus,
@@ -4292,6 +4333,38 @@ mod tests {
));
}
+ #[test]
+ fn remote_http_auth_test_hook_controls_process_env_state() {
+ let _guard = test_guard();
+ let mut policy = recovered_result_missing_auth_policy();
+ set_remote_http_auth_env_missing(RECOVERED_RESULT_MISSING_AUTH_ENV);
+
+ assert!(matches!(
+ policy.validate(),
+ Err(TradeValidationReceiptJobError::RemoteHttpAuthTokenMissing(env_var))
+ if env_var == RECOVERED_RESULT_MISSING_AUTH_ENV
+ ));
+
+ set_remote_http_auth_env_value(RECOVERED_RESULT_MISSING_AUTH_ENV, " test-token ");
+ match policy.validate() {
+ Ok(()) if cfg!(feature = "sp1_verify") => {}
+ Err(TradeValidationReceiptJobError::ProverBackendUnavailable("remote_http_prove"))
+ if !cfg!(feature = "sp1_verify") => {}
+ result => panic!("unexpected remote auth validation result: {result:?}"),
+ }
+
+ policy.remote_http.as_mut().expect("remote config").auth =
+ TradeValidationReceiptRemoteHttpAuth::BearerTokenEnv {
+ env_var: "RADROOTS_TEST_REMOTE_HTTP_TOKEN".to_string(),
+ };
+ assert!(matches!(
+ policy.validate(),
+ Err(TradeValidationReceiptJobError::RemoteHttpInvalidConfig(
+ "auth.env_var"
+ ))
+ ));
+ }
+
#[cfg(feature = "sp1_verify")]
#[test]
fn remote_http_policy_accepts_core_mode_when_configured() {
@@ -6367,6 +6440,7 @@ mod tests {
async fn proof_job_rejects_recovered_result_intent_missing_remote_auth_before_publish() {
let _guard = test_guard();
let current_policy = recovered_result_missing_auth_policy();
+ set_remote_http_auth_env_missing(RECOVERED_RESULT_MISSING_AUTH_ENV);
let scenario = recovered_sp1_result_intent_scenario(¤t_policy).await;
let error = handle_trade_validation_receipt_job_request(
@@ -6405,6 +6479,7 @@ mod tests {
async fn proof_job_rejects_missing_remote_auth_before_invalid_stored_result_json() {
let _guard = test_guard();
let current_policy = recovered_result_missing_auth_policy();
+ set_remote_http_auth_env_missing(RECOVERED_RESULT_MISSING_AUTH_ENV);
let scenario = recovered_sp1_result_intent_scenario_with_stored_events(
¤t_policy,
None,
@@ -6448,6 +6523,7 @@ mod tests {
async fn proof_job_rejects_missing_remote_auth_before_invalid_stored_receipt_json() {
let _guard = test_guard();
let current_policy = recovered_result_missing_auth_policy();
+ set_remote_http_auth_env_missing(RECOVERED_RESULT_MISSING_AUTH_ENV);
let scenario = recovered_sp1_result_intent_scenario_with_stored_events(
¤t_policy,
Some("{"),