mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
78 lines
3.4 KiB
Go
78 lines
3.4 KiB
Go
package domain
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
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")
|
|
}
|
|
}
|
|
|
|
func TestRankedSeasonWindowIsExactlyTwelveWeeksAndDueIsIdempotent(t *testing.T) {
|
|
start := time.Unix(1000, 0)
|
|
season, err := NewRankedSeason("season-1", start)
|
|
if err != nil || season.EndsAt.Sub(start) != RankedSeasonLength {
|
|
t.Fatalf("season = %+v err=%v", season, err)
|
|
}
|
|
if SeasonRolloverDue(season, season.EndsAt.Add(-time.Nanosecond)) || !SeasonRolloverDue(season, season.EndsAt) {
|
|
t.Fatal("season due boundary is wrong")
|
|
}
|
|
season.RolledOverAt = season.EndsAt
|
|
if SeasonRolloverDue(season, season.EndsAt.Add(time.Hour)) {
|
|
t.Fatal("completed season remained due")
|
|
}
|
|
}
|