package domain import ( "testing" "time" ) func sixConnectParticipants(connected ...int) []ConnectParticipant { set := make(map[int]bool) for _, index := range connected { set[index] = true } result := make([]ConnectParticipant, 6) for i := range result { result[i] = ConnectParticipant{PlayerID: string(rune('a' + i)), Team: i / 3, Slot: i, Connected: set[i]} } return result } func TestRankedInitialNoShowCancelsWithoutRatingPenalty(t *testing.T) { readyAt := time.Unix(1000, 0) decision, err := EvaluateInitialConnect(Ranked, readyAt, readyAt.Add(InitialConnectWindow), sixConnectParticipants(0, 1, 2, 3, 4), map[string][]time.Time{"f": {readyAt.Add(-time.Hour)}}) if err != nil || decision.Action != InitialConnectCancel || len(decision.NoShows) != 1 || decision.NoShows[0].PlayerID != "f" || decision.NoShows[0].Cooldown != 15*time.Minute || len(decision.Innocent) != 5 { t.Fatalf("ranked no-show decision = %+v err=%v", decision, err) } } func TestCompleteRosterStartsImmediatelyForEitherPlaylist(t *testing.T) { readyAt := time.Unix(1000, 0) participants := sixConnectParticipants(0, 1, 2, 3, 4, 5) for _, playlist := range []Playlist{Ranked, Casual} { plan, err := PlanInitialConnect(playlist, readyAt, readyAt.Add(time.Second), participants, nil) if err != nil || plan.Action != InitialConnectStart || plan.MatchState != Live || len(plan.Connected) != 6 || len(plan.NoShows) != 0 || len(plan.CasualLineup) != 0 { t.Fatalf("%s complete-roster plan = %+v err=%v", playlist, plan, err) } } } func TestCompleteRelaxedCasualRosterStartsImmediatelyWithBots(t *testing.T) { readyAt := time.Unix(1000, 0) participants := []ConnectParticipant{{PlayerID: "a", Team: 0, Slot: 0, Connected: true}, {PlayerID: "d", Team: 1, Slot: 3, Connected: true}} plan, err := PlanInitialConnect(Casual, readyAt, readyAt.Add(time.Second), participants, nil) if err != nil || plan.Action != InitialConnectStartWithBot || plan.MatchState != Live || len(plan.Connected) != 2 || len(plan.NoShows) != 0 || len(plan.CasualLineup) != 6 { t.Fatalf("relaxed casual plan = %+v err=%v", plan, err) } } func TestInitialConnectRejectsMalformedRosterBeforeStarting(t *testing.T) { readyAt := time.Unix(1000, 0) if _, err := EvaluateInitialConnect(Ranked, readyAt, readyAt, sixConnectParticipants(0, 1, 2, 3, 4)[:5], nil); err == nil { t.Fatal("five-player ranked roster accepted") } duplicate := sixConnectParticipants(0, 1, 2, 3, 4, 5) duplicate[5].PlayerID = duplicate[0].PlayerID if _, err := EvaluateInitialConnect(Casual, readyAt, readyAt, duplicate, nil); err == nil { t.Fatal("duplicate player accepted") } } func TestCasualWaitsThenStartsWithBotsOnlyWithHumanOnEachTeam(t *testing.T) { readyAt := time.Unix(1000, 0) participants := sixConnectParticipants(0, 3) if decision, err := EvaluateInitialConnect(Casual, readyAt, readyAt.Add(45*time.Second), participants, nil); err != nil || decision.Action != InitialConnectWait { t.Fatalf("casual early decision = %+v err=%v", decision, err) } decision, err := EvaluateInitialConnect(Casual, readyAt, readyAt.Add(CasualBotStartAfter), participants, nil) if err != nil || decision.Action != InitialConnectStartWithBot || len(decision.NoShows) != 4 || decision.NoShows[0].Cooldown != CasualNoShowCooldown { t.Fatalf("casual bot decision = %+v err=%v", decision, err) } noTeam := sixConnectParticipants(0) decision, err = EvaluateInitialConnect(Casual, readyAt, readyAt.Add(CasualBotStartAfter), noTeam, nil) if err != nil || decision.Action != InitialConnectCancel || len(decision.Innocent) != 1 { t.Fatalf("empty-team decision = %+v err=%v", decision, err) } } func TestPlanInitialConnectMakesLifecycleActionExplicit(t *testing.T) { readyAt := time.Unix(1000, 0) participants := sixConnectParticipants(0, 3) plan, err := PlanInitialConnect(Casual, readyAt, readyAt.Add(CasualBotStartAfter), participants, nil) if err != nil || plan.Action != InitialConnectStartWithBot || plan.MatchState != Live || len(plan.CasualLineup) != 6 || len(plan.NoShows) != 4 { t.Fatalf("casual initial-connect plan = %+v err=%v", plan, err) } botCount := 0 for _, slot := range plan.CasualLineup { if slot.IsBot { botCount++ } } if botCount != 4 { t.Fatalf("casual plan bot count = %d, want 4", botCount) } ranked, err := PlanInitialConnect(Ranked, readyAt, readyAt.Add(InitialConnectWindow), sixConnectParticipants(0, 1, 2, 3, 4), nil) if err != nil || ranked.Action != InitialConnectCancel || ranked.MatchState != Cancelled || len(ranked.CasualLineup) != 0 || len(ranked.Connected) != 5 { t.Fatalf("ranked initial-connect plan = %+v err=%v", ranked, err) } }