ELO System Tests (71): - Calculator: expected scores, rating updates, K-factors - Doubles: effective opponent calculations - Score weights: per-point scoring - Integration: convergence, conservation, symmetry - Stress: extreme ratings, edge cases, floating point Edge Case Tests (18): - Special characters in names - Rating extremes (1 to 3000) - Score extremes (0-0, 11-0, overtime) - Empty database queries - SQL injection protection - Concurrent access - Session management Database Tests (6): - Player CRUD - Match recording - Leaderboard queries - Daily summaries Match Reversal Tests (4): - Singles match reversal - Doubles match reversal - Multiple match reversal (LIFO) - Partial reversal preserves other matches
626 lines
19 KiB
Rust
626 lines
19 KiB
Rust
//! Match reversal/deletion tests
|
|
//!
|
|
//! Ensures that deleting matches correctly reverts ratings
|
|
|
|
use sqlx::SqlitePool;
|
|
use sqlx::sqlite::SqlitePoolOptions;
|
|
|
|
async fn setup_test_db() -> SqlitePool {
|
|
let pool = SqlitePoolOptions::new()
|
|
.max_connections(1)
|
|
.connect("sqlite::memory:")
|
|
.await
|
|
.expect("Failed to create test database");
|
|
|
|
sqlx::query(r#"
|
|
CREATE TABLE players (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE,
|
|
email TEXT DEFAULT '',
|
|
singles_rating REAL DEFAULT 1500.0,
|
|
singles_rd REAL DEFAULT 350.0,
|
|
doubles_rating REAL DEFAULT 1500.0,
|
|
doubles_rd REAL DEFAULT 350.0,
|
|
created_at TEXT DEFAULT (datetime('now'))
|
|
)
|
|
"#).execute(&pool).await.unwrap();
|
|
|
|
sqlx::query(r#"
|
|
CREATE TABLE matches (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
match_type TEXT NOT NULL,
|
|
team1_score INTEGER NOT NULL,
|
|
team2_score INTEGER NOT NULL,
|
|
timestamp TEXT DEFAULT (datetime('now')),
|
|
session_id INTEGER
|
|
)
|
|
"#).execute(&pool).await.unwrap();
|
|
|
|
sqlx::query(r#"
|
|
CREATE TABLE match_participants (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
match_id INTEGER NOT NULL,
|
|
player_id INTEGER NOT NULL,
|
|
team INTEGER NOT NULL,
|
|
rating_before REAL,
|
|
rating_after REAL,
|
|
rating_change REAL,
|
|
FOREIGN KEY (match_id) REFERENCES matches(id),
|
|
FOREIGN KEY (player_id) REFERENCES players(id)
|
|
)
|
|
"#).execute(&pool).await.unwrap();
|
|
|
|
pool
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_singles_match_reversal() {
|
|
let pool = setup_test_db().await;
|
|
|
|
// Create two players
|
|
sqlx::query("INSERT INTO players (name, singles_rating) VALUES (?, ?)")
|
|
.bind("Reversal A")
|
|
.bind(1500.0)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
sqlx::query("INSERT INTO players (name, singles_rating) VALUES (?, ?)")
|
|
.bind("Reversal B")
|
|
.bind(1500.0)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let pa_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = ?")
|
|
.bind("Reversal A")
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let pb_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = ?")
|
|
.bind("Reversal B")
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Record match with rating changes
|
|
let match_result = sqlx::query(
|
|
"INSERT INTO matches (match_type, team1_score, team2_score) VALUES (?, ?, ?)"
|
|
)
|
|
.bind("singles")
|
|
.bind(11)
|
|
.bind(5)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let match_id = match_result.last_insert_rowid();
|
|
|
|
let rating_change_a = 10.0;
|
|
let rating_change_b = -10.0;
|
|
|
|
// Record participants with rating changes
|
|
sqlx::query(
|
|
"INSERT INTO match_participants (match_id, player_id, team, rating_before, rating_after, rating_change) VALUES (?, ?, ?, ?, ?, ?)"
|
|
)
|
|
.bind(match_id)
|
|
.bind(pa_id.0)
|
|
.bind(1)
|
|
.bind(1500.0)
|
|
.bind(1510.0)
|
|
.bind(rating_change_a)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
sqlx::query(
|
|
"INSERT INTO match_participants (match_id, player_id, team, rating_before, rating_after, rating_change) VALUES (?, ?, ?, ?, ?, ?)"
|
|
)
|
|
.bind(match_id)
|
|
.bind(pb_id.0)
|
|
.bind(2)
|
|
.bind(1500.0)
|
|
.bind(1490.0)
|
|
.bind(rating_change_b)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Apply rating changes
|
|
sqlx::query("UPDATE players SET singles_rating = singles_rating + ? WHERE id = ?")
|
|
.bind(rating_change_a)
|
|
.bind(pa_id.0)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
sqlx::query("UPDATE players SET singles_rating = singles_rating + ? WHERE id = ?")
|
|
.bind(rating_change_b)
|
|
.bind(pb_id.0)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Verify ratings changed
|
|
let rating_a: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
|
|
.bind(pa_id.0)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
assert!((rating_a.0 - 1510.0).abs() < 0.01);
|
|
|
|
let rating_b: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
|
|
.bind(pb_id.0)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
assert!((rating_b.0 - 1490.0).abs() < 0.01);
|
|
|
|
// NOW REVERSE THE MATCH
|
|
// Get rating changes to revert
|
|
let participants: Vec<(i64, f64)> = sqlx::query_as(
|
|
"SELECT player_id, rating_change FROM match_participants WHERE match_id = ?"
|
|
)
|
|
.bind(match_id)
|
|
.fetch_all(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Revert each player's rating
|
|
for (player_id, change) in participants {
|
|
sqlx::query("UPDATE players SET singles_rating = singles_rating - ? WHERE id = ?")
|
|
.bind(change)
|
|
.bind(player_id)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
// Delete match participants and match
|
|
sqlx::query("DELETE FROM match_participants WHERE match_id = ?")
|
|
.bind(match_id)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
sqlx::query("DELETE FROM matches WHERE id = ?")
|
|
.bind(match_id)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Verify ratings are back to original
|
|
let final_rating_a: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
|
|
.bind(pa_id.0)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
assert!((final_rating_a.0 - 1500.0).abs() < 0.01,
|
|
"Player A rating not reverted: {}", final_rating_a.0);
|
|
|
|
let final_rating_b: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
|
|
.bind(pb_id.0)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
assert!((final_rating_b.0 - 1500.0).abs() < 0.01,
|
|
"Player B rating not reverted: {}", final_rating_b.0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_doubles_match_reversal() {
|
|
let pool = setup_test_db().await;
|
|
|
|
// Create four players
|
|
let players = [
|
|
("D1", 1500.0), ("D2", 1520.0), ("D3", 1480.0), ("D4", 1500.0)
|
|
];
|
|
|
|
for (name, rating) in &players {
|
|
sqlx::query("INSERT INTO players (name, singles_rating) VALUES (?, ?)")
|
|
.bind(*name)
|
|
.bind(*rating)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
let d1_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = 'D1'")
|
|
.fetch_one(&pool).await.unwrap();
|
|
let d2_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = 'D2'")
|
|
.fetch_one(&pool).await.unwrap();
|
|
let d3_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = 'D3'")
|
|
.fetch_one(&pool).await.unwrap();
|
|
let d4_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = 'D4'")
|
|
.fetch_one(&pool).await.unwrap();
|
|
|
|
let d1_id = d1_id.0;
|
|
let d2_id = d2_id.0;
|
|
let d3_id = d3_id.0;
|
|
let d4_id = d4_id.0;
|
|
|
|
// Record doubles match
|
|
let match_result = sqlx::query(
|
|
"INSERT INTO matches (match_type, team1_score, team2_score) VALUES (?, ?, ?)"
|
|
)
|
|
.bind("doubles")
|
|
.bind(11)
|
|
.bind(8)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let match_id = match_result.last_insert_rowid();
|
|
|
|
// Team 1 (D1 + D2) wins, Team 2 (D3 + D4) loses
|
|
// Each player has different rating change due to effective opponent
|
|
let changes = [
|
|
(d1_id, 1, 8.0),
|
|
(d2_id, 1, 5.0), // Stronger player gains less
|
|
(d3_id, 2, -7.0),
|
|
(d4_id, 2, -6.0),
|
|
];
|
|
|
|
for (player_id, team, change) in &changes {
|
|
let before = match *team {
|
|
1 => if *player_id == d1_id { 1500.0 } else { 1520.0 },
|
|
_ => if *player_id == d3_id { 1480.0 } else { 1500.0 },
|
|
};
|
|
|
|
sqlx::query(
|
|
"INSERT INTO match_participants (match_id, player_id, team, rating_before, rating_after, rating_change) VALUES (?, ?, ?, ?, ?, ?)"
|
|
)
|
|
.bind(match_id)
|
|
.bind(player_id)
|
|
.bind(team)
|
|
.bind(before)
|
|
.bind(before + change)
|
|
.bind(change)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Apply rating change
|
|
sqlx::query("UPDATE players SET singles_rating = singles_rating + ? WHERE id = ?")
|
|
.bind(change)
|
|
.bind(player_id)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
// Store original ratings for comparison
|
|
let original_ratings: Vec<(i64, f64)> = vec![
|
|
(d1_id, 1500.0), (d2_id, 1520.0), (d3_id, 1480.0), (d4_id, 1500.0)
|
|
];
|
|
|
|
// REVERSE THE MATCH
|
|
let participants: Vec<(i64, f64)> = sqlx::query_as(
|
|
"SELECT player_id, rating_change FROM match_participants WHERE match_id = ?"
|
|
)
|
|
.bind(match_id)
|
|
.fetch_all(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
for (player_id, change) in participants {
|
|
sqlx::query("UPDATE players SET singles_rating = singles_rating - ? WHERE id = ?")
|
|
.bind(change)
|
|
.bind(player_id)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
sqlx::query("DELETE FROM match_participants WHERE match_id = ?")
|
|
.bind(match_id)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
sqlx::query("DELETE FROM matches WHERE id = ?")
|
|
.bind(match_id)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Verify all ratings reverted
|
|
for (player_id, original_rating) in original_ratings {
|
|
let final_rating: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
|
|
.bind(player_id)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert!((final_rating.0 - original_rating).abs() < 0.01,
|
|
"Player {} rating not reverted: {} (expected {})",
|
|
player_id, final_rating.0, original_rating);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_multiple_match_reversal() {
|
|
let pool = setup_test_db().await;
|
|
|
|
sqlx::query("INSERT INTO players (name, singles_rating) VALUES (?, ?)")
|
|
.bind("Multi A")
|
|
.bind(1500.0)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
sqlx::query("INSERT INTO players (name, singles_rating) VALUES (?, ?)")
|
|
.bind("Multi B")
|
|
.bind(1500.0)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let pa_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = ?")
|
|
.bind("Multi A")
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let pb_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = ?")
|
|
.bind("Multi B")
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Record 5 matches
|
|
let mut match_ids = vec![];
|
|
let mut changes_a = vec![];
|
|
let mut changes_b = vec![];
|
|
|
|
for i in 0..5 {
|
|
let score1 = 11;
|
|
let score2 = 5 + i;
|
|
let change_a = 10.0 - i as f64;
|
|
let change_b = -(10.0 - i as f64);
|
|
|
|
let match_result = sqlx::query(
|
|
"INSERT INTO matches (match_type, team1_score, team2_score) VALUES (?, ?, ?)"
|
|
)
|
|
.bind("singles")
|
|
.bind(score1)
|
|
.bind(score2)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let match_id = match_result.last_insert_rowid();
|
|
match_ids.push(match_id);
|
|
changes_a.push(change_a);
|
|
changes_b.push(change_b);
|
|
|
|
// Record participants
|
|
sqlx::query(
|
|
"INSERT INTO match_participants (match_id, player_id, team, rating_change) VALUES (?, ?, ?, ?)"
|
|
)
|
|
.bind(match_id)
|
|
.bind(pa_id.0)
|
|
.bind(1)
|
|
.bind(change_a)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
sqlx::query(
|
|
"INSERT INTO match_participants (match_id, player_id, team, rating_change) VALUES (?, ?, ?, ?)"
|
|
)
|
|
.bind(match_id)
|
|
.bind(pb_id.0)
|
|
.bind(2)
|
|
.bind(change_b)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Apply changes
|
|
sqlx::query("UPDATE players SET singles_rating = singles_rating + ? WHERE id = ?")
|
|
.bind(change_a)
|
|
.bind(pa_id.0)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
sqlx::query("UPDATE players SET singles_rating = singles_rating + ? WHERE id = ?")
|
|
.bind(change_b)
|
|
.bind(pb_id.0)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
// Verify ratings after 5 matches
|
|
// A gains: 10+9+8+7+6 = 40
|
|
// B loses: -40
|
|
let rating_a: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
|
|
.bind(pa_id.0)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
assert!((rating_a.0 - 1540.0).abs() < 0.01);
|
|
|
|
// Delete matches in REVERSE order (LIFO - last in, first out)
|
|
for match_id in match_ids.iter().rev() {
|
|
let participants: Vec<(i64, f64)> = sqlx::query_as(
|
|
"SELECT player_id, rating_change FROM match_participants WHERE match_id = ?"
|
|
)
|
|
.bind(match_id)
|
|
.fetch_all(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
for (player_id, change) in participants {
|
|
sqlx::query("UPDATE players SET singles_rating = singles_rating - ? WHERE id = ?")
|
|
.bind(change)
|
|
.bind(player_id)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
sqlx::query("DELETE FROM match_participants WHERE match_id = ?")
|
|
.bind(match_id)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
sqlx::query("DELETE FROM matches WHERE id = ?")
|
|
.bind(match_id)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
// Verify both players back to 1500
|
|
let final_a: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
|
|
.bind(pa_id.0)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
assert!((final_a.0 - 1500.0).abs() < 0.01);
|
|
|
|
let final_b: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
|
|
.bind(pb_id.0)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
assert!((final_b.0 - 1500.0).abs() < 0.01);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_reversal_preserves_other_matches() {
|
|
let pool = setup_test_db().await;
|
|
|
|
// Create 3 players: A plays both B and C
|
|
for (name, rating) in [("Preserve A", 1500.0), ("Preserve B", 1500.0), ("Preserve C", 1500.0)] {
|
|
sqlx::query("INSERT INTO players (name, singles_rating) VALUES (?, ?)")
|
|
.bind(name)
|
|
.bind(rating)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
let pa_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = ?")
|
|
.bind("Preserve A")
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let pb_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = ?")
|
|
.bind("Preserve B")
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
let pc_id: (i64,) = sqlx::query_as("SELECT id FROM players WHERE name = ?")
|
|
.bind("Preserve C")
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Match 1: A vs B (A wins)
|
|
let match1 = sqlx::query("INSERT INTO matches (match_type, team1_score, team2_score) VALUES (?, ?, ?)")
|
|
.bind("singles").bind(11).bind(5)
|
|
.execute(&pool).await.unwrap();
|
|
let match1_id = match1.last_insert_rowid();
|
|
|
|
sqlx::query("INSERT INTO match_participants (match_id, player_id, team, rating_change) VALUES (?, ?, ?, ?)")
|
|
.bind(match1_id).bind(pa_id.0).bind(1).bind(10.0)
|
|
.execute(&pool).await.unwrap();
|
|
sqlx::query("INSERT INTO match_participants (match_id, player_id, team, rating_change) VALUES (?, ?, ?, ?)")
|
|
.bind(match1_id).bind(pb_id.0).bind(2).bind(-10.0)
|
|
.execute(&pool).await.unwrap();
|
|
|
|
sqlx::query("UPDATE players SET singles_rating = singles_rating + 10 WHERE id = ?")
|
|
.bind(pa_id.0).execute(&pool).await.unwrap();
|
|
sqlx::query("UPDATE players SET singles_rating = singles_rating - 10 WHERE id = ?")
|
|
.bind(pb_id.0).execute(&pool).await.unwrap();
|
|
|
|
// Match 2: A vs C (A wins)
|
|
let match2 = sqlx::query("INSERT INTO matches (match_type, team1_score, team2_score) VALUES (?, ?, ?)")
|
|
.bind("singles").bind(11).bind(7)
|
|
.execute(&pool).await.unwrap();
|
|
let match2_id = match2.last_insert_rowid();
|
|
|
|
sqlx::query("INSERT INTO match_participants (match_id, player_id, team, rating_change) VALUES (?, ?, ?, ?)")
|
|
.bind(match2_id).bind(pa_id.0).bind(1).bind(8.0)
|
|
.execute(&pool).await.unwrap();
|
|
sqlx::query("INSERT INTO match_participants (match_id, player_id, team, rating_change) VALUES (?, ?, ?, ?)")
|
|
.bind(match2_id).bind(pc_id.0).bind(2).bind(-8.0)
|
|
.execute(&pool).await.unwrap();
|
|
|
|
sqlx::query("UPDATE players SET singles_rating = singles_rating + 8 WHERE id = ?")
|
|
.bind(pa_id.0).execute(&pool).await.unwrap();
|
|
sqlx::query("UPDATE players SET singles_rating = singles_rating - 8 WHERE id = ?")
|
|
.bind(pc_id.0).execute(&pool).await.unwrap();
|
|
|
|
// After both matches: A=1518, B=1490, C=1492
|
|
|
|
// Delete ONLY match 1 (A vs B)
|
|
let participants: Vec<(i64, f64)> = sqlx::query_as(
|
|
"SELECT player_id, rating_change FROM match_participants WHERE match_id = ?"
|
|
)
|
|
.bind(match1_id)
|
|
.fetch_all(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
for (player_id, change) in participants {
|
|
sqlx::query("UPDATE players SET singles_rating = singles_rating - ? WHERE id = ?")
|
|
.bind(change)
|
|
.bind(player_id)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
sqlx::query("DELETE FROM match_participants WHERE match_id = ?")
|
|
.bind(match1_id)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
sqlx::query("DELETE FROM matches WHERE id = ?")
|
|
.bind(match1_id)
|
|
.execute(&pool)
|
|
.await
|
|
.unwrap();
|
|
|
|
// After reverting match 1: A=1508, B=1500, C=1492
|
|
// Match 2's effect on A and C should be preserved
|
|
|
|
let final_a: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
|
|
.bind(pa_id.0)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
assert!((final_a.0 - 1508.0).abs() < 0.01, "A rating wrong: {}", final_a.0);
|
|
|
|
let final_b: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
|
|
.bind(pb_id.0)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
assert!((final_b.0 - 1500.0).abs() < 0.01, "B rating wrong: {}", final_b.0);
|
|
|
|
let final_c: (f64,) = sqlx::query_as("SELECT singles_rating FROM players WHERE id = ?")
|
|
.bind(pc_id.0)
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
assert!((final_c.0 - 1492.0).abs() < 0.01, "C rating wrong: {}", final_c.0);
|
|
|
|
// Match 2 should still exist
|
|
let match_count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM matches")
|
|
.fetch_one(&pool)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(match_count.0, 1);
|
|
}
|