test(fifa17): harden SBC retail acceptance

This commit is contained in:
funman300
2026-08-18 19:01:33 +00:00
parent f9740f640d
commit e8d1c1ddac
8 changed files with 650 additions and 25 deletions
@@ -95,6 +95,7 @@
//! killed on guard drop. Never touches the production Core DB/ports or `.105`.
use openfut_adapter_fifa17::fut::catalog::Fifa17CardCatalog;
use openfut_adapter_fifa17::fut::club_response::ItemIdentityResolver;
use openfut_adapter_fifa17::fut::entities::Fifa17Entities;
use openfut_adapter_fifa17::fut::non_economy::PERSONA_DISPLAY_NAME;
use openfut_adapter_fifa17::fut::store_session::{SessionStore, StoreMode, SENTINEL_PACK_ID};
@@ -310,8 +311,12 @@ fn core_post(http: &reqwest::blocking::Client, base: &str, path: &str, body: Val
/// Build a real `Server` with economy authority wired against the seeded Core.
/// Returns the server, a direct Core client for balance/entitlement assertions,
/// and a valid wire `resourceId` (20000) that reverse-maps to a real Core card.
fn build_econ_server(base: &str, dir: &std::path::Path) -> (Server, HttpCoreClient, i64) {
/// the resolver used to obtain stable wire instance ids, and a valid wire
/// `resourceId` (20000) that reverse-maps to a real Core card.
fn build_econ_server(
base: &str,
dir: &std::path::Path,
) -> (Server, HttpCoreClient, Arc<Fifa17IdentityResolver>, i64) {
let probe = HttpCoreClient::new(base, "fifa17");
let owned = probe.all_owned().expect("core collection");
assert!(!owned.is_empty(), "seed must grant a starter collection");
@@ -374,7 +379,7 @@ fn build_econ_server(base: &str, dir: &std::path::Path) -> (Server, HttpCoreClie
PERSONA_ID,
)
.with_economy(services);
(server, probe, 20000)
(server, probe, resolver, 20000)
}
// ─────────────────────────── differential comparison ────────────────────────
@@ -433,7 +438,7 @@ fn json_shape(value: &Value) -> Value {
fn run_differential(core_base: &str, oracle: &Oracle, dir: &std::path::Path) {
wait_ready(core_base);
let http = reqwest::blocking::Client::new();
let (server, client, _sample_resource) = build_econ_server(core_base, dir);
let (server, client, _resolver, _sample_resource) = build_econ_server(core_base, dir);
// ── Fixture alignment: both sides own exactly one pack-70 entitlement. ──
// Oracle: fresh profile already owns pack 70. Core: grant the "70" entitlement
@@ -1264,13 +1269,14 @@ fn run_differential(core_base: &str, oracle: &Oracle, dir: &std::path::Path) {
/// Python acknowledges any body without consuming cards; Rust rejects an empty squad.
fn run_sbc_differential(core_base: &str, oracle: &Oracle, dir: &std::path::Path) {
wait_ready(core_base);
let (server, _client, _sample_resource) = build_econ_server(core_base, dir);
let (server, client, resolver, _sample_resource) = build_econ_server(core_base, dir);
let cases = [
("GET", "/ut/game/fifa17/sbs/sets", None),
("GET", "/ut/game/fifa17/sbs/setId/1/challenges", None),
("GET", "/ut/game/fifa17/sbs/setId/2/challenges", None),
("GET", "/ut/game/fifa17/sbs/setId/999/challenges", None),
("POST", "/ut/game/fifa17/sbs/sets/tag", Some(json!({}))),
("PUT", "/ut/game/fifa17/sbs/sets/tag", Some(json!({}))),
("POST", "/ut/game/fifa17/sbs/challenge/101", None),
("GET", "/ut/game/fifa17/sbs/challenge/101/squad", None),
(
@@ -1380,6 +1386,77 @@ fn run_sbc_differential(core_base: &str, oracle: &Oracle, dir: &std::path::Path)
rust_submit.0, 400,
"Rust must validate instead of copying the oracle's no-op acceptance"
);
let owned = client.all_owned().expect("SBC differential inventory");
let mut selected = Vec::new();
for nation in ["Argentina", "Brazil"] {
selected.push(
owned
.iter()
.find(|item| item.rating >= 70 && item.nation == nation)
.unwrap_or_else(|| panic!("missing {nation} SBC fixture")),
);
}
for item in &owned {
if selected.len() == 11 {
break;
}
if item.rating >= 70
&& !selected
.iter()
.any(|existing| existing.owned_card_id == item.owned_card_id)
{
selected.push(item);
}
}
assert_eq!(selected.len(), 11);
let successful = json!({
"squad": selected
.iter()
.enumerate()
.map(|(index, item)| json!({
"index": index,
"itemData": {
"id": i64::from(
resolver.resolve(item).expect("SBC wire identity").item_id
)
}
}))
.collect::<Vec<_>>()
});
let before_balance = client.balance().unwrap();
let before_packs = client.entitlements().unwrap().len();
let oracle_success = oracle.req(
"POST",
"/ut/game/fifa17/sbs/challenge/201",
Some(successful.clone()),
None,
);
let rust_success = rust(
&server,
"POST",
"/ut/game/fifa17/sbs/challenge/201",
&serde_json::to_vec(&successful).unwrap(),
None,
);
assert_eq!(oracle_success.0, 200);
assert_eq!(rust_success.0, 200);
assert_eq!(
json_shape(&rust_success.1),
json_shape(&oracle_success.1),
"successful Rust submit preserves the oracle response class"
);
assert_eq!(rust_success.1["challengeId"], 201);
assert_eq!(rust_success.1["setId"], 2);
assert!(
client.balance().unwrap() > before_balance,
"Core applies challenge and achievement coin rewards"
);
assert_eq!(
client.entitlements().unwrap().len(),
before_packs + 1,
"Core grants one Hybrid Nations pack"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]