Files
CosmicClash/server/domain/season_test.go
T
2026-08-31 21:08:10 +01:00

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")
}
}