//! Integration tests for `Outcome::Scored` routing through `History::add_events`. use smallvec::smallvec; use trueskill_tt::{ConstantDrift, Event, History, Member, Outcome, Team}; #[test] fn scored_two_team_one_event_pulls_winner_up() { let mut h: History = History::builder() .mu(0.0) .sigma(2.0) .beta(1.0) .drift(ConstantDrift(0.0)) .score_sigma(1.0) .build(); let events: Vec> = vec![Event { time: 1, teams: smallvec![ Team::with_members([Member::new("a")]), Team::with_members([Member::new("b")]), ], outcome: Outcome::scores([10.0, 4.0]), }]; h.add_events(events).unwrap(); let mu_a = h.current_skill(&"a").unwrap().mu(); let mu_b = h.current_skill(&"b").unwrap().mu(); assert!( mu_a > 0.0, "winner mu should be pulled up; got mu_a = {mu_a}" ); assert!( mu_b < 0.0, "loser mu should be pulled down; got mu_b = {mu_b}" ); assert!( mu_a > mu_b, "winner mu should exceed loser mu; got mu_a = {mu_a}, mu_b = {mu_b}" ); } #[test] fn scored_zero_margin_treats_as_tie() { let mut h: History = History::builder() .mu(0.0) .sigma(2.0) .beta(1.0) .drift(ConstantDrift(0.0)) .score_sigma(1.0) .build(); let events: Vec> = vec![Event { time: 1, teams: smallvec![ Team::with_members([Member::new("a")]), Team::with_members([Member::new("b")]), ], outcome: Outcome::scores([5.0, 5.0]), }]; h.add_events(events).unwrap(); let mu_a = h.current_skill(&"a").unwrap().mu(); let mu_b = h.current_skill(&"b").unwrap().mu(); let sigma_a = h.current_skill(&"a").unwrap().sigma(); // Equal scores: posterior means stay symmetric around the prior mean. assert!( (mu_a - mu_b).abs() < 1e-9, "equal scores should leave mu_a == mu_b; got {mu_a} vs {mu_b}" ); assert!( mu_a.abs() < 1e-9, "equal scores against equal priors should leave mu near zero; got {mu_a}" ); // A zero-margin scored event still reduces uncertainty. assert!( sigma_a < 2.0, "expected sigma to tighten below prior 2.0; got {}", sigma_a ); } #[test] fn scored_three_team_partial_order() { let mut h: History = History::builder() .mu(0.0) .sigma(2.0) .beta(1.0) .drift(ConstantDrift(0.0)) .score_sigma(1.0) .build(); let events: Vec> = vec![Event { time: 1, teams: smallvec![ Team::with_members([Member::new("a")]), Team::with_members([Member::new("b")]), Team::with_members([Member::new("c")]), ], outcome: Outcome::scores([9.0, 5.0, 1.0]), }]; h.add_events(events).unwrap(); let mu_a = h.current_skill(&"a").unwrap().mu(); let mu_b = h.current_skill(&"b").unwrap().mu(); let mu_c = h.current_skill(&"c").unwrap().mu(); assert!( mu_a > mu_b, "team with highest score should rank highest; mu_a = {mu_a}, mu_b = {mu_b}" ); assert!( mu_b > mu_c, "middle score should outrank lowest; mu_b = {mu_b}, mu_c = {mu_c}" ); } #[test] fn scored_rejects_outcome_team_count_mismatch() { use trueskill_tt::InferenceError; let mut h: History = History::builder().build(); let events: Vec> = vec![Event { time: 1, teams: smallvec![ Team::with_members([Member::new("a")]), Team::with_members([Member::new("b")]), ], outcome: Outcome::scores([10.0, 4.0, 1.0]), // 3 scores, 2 teams }]; let err = h.add_events(events).unwrap_err(); assert!( matches!(err, InferenceError::MismatchedShape { .. }), "expected MismatchedShape error, got {err:?}" ); }