Phase 7 (Core): chemistry v2, expanded card pool & search filters
CI / Build, lint & test (push) Failing after 1m46s
CI / Build, lint & test (push) Failing after 1m46s
- Chemistry v2: FUT-style link scoring with per-player breakdown (club +3/link cap 6, league +1/link cap 4, nation +1/link cap 3; player cap 10, team cap 100); chemistry response now includes per-player breakdown with club/league/nation link counts and pts - Card pool: 34 new cards across Premier League, La Liga, Bundesliga with overlapping clubs/nations for meaningful chemistry testing - Card search: extended GET /cards with nation, league, club, min_overall, max_overall, limit query params; results sorted by overall descending - Match opponent: added "ultimate" difficulty band (85+ OVR); random formation selection from 6 tactical formations; falls back to lower OVR pool when not enough cards at the requested band - Tests: 9 new integration tests (28 total, all passing) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -482,3 +482,154 @@ async fn test_event_market_refresh_injects_totw_cards() {
|
||||
"expected at least one [EVENT] market listing"
|
||||
);
|
||||
}
|
||||
|
||||
// ── Phase 7: Extended card search & chemistry ────────────────────────────────
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cards_filter_by_league() {
|
||||
let app = build_test_app().await;
|
||||
let (status, json) = json_get(&app, "/cards?league=Premier+League").await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
let cards = json["cards"].as_array().expect("cards array");
|
||||
assert!(!cards.is_empty(), "Premier League cards should be loaded");
|
||||
for card in cards {
|
||||
assert_eq!(
|
||||
card["league"].as_str().unwrap().to_lowercase(),
|
||||
"premier league"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cards_filter_by_nation() {
|
||||
let app = build_test_app().await;
|
||||
let (status, json) = json_get(&app, "/cards?nation=Spain").await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
let cards = json["cards"].as_array().expect("cards array");
|
||||
assert!(!cards.is_empty(), "Spanish cards should exist in La Liga data");
|
||||
for card in cards {
|
||||
assert_eq!(
|
||||
card["nation"].as_str().unwrap().to_lowercase(),
|
||||
"spain"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cards_filter_by_min_overall() {
|
||||
let app = build_test_app().await;
|
||||
let (status, json) = json_get(&app, "/cards?min_overall=84").await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
let cards = json["cards"].as_array().expect("cards array");
|
||||
assert!(!cards.is_empty());
|
||||
for card in cards {
|
||||
assert!(
|
||||
card["overall"].as_u64().unwrap() >= 84,
|
||||
"card below min_overall threshold"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cards_filter_by_club() {
|
||||
let app = build_test_app().await;
|
||||
let (status, json) = json_get(&app, "/cards?club=Northgate+United").await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
let cards = json["cards"].as_array().expect("cards array");
|
||||
assert!(!cards.is_empty(), "Northgate United cards expected");
|
||||
for card in cards {
|
||||
assert_eq!(
|
||||
card["club"].as_str().unwrap().to_lowercase(),
|
||||
"northgate united"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cards_filter_combined_params() {
|
||||
let app = build_test_app().await;
|
||||
// La Liga cards with overall >= 82 — should include several
|
||||
let (status, json) = json_get(&app, "/cards?league=La+Liga&min_overall=82").await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
let cards = json["cards"].as_array().expect("cards array");
|
||||
assert!(!cards.is_empty(), "combined filter should return results");
|
||||
for card in cards {
|
||||
assert_eq!(card["league"].as_str().unwrap().to_lowercase(), "la liga");
|
||||
assert!(card["overall"].as_u64().unwrap() >= 82);
|
||||
}
|
||||
// total reflects untruncated count, returned reflects actual
|
||||
assert_eq!(json["total"], json["returned"]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cards_limit_parameter() {
|
||||
let app = build_test_app().await;
|
||||
let (status, json) = json_get(&app, "/cards?limit=3").await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
let returned = json["returned"].as_u64().unwrap();
|
||||
let total = json["total"].as_u64().unwrap();
|
||||
assert!(returned <= 3);
|
||||
assert!(total >= returned, "total should not be less than returned");
|
||||
// Cards should be sorted by overall descending
|
||||
let cards = json["cards"].as_array().unwrap();
|
||||
for w in cards.windows(2) {
|
||||
assert!(
|
||||
w[0]["overall"].as_u64().unwrap() >= w[1]["overall"].as_u64().unwrap(),
|
||||
"cards should be sorted by overall descending"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cards_max_overall_boundary() {
|
||||
let app = build_test_app().await;
|
||||
let (status, json) = json_get(&app, "/cards?max_overall=70").await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
let cards = json["cards"].as_array().expect("cards array");
|
||||
// We don't require results (there may be none), just verify none exceed limit
|
||||
for card in cards {
|
||||
assert!(
|
||||
card["overall"].as_u64().unwrap() <= 70,
|
||||
"card above max_overall threshold"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_draft_squad_ultimate_difficulty() {
|
||||
let app = build_test_app().await;
|
||||
auth(&app, "UltimateDrafter").await;
|
||||
|
||||
let (status, json) = json_get(&app, "/draft/squad?difficulty=ultimate").await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
assert_eq!(json["difficulty"], "ultimate");
|
||||
assert!(json["cards"].is_array());
|
||||
// Should fall back to lower band if not enough 90+ cards — just verify 11 or fewer cards
|
||||
let card_count = json["cards"].as_array().unwrap().len();
|
||||
assert!(card_count <= 11);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_chemistry_response_has_breakdown_structure() {
|
||||
let app = build_test_app().await;
|
||||
auth(&app, "ChemPlayer").await;
|
||||
|
||||
// Create an empty squad (no players)
|
||||
let (s, j) = json_post(
|
||||
&app,
|
||||
"/squad",
|
||||
serde_json::json!({ "name": "ChemSquad", "formation": "4-3-3", "players": [] }),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(s, StatusCode::OK, "{j}");
|
||||
let squad_id = j["squad"]["id"].as_str().unwrap().to_string();
|
||||
|
||||
let (status, json) = json_get(&app, &format!("/squads/{squad_id}")).await;
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
// Chemistry block should always be present
|
||||
let chem = &json["chemistry"];
|
||||
assert!(chem.is_object(), "chemistry should be an object");
|
||||
assert_eq!(chem["total"], 0, "empty squad has 0 chemistry");
|
||||
assert_eq!(chem["max"], 100);
|
||||
assert!(chem["player_chemistries"].is_array());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user