Refactor series handling in backend to include player context in Toss, Ban, Pick, and Record actions. Update service methods to accept player parameters, enhancing authorization checks and ensuring proper team actions. Modify integration tests to reflect new method signatures and improve tournament hydration logic in the store. Enhance frontend components to support new series features and improve user experience with live match navigation.
This commit is contained in:
@@ -135,20 +135,20 @@ func TestFullScrimPipeline(t *testing.T) {
|
||||
t.Fatalf("start failed: %#v, %v", started, err)
|
||||
}
|
||||
seriesIDs = append(seriesIDs, started.Series.ID)
|
||||
series, err := service.TossSeriesCoin(ctx, admin, started.Series.ID, "integration-seed", started.Series.Version)
|
||||
series, err := service.TossSeriesCoin(ctx, admin, domain.Player{}, started.Series.ID, "integration-seed", started.Series.TeamAID, started.Series.Version)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for mapNumber := 0; mapNumber < 2; mapNumber++ {
|
||||
if series.Phase == domain.MapPickPhase {
|
||||
series, err = service.PickSeriesMap(ctx, admin, series.ID, series.NextMapPickerID, series.AvailableMaps[0], series.Version)
|
||||
series, err = service.PickSeriesMap(ctx, admin, domain.Player{}, series.ID, series.NextMapPickerID, series.AvailableMaps[0], series.Version)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
for series.Phase != domain.HeroBanPhase {
|
||||
name := firstUnbanned(series.MapDraft.Pool, series.MapDraft.Banned)
|
||||
series, err = service.BanSeriesMap(ctx, admin, series.ID, series.MapDraft.NextTeam(), name, series.Version)
|
||||
series, err = service.BanSeriesMap(ctx, admin, domain.Player{}, series.ID, series.MapDraft.NextTeam(), name, series.Version)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -156,12 +156,12 @@ func TestFullScrimPipeline(t *testing.T) {
|
||||
for series.Phase != domain.PlayingPhase {
|
||||
teamID := series.HeroDraft.NextTeam()
|
||||
hero := firstAvailableHero(series.HeroDraft, teamID)
|
||||
series, err = service.BanSeriesHero(ctx, admin, series.ID, teamID, hero, series.Version)
|
||||
series, err = service.BanSeriesHero(ctx, admin, domain.Player{}, series.ID, teamID, hero, series.Version)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
series, err = service.RecordSeriesResult(ctx, admin, series.ID, domain.TeamAWin, series.Version)
|
||||
series, err = service.RecordSeriesResult(ctx, admin, domain.Player{}, series.ID, series.TeamAID, domain.TeamAWin, series.Version)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -175,6 +175,97 @@ func TestFullScrimPipeline(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParallelTournamentHydratesLiveSeriesAndFinal(t *testing.T) {
|
||||
databaseURL := os.Getenv("TEST_DATABASE_URL")
|
||||
if databaseURL == "" {
|
||||
t.Skip("TEST_DATABASE_URL is not configured")
|
||||
}
|
||||
ctx := context.Background()
|
||||
if err := Migrate(ctx, databaseURL, "../../../migrations"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store, err := Open(ctx, databaseURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer store.Close()
|
||||
suffix := fmt.Sprintf("%d", time.Now().UnixNano())
|
||||
accountID, eventID, tournamentID := "parallel-admin-"+suffix, "parallel-event-"+suffix, "parallel-tournament-"+suffix
|
||||
_, err = store.pool.Exec(ctx, `INSERT INTO accounts(id,discord_id,username,avatar_url,role,created_at) VALUES($1,$1,'Parallel Admin','','admin',now())`, accountID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
_, _ = store.pool.Exec(ctx, `DELETE FROM series WHERE tournament_id=$1`, tournamentID)
|
||||
_, _ = store.pool.Exec(ctx, `DELETE FROM events WHERE id=$1`, eventID)
|
||||
_, _ = store.pool.Exec(ctx, `DELETE FROM accounts WHERE id=$1`, accountID)
|
||||
}()
|
||||
service := application.New(store, discardPublisher{})
|
||||
event, err := service.CreateEvent(ctx, domain.Account{ID: accountID, Role: domain.RoleAdmin}, domain.Event{
|
||||
ID: eventID, Name: "Parallel tournament", StartsAt: time.Now().Add(time.Hour), EndsAt: time.Now().Add(3 * time.Hour),
|
||||
RegistrationDeadline: time.Now(), RulesetID: "standard-control-hybrid-control",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
eventID = event.ID
|
||||
rules, err := store.GetRuleset(ctx, event.RulesetID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tournament, err := domain.NewTournament(tournamentID, eventID, event.Name, []string{"team-a", "team-b", "team-c", "team-d"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err = store.SaveTournament(ctx, *tournament); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for match := range tournament.Rounds[0] {
|
||||
slot := tournament.Rounds[0][match]
|
||||
series, createErr := domain.NewSeries(slot.ID, eventID, tournamentID, [2]string{slot.TeamAID, slot.TeamBID}, rules)
|
||||
if createErr != nil {
|
||||
t.Fatal(createErr)
|
||||
}
|
||||
if match == 0 {
|
||||
if createErr = series.Toss("parallel-seed", accountID, time.Now().UTC(), rules); createErr != nil {
|
||||
t.Fatal(createErr)
|
||||
}
|
||||
}
|
||||
if _, createErr = store.SaveSeries(ctx, *series); createErr != nil {
|
||||
t.Fatal(createErr)
|
||||
}
|
||||
}
|
||||
hydrated, err := store.GetTournamentByEvent(ctx, eventID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if hydrated.Rounds[0][0].Phase != domain.MapBanPhase || hydrated.Rounds[0][1].Phase != domain.CoinTossPending {
|
||||
t.Fatalf("parallel series were not hydrated independently: %s / %s", hydrated.Rounds[0][0].Phase, hydrated.Rounds[0][1].Phase)
|
||||
}
|
||||
for match := range hydrated.Rounds[0] {
|
||||
hydrated.Rounds[0][match].WinnerTeamID = hydrated.Rounds[0][match].TeamAID
|
||||
hydrated.Rounds[0][match].Phase = domain.SeriesComplete
|
||||
if err = hydrated.Advance(0, match); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if _, err = store.SaveTournament(ctx, hydrated); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
finalSlot := hydrated.Rounds[1][0]
|
||||
final, err := domain.NewSeries(finalSlot.ID, eventID, tournamentID, [2]string{finalSlot.TeamAID, finalSlot.TeamBID}, rules)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err = store.SaveSeries(ctx, *final); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hydrated, err = store.GetTournamentByEvent(ctx, eventID)
|
||||
if err != nil || hydrated.Rounds[1][0].Phase != domain.CoinTossPending {
|
||||
t.Fatalf("final was not persisted and hydrated: phase=%s err=%v", hydrated.Rounds[1][0].Phase, err)
|
||||
}
|
||||
}
|
||||
|
||||
func firstUnbanned(pool, banned []string) string {
|
||||
for _, name := range pool {
|
||||
found := false
|
||||
|
||||
Reference in New Issue
Block a user