Fix: rating update
This commit is contained in:
@ -14,18 +14,99 @@ func setupPlayersTestDB(t *testing.T) (*sqlx.DB, func()) {
|
||||
db, err := sqlx.Connect("sqlite3", ":memory:")
|
||||
assert.NoError(t, err)
|
||||
|
||||
schema := `
|
||||
CREATE TABLE IF NOT EXISTS players (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
room_id TEXT,
|
||||
username TEXT,
|
||||
password TEXT NOT NULL DEFAULT '',
|
||||
team TEXT,
|
||||
role TEXT,
|
||||
is_bot BOOLEAN
|
||||
);
|
||||
// Load schema from migration files
|
||||
schema001 := `
|
||||
-- migrations/001_initial_schema.up.sql
|
||||
|
||||
CREATE TABLE player_stats (
|
||||
CREATE TABLE rooms (
|
||||
id TEXT PRIMARY KEY,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
creator_name TEXT NOT NULL,
|
||||
team_turn TEXT NOT NULL DEFAULT '',
|
||||
this_turn_limit INTEGER NOT NULL DEFAULT 0,
|
||||
opened_this_turn INTEGER NOT NULL DEFAULT 0,
|
||||
blue_counter INTEGER NOT NULL DEFAULT 0,
|
||||
red_counter INTEGER NOT NULL DEFAULT 0,
|
||||
red_turn BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
mime_done BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
is_running BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
is_over BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
team_won TEXT NOT NULL DEFAULT '',
|
||||
room_link TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE TABLE players (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
room_id TEXT, -- nullable
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password TEXT NOT NULL DEFAULT '',
|
||||
team TEXT NOT NULL DEFAULT '', -- 'red' or 'blue'
|
||||
role TEXT NOT NULL DEFAULT '', -- 'guesser' or 'mime'
|
||||
is_bot BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE word_cards (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
room_id TEXT NOT NULL,
|
||||
word TEXT NOT NULL,
|
||||
color TEXT NOT NULL DEFAULT '',
|
||||
revealed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
mime_view BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE card_marks (
|
||||
card_id INTEGER NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
FOREIGN KEY (card_id) REFERENCES word_cards(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (username) REFERENCES players(username) ON DELETE CASCADE,
|
||||
PRIMARY KEY (card_id, username)
|
||||
);
|
||||
|
||||
CREATE TABLE actions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
room_id TEXT NOT NULL,
|
||||
actor TEXT NOT NULL,
|
||||
actor_color TEXT NOT NULL DEFAULT '',
|
||||
action_type TEXT NOT NULL,
|
||||
word TEXT NOT NULL DEFAULT '',
|
||||
word_color TEXT NOT NULL DEFAULT '',
|
||||
number_associated TEXT NOT NULL DEFAULT '', -- for clues
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE settings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
room_id TEXT NOT NULL,
|
||||
language TEXT NOT NULL DEFAULT 'en',
|
||||
room_pass TEXT NOT NULL DEFAULT '',
|
||||
turn_time INTEGER NOT NULL DEFAULT 60, -- seconds
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE sessions(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
lifetime INTEGER NOT NULL DEFAULT 3600,
|
||||
token_key TEXT NOT NULL DEFAULT '' UNIQUE, -- encoded value
|
||||
username TEXT NOT NULL,
|
||||
FOREIGN KEY (username) REFERENCES players(username) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE journal(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
entry TEXT NOT NULL DEFAULT '',
|
||||
username TEXT NOT NULL,
|
||||
room_id TEXT NOT NULL,
|
||||
FOREIGN KEY (username) REFERENCES players(username) ON DELETE CASCADE,
|
||||
FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE player_stats (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
games_played INTEGER NOT NULL DEFAULT 0,
|
||||
@ -40,9 +121,32 @@ func setupPlayersTestDB(t *testing.T) (*sqlx.DB, func()) {
|
||||
played_as_guesser INTEGER NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY (username) REFERENCES players(username) ON DELETE CASCADE
|
||||
);
|
||||
`
|
||||
_, err = db.Exec(schema001)
|
||||
assert.NoError(t, err)
|
||||
|
||||
`
|
||||
_, err = db.Exec(schema)
|
||||
schema002 := `
|
||||
ALTER TABLE player_stats
|
||||
ADD COLUMN rating REAL NOT NULL DEFAULT 1000.0;
|
||||
|
||||
CREATE TRIGGER update_player_rating
|
||||
AFTER UPDATE OF games_played, games_won ON player_stats
|
||||
WHEN NEW.games_played = OLD.games_played + 1
|
||||
BEGIN
|
||||
UPDATE player_stats
|
||||
SET rating = OLD.rating +
|
||||
32.0 * (
|
||||
CASE
|
||||
WHEN NEW.games_won = OLD.games_won + 1
|
||||
THEN 1.0 - 0.5 -- Win term: 0.5
|
||||
ELSE 0.0 - 0.5 -- Loss term: -0.5
|
||||
END
|
||||
) +
|
||||
0.05 * (1000.0 - OLD.rating)
|
||||
WHERE id = OLD.id;
|
||||
END;
|
||||
`
|
||||
_, err = db.Exec(schema002)
|
||||
assert.NoError(t, err)
|
||||
|
||||
return db, func() {
|
||||
@ -50,6 +154,39 @@ func setupPlayersTestDB(t *testing.T) (*sqlx.DB, func()) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlayerStatsRatingUpdate(t *testing.T) {
|
||||
db, teardown := setupPlayersTestDB(t)
|
||||
defer teardown()
|
||||
|
||||
username := "test_player_rating"
|
||||
_, err := db.Exec(`INSERT INTO players (username) VALUES (?)`, username)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = db.Exec(`INSERT INTO player_stats (username, games_played, games_won, rating) VALUES (?, 0, 0, 1000.0)`, username)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Simulate a win
|
||||
_, err = db.Exec(`UPDATE player_stats SET games_played = 1, games_won = 1 WHERE username = ?`, username)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var ratingAfterWin float64
|
||||
err = db.Get(&ratingAfterWin, `SELECT rating FROM player_stats WHERE username = ?`, username)
|
||||
assert.NoError(t, err)
|
||||
// Expected: 1000 + 32 * (1 - 0.5) + 0.05 * (1000 - 1000) = 1000 + 16 = 1016
|
||||
assert.InDelta(t, 1016.0, ratingAfterWin, 0.001)
|
||||
|
||||
// Simulate a loss
|
||||
_, err = db.Exec(`UPDATE player_stats SET games_played = 2, games_won = 1 WHERE username = ?`, username)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var ratingAfterLoss float64
|
||||
err = db.Get(&ratingAfterLoss, `SELECT rating FROM player_stats WHERE username = ?`, username)
|
||||
assert.NoError(t, err)
|
||||
// Expected: 1016 + 32 * (0 - 0.5) + 0.05 * (1000 - 1016) = 1016 - 16 + 0.05 * (-16) = 1000 - 0.8 = 999.2
|
||||
assert.InDelta(t, 999.2, ratingAfterLoss, 0.001)
|
||||
}
|
||||
|
||||
|
||||
func TestPlayersRepo_AddPlayer(t *testing.T) {
|
||||
db, teardown := setupPlayersTestDB(t)
|
||||
defer teardown()
|
||||
|
Reference in New Issue
Block a user