//! Content preflight: a real profile with owned players but a missing //! CardDefinition must fail startup LOUDLY, never serve a silent empty club. use axum::{ body::Body, http::{Request, StatusCode}, }; use openfut_core::services::card_db::CardDb; use tower::ServiceExt; async fn fresh_pool() -> sqlx::SqlitePool { let p = sqlx::sqlite::SqlitePoolOptions::new() .max_connections(1) .connect("sqlite::memory:") .await .expect("in-memory sqlite"); sqlx::migrate!("./migrations") .run(&p) .await .expect("migrations"); p } async fn create_profile(app: &axum::Router) { let resp = app .clone() .oneshot( Request::builder() .method("POST") .uri("/auth/local") .header("content-type", "application/json") .body(Body::from(r#"{"username":"CAGE"}"#)) .unwrap(), ) .await .unwrap(); assert_eq!( resp.status(), StatusCode::OK, "auth/local should create a profile+club" ); } async fn insert_owned(pool: &sqlx::SqlitePool, id: &str, club_id: &str, card_id: &str) { sqlx::query( "INSERT INTO owned_cards (id, club_id, card_id, is_loan, loan_matches_remaining, acquired_at) \ VALUES (?, ?, ?, 0, NULL, ?)", ) .bind(id) .bind(club_id) .bind(card_id) .bind("2026-01-01T00:00:00Z") .execute(pool) .await .unwrap(); } #[tokio::test] async fn preflight_fails_on_owned_card_missing_definition() { let pool = fresh_pool().await; // first build is fine: no owned cards yet. let app = openfut_core::build_app(pool.clone(), "data").await.unwrap(); create_profile(&app).await; let club: String = sqlx::query_scalar("SELECT id FROM clubs LIMIT 1") .fetch_one(&pool) .await .unwrap(); insert_owned(&pool, "oc-bogus", &club, "fifa17_definitely_missing_999999").await; // second build must now fail preflight: one owned card references a def that // is not loaded — must not silently serve an empty collection. let err = openfut_core::build_app(pool.clone(), "data") .await .expect_err("preflight must fail on a missing definition"); let msg = format!("{err:#}"); assert!( msg.contains("content preflight failed"), "unexpected error: {msg}" ); } #[tokio::test] async fn preflight_passes_when_owned_card_definition_is_loaded() { let pool = fresh_pool().await; // pick a definition that IS in the default data/cards catalog. let valid_id = CardDb::load("data") .unwrap() .all() .first() .map(|c| c.id.clone()) .expect("data/cards must be non-empty"); let app = openfut_core::build_app(pool.clone(), "data").await.unwrap(); create_profile(&app).await; let club: String = sqlx::query_scalar("SELECT id FROM clubs LIMIT 1") .fetch_one(&pool) .await .unwrap(); insert_owned(&pool, "oc-valid", &club, &valid_id).await; let _app = openfut_core::build_app(pool.clone(), "data") .await .expect("preflight passes when the owned card's definition is loaded"); }