Files
OpenFUT-Core/tests/dev_seed_test.rs
T

182 lines
6.2 KiB
Rust

//! FIFA 17 development content pack + ownership seed (Commit 5).
//!
//! Proves: the dev pack is opt-in and isolated from default content; the seed
//! creates a `game_id=fifa17` profile/club and grants real Core `OwnedCard`s
//! (never FIFA wire ids); it is idempotent and leaves the default profile alone;
//! and the seeded inventory can exercise the retail `/club` filter + pagination.
use openfut_core::db::Pool;
use openfut_core::services::card_db::CardDb;
async fn pool() -> Pool {
let pool = sqlx::sqlite::SqlitePoolOptions::new()
.max_connections(1)
.connect("sqlite::memory:")
.await
.expect("in-memory sqlite");
sqlx::migrate!("./migrations")
.run(&pool)
.await
.expect("migrations");
pool
}
fn dev_card_db() -> CardDb {
let mut db = CardDb::load("data").expect("default cards");
db.load_game_dev("data", "fifa17").expect("dev pack");
db
}
// ── Content isolation ────────────────────────────────────────────────────────
#[test]
fn default_load_never_contains_dev_pack() {
// The default global loader reads only data/cards — the dev pack under
// data/games/fifa17/dev must be invisible unless explicitly requested.
let default = CardDb::load("data").expect("default cards");
let leaked: Vec<_> = default
.cards
.keys()
.filter(|k| k.starts_with("fifa17_"))
.collect();
assert!(
leaked.is_empty(),
"default content must not include FIFA17 dev cards: {leaked:?}"
);
assert!(
!default.cards.is_empty(),
"default synthetic catalogue still loads"
);
}
#[test]
fn opt_in_load_adds_dev_pack_only() {
let default_n = CardDb::load("data").unwrap().cards.len();
let db = dev_card_db();
let dev: Vec<_> = db
.cards
.keys()
.filter(|k| k.starts_with("fifa17_"))
.collect();
assert_eq!(dev.len(), 32, "the curated dev pack is 32 definitions");
assert_eq!(
db.cards.len(),
default_n + 32,
"dev pack is additive; default content unchanged"
);
}
#[test]
fn dev_definitions_carry_semantic_names_not_raw_ids() {
let db = dev_card_db();
for c in db.cards.values().filter(|c| c.id.starts_with("fifa17_")) {
// Semantic Core fields are names, never raw FIFA numeric entity ids.
assert!(
c.nation.parse::<i64>().is_err(),
"nation must be a name, got {:?}",
c.nation
);
assert!(
c.league.parse::<i64>().is_err(),
"league must be a name: {:?}",
c.league
);
assert!(
c.club.parse::<i64>().is_err(),
"club must be a name: {:?}",
c.club
);
assert!(!c.name.is_empty(), "every dev card has a player name");
}
}
// ── Ownership seed ─────────────────────────────────────────────────────────
#[tokio::test]
async fn seed_grants_game_scoped_inventory_with_filter_coverage() {
let pool = pool().await;
let db = dev_card_db();
let r = openfut_core::seed::seed_fifa17_dev(&pool, &db)
.await
.unwrap();
assert_eq!(r.game_id, "fifa17");
assert!(!r.already_seeded);
assert_eq!(r.definitions_available, 32);
assert_eq!(r.owned_total, 33, "32 defs + 1 deliberate duplicate");
assert_eq!(r.unique_definitions, 32);
assert!(r.gold_over_one_page, "gold spans >1 page (22 > 11)");
assert!(r.gold > 11, "enough gold for pagination");
assert!(r.silver >= 1 && r.bronze >= 1, "quality spread");
assert!(r.positions.contains_key("GK"), "GK present");
assert!(r.positions.contains_key("ST"), "ST present");
assert!(r.distinct_leagues >= 2, "multiple leagues");
assert!(r.distinct_nations >= 2, "multiple nations");
assert!(r.max_same_club >= 2, "a same-club group for team filters");
// The seed created ONLY a fifa17 profile — the default (fifa23) profile and
// any synthetic inventory are untouched.
let games: Vec<(String,)> = sqlx::query_as("SELECT game_id FROM profiles")
.fetch_all(&pool)
.await
.unwrap();
assert_eq!(
games,
vec![("fifa17".to_string(),)],
"only the fifa17 profile exists"
);
// Every seeded owned card references a dev-pack definition (all renderable).
let orphans: i64 = sqlx::query_scalar(
"SELECT COUNT(*) FROM owned_cards o \
WHERE o.card_id LIKE 'fifa17_%' AND o.card_id NOT IN \
(SELECT card_id FROM owned_cards WHERE card_id LIKE 'fifa17_%')",
)
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(orphans, 0);
}
#[tokio::test]
async fn seed_is_idempotent_across_reruns() {
let pool = pool().await;
let db = dev_card_db();
let first = openfut_core::seed::seed_fifa17_dev(&pool, &db)
.await
.unwrap();
let second = openfut_core::seed::seed_fifa17_dev(&pool, &db)
.await
.unwrap();
assert!(!first.already_seeded);
assert!(second.already_seeded, "second run sees existing ownership");
assert_eq!(first.owned_total, second.owned_total, "no duplicate grants");
let n: i64 =
sqlx::query_scalar("SELECT COUNT(*) FROM owned_cards WHERE card_id LIKE 'fifa17_%'")
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(n, 33, "row count stable after rerun");
}
#[tokio::test]
async fn seed_creates_exactly_one_two_copy_definition() {
let pool = pool().await;
let db = dev_card_db();
openfut_core::seed::seed_fifa17_dev(&pool, &db)
.await
.unwrap();
// Exactly one definition is owned twice (distinct owned ids, same card_id):
// the identity foundation for "two copies of one card" later.
let dupes: Vec<(String, i64)> = sqlx::query_as(
"SELECT card_id, COUNT(*) c FROM owned_cards WHERE card_id LIKE 'fifa17_%' \
GROUP BY card_id HAVING c > 1",
)
.fetch_all(&pool)
.await
.unwrap();
assert_eq!(dupes.len(), 1, "exactly one duplicated definition");
assert_eq!(dupes[0].1, 2, "owned twice");
}