Migrate club rename and numeric squad reads

This commit is contained in:
funman300
2026-08-18 17:29:24 +00:00
parent fc55de19fa
commit bf6db98f0d
8 changed files with 645 additions and 136 deletions
+93 -14
View File
@@ -12,6 +12,7 @@ use openfut_adapter_fifa17::fut::catalog::Fifa17CardCatalog;
use openfut_adapter_fifa17::fut::club_response::{CoreOwnedItem, ItemIdentityResolver};
use openfut_adapter_fifa17::fut::entities::Fifa17Entities;
use openfut_identity::JsonIdentityStore;
use openfut_utas_host::account_store::AccountStore;
use openfut_utas_host::{
classify, handle_club, handle_put_squad, handle_squad_active, handle_squad_list,
handle_user_mass_info, read_request, ClubDeps, CoreAccess, CoreError, CoreExtState, CorePage,
@@ -302,9 +303,33 @@ fn resolver_for(cards: &[(&str, u32)]) -> Arc<Fifa17IdentityResolver> {
fn club_excludes_listed_items_and_paginates_the_visible_set() {
let core = Arc::new(FakeCore::new(
vec![
item("oc1", "card_a", 86, "CDM", "Argentina", "Premier League", "Chelsea"),
item("oc2", "card_b", 85, "ST", "Argentina", "Premier League", "Chelsea"),
item("oc3", "card_c", 84, "CB", "Argentina", "Premier League", "Chelsea"),
item(
"oc1",
"card_a",
86,
"CDM",
"Argentina",
"Premier League",
"Chelsea",
),
item(
"oc2",
"card_b",
85,
"ST",
"Argentina",
"Premier League",
"Chelsea",
),
item(
"oc3",
"card_c",
84,
"CB",
"Argentina",
"Premier League",
"Chelsea",
),
],
3,
));
@@ -502,21 +527,43 @@ fn passthrough_forwards_verbatim_and_never_calls_core() {
}
#[test]
fn mutating_route_classifies_to_passthrough_not_rust() {
// A PUT to the club PATH is NOT the read route; it must go to Python, never
// execute Rust/Core (guards against double-applying a mutation).
assert_eq!(classify("PUT", "/ut/game/fifa17/club"), Route::Passthrough);
fn club_rename_is_rust_owned_persistent_and_never_calls_python_or_core() {
assert_eq!(classify("PUT", "/ut/game/fifa17/club"), Route::ClubRename);
assert_eq!(
classify("PUT", "/ut/game/fifa17/user/club"),
Route::ClubRename
);
assert_eq!(
classify("POST", "/ut/game/fifa17/user/club"),
Route::ClubRename
);
assert_eq!(
classify("POST", "/ut/game/fifa17/squad/0"),
Route::Passthrough
);
let (upstream, _rec) = spawn_mock_python();
let (upstream, rec) = spawn_mock_python();
let core = Arc::new(FakeCore::forbidden());
let server = build_server(core.clone(), &upstream, None);
let resp = server.handle("PUT", "/ut/game/fifa17/club", &[], br#"{"x":1}"#);
let account_path = unique_store_path();
let account = Arc::new(AccountStore::open(&account_path));
let server = build_server(core.clone(), &upstream, None).with_account(account.clone());
let resp = server.handle(
"PUT",
"/ut/game/fifa17/user/club",
&[],
br#"{"clubName":"Real FUT","clubAbbr":"RF"}"#,
);
assert_eq!(resp.status, 200);
assert_eq!(
serde_json::from_slice::<Value>(&resp.body).unwrap(),
json!({})
);
assert_eq!(account.club().name, "Real FUT");
assert_eq!(account.club().abbr, "RF");
assert_eq!(AccountStore::open(&account_path).club(), account.club());
assert_eq!(core.calls(), 0);
assert_eq!(rec.lock().len(), 0, "rename never reached Python");
let _ = std::fs::remove_file(account_path);
}
// ── End-to-end over a socket (read_request + write_response + keep-alive) ─────
@@ -874,9 +921,8 @@ fn classify_squad_and_usermassinfo_routes() {
classify("GET", "/ut/game/fifa17/userMassInfo"),
Route::UserMassInfo
);
// GET /squad/active is now Core-backed (SquadActive). A squad PUT is never a
// GET. GET /squad/0 IS the active squad (id 0) -> SquadActive; a numeric
// GET /squad/<n> for a NON-active squad (n != 0) stays on Python.
// GET /squad/active and every numeric GET are the one Core-backed current
// squad, matching the oracle's single-squad response regardless of URL id.
assert_eq!(
classify("GET", "/ut/game/fifa17/squad/active"),
Route::SquadActive
@@ -891,7 +937,7 @@ fn classify_squad_and_usermassinfo_routes() {
);
assert_eq!(
classify("GET", "/ut/game/fifa17/squad/5"),
Route::Passthrough
Route::SquadActive
);
assert_eq!(
classify("PUT", "/ut/game/fifa17/squad/list"),
@@ -900,6 +946,39 @@ fn classify_squad_and_usermassinfo_routes() {
assert_eq!(classify("GET", "/ut/game/fifa17/club"), Route::Club);
}
#[test]
fn numbered_squad_get_returns_current_core_squad_without_python() {
let items = vec![gk(), st()];
let (resolver, wires) = resolver_with_wires(&items, ASSETS);
let core = Arc::new(FakeCore::new(items, 2));
let (python, recorded) = spawn_mock_python();
let server = Server::new(
core,
Arc::new(entities()),
Arc::new(resolver),
Arc::new(PassClient::new(&python)),
33_068_179,
);
let put = server.handle(
"PUT",
"/ut/game/fifa17/squad/0",
&[],
&put_body(
"f442",
wires["oc-a"],
&[(0, wires["oc-a"], 1), (1, wires["oc-b"], 9)],
"[1,2,3]",
),
);
assert_eq!(put.status, 200);
let active = server.handle("GET", "/ut/game/fifa17/squad/active", &[], b"");
let numbered = server.handle("GET", "/ut/game/fifa17/squad/5", &[], b"");
assert_eq!(numbered.status, 200);
assert_eq!(numbered.body, active.body);
assert_eq!(recorded.lock().len(), 0, "numeric GET never reached Python");
}
// ── PUT pipeline ────────────────────────────────────────────────────────────
#[test]