test(fifa17): harden SBC retail acceptance
This commit is contained in:
@@ -3,6 +3,18 @@
|
||||
//! collide with the live oracle). `core_url` defaults to Bridge's convention.
|
||||
|
||||
use std::env;
|
||||
const SBC_FAULT_ACK: &str = "staging-only-sbc-receipt-loss";
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum SbcPostCommitFault {
|
||||
#[default]
|
||||
Off,
|
||||
Drop,
|
||||
Malformed,
|
||||
Delay {
|
||||
millis: u64,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HostConfig {
|
||||
@@ -44,6 +56,9 @@ pub struct HostConfig {
|
||||
/// `OPENFUT_ACCOUNT_PATH`, then existing `FUT_ACCOUNT_PATH`, then the
|
||||
/// identity store's parent directory + `active_account.json`.
|
||||
pub account_path: String,
|
||||
/// Disabled by default. Non-off values require three explicit staging guards;
|
||||
/// see [`parse_sbc_post_commit_fault`].
|
||||
pub sbc_post_commit_fault: SbcPostCommitFault,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -76,9 +91,68 @@ fn required_i64_nonzero(key: &str) -> Result<i64, ConfigError> {
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn parse_sbc_post_commit_fault(
|
||||
raw: Option<&str>,
|
||||
environment: Option<&str>,
|
||||
ack: Option<&str>,
|
||||
listen_addr: &str,
|
||||
) -> Result<SbcPostCommitFault, ConfigError> {
|
||||
let mode = match raw.filter(|value| !value.is_empty()).unwrap_or("off") {
|
||||
"off" => return Ok(SbcPostCommitFault::Off),
|
||||
"drop" => SbcPostCommitFault::Drop,
|
||||
"malformed" => SbcPostCommitFault::Malformed,
|
||||
value if value.starts_with("delay:") => {
|
||||
let millis = value["delay:".len()..].parse::<u64>().map_err(|_| {
|
||||
ConfigError(
|
||||
"OPENFUT_FIFA17_SBC_POST_COMMIT_FAULT delay must be delay:<milliseconds>"
|
||||
.into(),
|
||||
)
|
||||
})?;
|
||||
if !(1..=30_000).contains(&millis) {
|
||||
return Err(ConfigError(
|
||||
"OPENFUT_FIFA17_SBC_POST_COMMIT_FAULT delay must be 1..=30000 ms".into(),
|
||||
));
|
||||
}
|
||||
SbcPostCommitFault::Delay { millis }
|
||||
}
|
||||
value => {
|
||||
return Err(ConfigError(format!(
|
||||
"OPENFUT_FIFA17_SBC_POST_COMMIT_FAULT must be off, drop, malformed, or delay:<milliseconds>; got {value:?}"
|
||||
)));
|
||||
}
|
||||
};
|
||||
if environment != Some("staging") {
|
||||
return Err(ConfigError(
|
||||
"SBC post-commit faults require OPENFUT_ENVIRONMENT=staging".into(),
|
||||
));
|
||||
}
|
||||
if ack != Some(SBC_FAULT_ACK) {
|
||||
return Err(ConfigError(format!(
|
||||
"SBC post-commit faults require OPENFUT_FIFA17_SBC_POST_COMMIT_FAULT_ACK={SBC_FAULT_ACK}"
|
||||
)));
|
||||
}
|
||||
if listen_addr.rsplit_once(':').map(|(_, port)| port) == Some("8099") {
|
||||
return Err(ConfigError(
|
||||
"SBC post-commit faults refuse the production UTAS port 8099".into(),
|
||||
));
|
||||
}
|
||||
Ok(mode)
|
||||
}
|
||||
|
||||
impl HostConfig {
|
||||
pub fn from_env() -> Result<Self, ConfigError> {
|
||||
let identity_store_path = required("OPENFUT_IDENTITY_STORE")?;
|
||||
let listen_addr = required("OPENFUT_UTAS_HOST_ADDR")?;
|
||||
let sbc_post_commit_fault = parse_sbc_post_commit_fault(
|
||||
env::var("OPENFUT_FIFA17_SBC_POST_COMMIT_FAULT")
|
||||
.ok()
|
||||
.as_deref(),
|
||||
env::var("OPENFUT_ENVIRONMENT").ok().as_deref(),
|
||||
env::var("OPENFUT_FIFA17_SBC_POST_COMMIT_FAULT_ACK")
|
||||
.ok()
|
||||
.as_deref(),
|
||||
&listen_addr,
|
||||
)?;
|
||||
let clientdata_path = env::var("OPENFUT_CLIENTDATA_DB")
|
||||
.ok()
|
||||
.filter(|v| !v.is_empty())
|
||||
@@ -89,7 +163,7 @@ impl HostConfig {
|
||||
.or_else(|| env::var("FUT_ACCOUNT_PATH").ok().filter(|v| !v.is_empty()))
|
||||
.unwrap_or_else(|| default_account_path(&identity_store_path));
|
||||
Ok(HostConfig {
|
||||
listen_addr: required("OPENFUT_UTAS_HOST_ADDR")?,
|
||||
listen_addr,
|
||||
python_upstream: required("OPENFUT_UTAS_PYTHON_URL")?,
|
||||
core_url: env::var("OPENFUT_CORE_URL")
|
||||
.unwrap_or_else(|_| "http://127.0.0.1:8080".into()),
|
||||
@@ -102,6 +176,7 @@ impl HostConfig {
|
||||
identity_store_path,
|
||||
clientdata_path,
|
||||
account_path,
|
||||
sbc_post_commit_fault,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -125,3 +200,57 @@ fn default_account_path(identity_store_path: &str) -> String {
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sbc_post_commit_faults_require_all_staging_guards() {
|
||||
assert_eq!(
|
||||
parse_sbc_post_commit_fault(None, None, None, "0.0.0.0:8099").unwrap(),
|
||||
SbcPostCommitFault::Off
|
||||
);
|
||||
assert!(parse_sbc_post_commit_fault(
|
||||
Some("drop"),
|
||||
None,
|
||||
Some(SBC_FAULT_ACK),
|
||||
"127.0.0.1:18199"
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_sbc_post_commit_fault(
|
||||
Some("drop"),
|
||||
Some("staging"),
|
||||
None,
|
||||
"127.0.0.1:18199"
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_sbc_post_commit_fault(
|
||||
Some("drop"),
|
||||
Some("staging"),
|
||||
Some(SBC_FAULT_ACK),
|
||||
"0.0.0.0:8099"
|
||||
)
|
||||
.is_err());
|
||||
assert_eq!(
|
||||
parse_sbc_post_commit_fault(
|
||||
Some("drop"),
|
||||
Some("staging"),
|
||||
Some(SBC_FAULT_ACK),
|
||||
"0.0.0.0:18199"
|
||||
)
|
||||
.unwrap(),
|
||||
SbcPostCommitFault::Drop
|
||||
);
|
||||
assert_eq!(
|
||||
parse_sbc_post_commit_fault(
|
||||
Some("delay:25"),
|
||||
Some("staging"),
|
||||
Some(SBC_FAULT_ACK),
|
||||
"0.0.0.0:18199"
|
||||
)
|
||||
.unwrap(),
|
||||
SbcPostCommitFault::Delay { millis: 25 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user