mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-16 00:32:21 +00:00
feat: add ranked season rollover policy
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
package domain
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRankedProvisionalBoundaryIsFirstTenGames(t *testing.T) {
|
||||
for games := 0; games < 10; games++ {
|
||||
if !RankedIsProvisional(RankedProfile{RankedGames: games}) {
|
||||
t.Fatalf("game %d should be provisional", games)
|
||||
}
|
||||
}
|
||||
if RankedIsProvisional(RankedProfile{RankedGames: 10}) {
|
||||
t.Fatal("game ten should be fully ranked")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeasonRolloverCompressesRatingAndPreservesHistory(t *testing.T) {
|
||||
profile := RankedProfile{Rating: Rating{Value: 1900, RD: 100, Volatility: 0.12}, RankedGames: 25, SeasonHistory: []string{"season-0"}}
|
||||
updated, applied, err := ApplySeasonRollover(profile, "season-1")
|
||||
if err != nil || !applied {
|
||||
t.Fatalf("rollover failed: %+v applied=%v err=%v", updated, applied, err)
|
||||
}
|
||||
if updated.Value != 1800 || updated.RD != 200 || updated.Volatility != profile.Volatility || updated.RankedGames != profile.RankedGames {
|
||||
t.Fatalf("rollover changed wrong fields: %+v", updated)
|
||||
}
|
||||
if len(updated.SeasonHistory) != 2 || updated.SeasonHistory[0] != "season-0" || updated.SeasonHistory[1] != "season-1" {
|
||||
t.Fatalf("history not preserved: %+v", updated.SeasonHistory)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeasonRolloverIsExactlyOnceAndCapsRD(t *testing.T) {
|
||||
profile := RankedProfile{Rating: Rating{Value: 1200, RD: 350, Volatility: 0.06}, RankedGames: 4}
|
||||
updated, applied, err := ApplySeasonRollover(profile, "season-1")
|
||||
if err != nil || !applied || updated.Value != 1275 || updated.RD != 350 {
|
||||
t.Fatalf("first rollover wrong: %+v applied=%v err=%v", updated, applied, err)
|
||||
}
|
||||
replay, applied, err := ApplySeasonRollover(updated, "season-1")
|
||||
if err != nil || applied || replay.Value != updated.Value || replay.RD != updated.RD || len(replay.SeasonHistory) != 1 {
|
||||
t.Fatalf("duplicate rollover was not inert: %+v applied=%v err=%v", replay, applied, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeasonRolloverRejectsInvalidProfileAndReplaysAnyRecordedSeason(t *testing.T) {
|
||||
if _, _, err := ApplySeasonRollover(RankedProfile{RankedGames: -1, Rating: Rating{Value: 1500, RD: 350, Volatility: 0.06}}, "season-1"); err == nil {
|
||||
t.Fatal("negative ranked games should be rejected")
|
||||
}
|
||||
profile := RankedProfile{Rating: Rating{Value: 1600, RD: 250, Volatility: 0.06}, SeasonHistory: []string{"season-1", "season-2"}}
|
||||
updated, applied, err := ApplySeasonRollover(profile, "season-1")
|
||||
if err != nil || applied || updated.Value != profile.Value || updated.RD != profile.RD {
|
||||
t.Fatalf("recorded season replay was not inert: %+v applied=%v err=%v", updated, applied, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCasualRatingHasNoSeasonOperation(t *testing.T) {
|
||||
// The API accepts only RankedProfile, making casual season reset impossible
|
||||
// without an explicit type/compile-time boundary violation.
|
||||
if RankedIsProvisional(RankedProfile{RankedGames: 10}) {
|
||||
t.Fatal("casual boundary test fixture unexpectedly provisional")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user