code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/teams_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package sqlstore_test 17 18 import ( 19 "encoding/json" 20 "fmt" 21 "math/rand" 22 "sort" 23 "strings" 24 "testing" 25 "time" 26 27 "code.vegaprotocol.io/vega/datanode/entities" 28 "code.vegaprotocol.io/vega/datanode/sqlstore" 29 vgcrypto "code.vegaprotocol.io/vega/libs/crypto" 30 "code.vegaprotocol.io/vega/libs/num" 31 "code.vegaprotocol.io/vega/libs/ptr" 32 vegapb "code.vegaprotocol.io/vega/protos/vega" 33 eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" 34 35 "github.com/georgysavva/scany/pgxscan" 36 "github.com/shopspring/decimal" 37 "github.com/stretchr/testify/assert" 38 "github.com/stretchr/testify/require" 39 "golang.org/x/exp/slices" 40 ) 41 42 func TestTeams_AddTeams(t *testing.T) { 43 bs, ts, ps := setupTeamsTest(t) 44 ctx := tempTransaction(t) 45 46 block := addTestBlock(t, ctx, bs) 47 referrer := addTestParty(t, ctx, ps, block) 48 49 team := entities.Team{ 50 ID: entities.TeamID(GenerateID()), 51 Referrer: referrer.ID, 52 Name: "Test Team", 53 TeamURL: nil, 54 AvatarURL: nil, 55 CreatedAt: block.VegaTime, 56 CreatedAtEpoch: 1, 57 VegaTime: block.VegaTime, 58 Closed: true, 59 AllowList: []string{GenerateID(), GenerateID()}, 60 } 61 62 t.Run("Should add a new if it does not already exist", func(t *testing.T) { 63 err := ts.AddTeam(ctx, &team) 64 65 require.NoError(t, err) 66 67 var teamFromDB entities.Team 68 err = pgxscan.Get(ctx, connectionSource, &teamFromDB, `SELECT * FROM teams WHERE id=$1`, team.ID) 69 require.NoError(t, err) 70 require.Equal(t, team, teamFromDB) 71 }) 72 t.Run("Should error if team already exists", func(t *testing.T) { 73 err := ts.AddTeam(ctx, &team) 74 75 require.Error(t, err) 76 assert.Contains(t, err.Error(), "duplicate key value violates unique constraint") 77 }) 78 } 79 80 func TestTeams_UpdateTeam(t *testing.T) { 81 bs, ts, ps := setupTeamsTest(t) 82 ctx := tempTransaction(t) 83 84 block := addTestBlock(t, ctx, bs) 85 referrer := addTestParty(t, ctx, ps, block) 86 87 team := entities.Team{ 88 ID: entities.TeamID(GenerateID()), 89 Referrer: referrer.ID, 90 Name: "Test Team", 91 TeamURL: nil, 92 AvatarURL: nil, 93 CreatedAt: block.VegaTime, 94 VegaTime: block.VegaTime, 95 Closed: true, 96 AllowList: []string{GenerateID(), GenerateID()}, 97 } 98 99 err := ts.AddTeam(ctx, &team) 100 require.NoError(t, err) 101 102 t.Run("Should update a team if it exists", func(t *testing.T) { 103 nextBlock := addTestBlock(t, ctx, bs) 104 105 updateTeam := entities.TeamUpdated{ 106 ID: team.ID, 107 Name: team.Name, 108 TeamURL: ptr.From("https://surely-you-cant-be-serio.us"), 109 AvatarURL: ptr.From("https://dont-call-me-shirl.ee"), 110 VegaTime: nextBlock.VegaTime, 111 Closed: true, 112 AllowList: []string{GenerateID(), GenerateID()}, 113 } 114 115 err := ts.UpdateTeam(ctx, &updateTeam) 116 require.NoError(t, err) 117 118 want := entities.Team{ 119 ID: team.ID, 120 Referrer: team.Referrer, 121 Name: team.Name, 122 TeamURL: updateTeam.TeamURL, 123 AvatarURL: updateTeam.AvatarURL, 124 Closed: updateTeam.Closed, 125 AllowList: updateTeam.AllowList, 126 CreatedAt: team.CreatedAt, 127 VegaTime: team.VegaTime, 128 } 129 130 var got entities.Team 131 132 err = pgxscan.Get(ctx, connectionSource, &got, `SELECT * FROM teams WHERE id=$1`, team.ID) 133 require.NoError(t, err) 134 135 assert.Equal(t, want, got) 136 }) 137 138 t.Run("Should error if team does not exist", func(t *testing.T) { 139 nextBlock := addTestBlock(t, ctx, bs) 140 141 updateTeam := entities.TeamUpdated{ 142 ID: entities.TeamID(GenerateID()), 143 Name: team.Name, 144 TeamURL: ptr.From("https://surely-you-cant-be-serio.us"), 145 AvatarURL: ptr.From("https://dont-call-me-shirl.ee"), 146 Closed: false, 147 VegaTime: nextBlock.VegaTime, 148 } 149 150 err := ts.UpdateTeam(ctx, &updateTeam) 151 require.Error(t, err) 152 }) 153 } 154 155 func TestTeams_RefereeJoinedTeam(t *testing.T) { 156 t.Run("Should add a new referee for the team", testTeamsShouldAddReferee) 157 t.Run("Should show joined team as current team", testTeamsShouldShowJoinedTeamAsCurrentTeam) 158 } 159 160 func testTeamsShouldAddReferee(t *testing.T) { 161 bs, ts, ps := setupTeamsTest(t) 162 ctx := tempTransaction(t) 163 164 block := addTestBlock(t, ctx, bs) 165 referrer := addTestParty(t, ctx, ps, block) 166 167 team := entities.Team{ 168 ID: entities.TeamID(GenerateID()), 169 Referrer: referrer.ID, 170 Name: "Test Team", 171 TeamURL: nil, 172 AvatarURL: nil, 173 CreatedAt: block.VegaTime, 174 VegaTime: block.VegaTime, 175 } 176 177 require.NoError(t, ts.AddTeam(ctx, &team)) 178 179 referee := addTestParty(t, ctx, ps, block) 180 181 joinEvent := &eventspb.RefereeJoinedTeam{ 182 TeamId: team.ID.String(), 183 Referee: referee.ID.String(), 184 JoinedAt: block.VegaTime.UnixNano(), 185 } 186 187 teamReferee := entities.TeamRefereeFromProto(joinEvent, block.VegaTime) 188 assert.NoError(t, ts.RefereeJoinedTeam(ctx, teamReferee)) 189 190 var got entities.TeamMember 191 require.NoError(t, pgxscan.Get(ctx, connectionSource, &got, `SELECT * FROM team_members WHERE team_id=$1 AND party_id=$2`, team.ID, referee.ID)) 192 assert.Equal(t, teamReferee, &got) 193 } 194 195 func testTeamsShouldShowJoinedTeamAsCurrentTeam(t *testing.T) { 196 bs, ts, ps := setupTeamsTest(t) 197 ctx := tempTransaction(t) 198 199 block := addTestBlock(t, ctx, bs) 200 referrer1 := addTestParty(t, ctx, ps, block) 201 referrer2 := addTestParty(t, ctx, ps, block) 202 203 team1 := entities.Team{ 204 ID: entities.TeamID(GenerateID()), 205 Referrer: referrer1.ID, 206 Name: "Test Team 1", 207 TeamURL: nil, 208 AvatarURL: nil, 209 CreatedAt: block.VegaTime, 210 CreatedAtEpoch: 1, 211 VegaTime: block.VegaTime, 212 } 213 require.NoError(t, ts.AddTeam(ctx, &team1)) 214 215 team2 := entities.Team{ 216 ID: entities.TeamID(GenerateID()), 217 Referrer: referrer2.ID, 218 Name: "Test Team 2", 219 TeamURL: nil, 220 AvatarURL: nil, 221 CreatedAt: block.VegaTime, 222 CreatedAtEpoch: 1, 223 VegaTime: block.VegaTime, 224 } 225 require.NoError(t, ts.AddTeam(ctx, &team2)) 226 227 referee1 := addTestParty(t, ctx, ps, block) 228 229 joinEvent1 := &eventspb.RefereeJoinedTeam{ 230 TeamId: team1.ID.String(), 231 Referee: referee1.ID.String(), 232 JoinedAt: block.VegaTime.UnixNano(), 233 AtEpoch: 2, 234 } 235 assert.NoError(t, ts.RefereeJoinedTeam(ctx, entities.TeamRefereeFromProto(joinEvent1, block.VegaTime))) 236 237 var got1 entities.TeamMember 238 require.NoError(t, pgxscan.Get(ctx, connectionSource, &got1, `SELECT * FROM current_team_members WHERE party_id=$1`, referee1.ID)) 239 assert.Equal(t, team1.ID, (&got1).TeamID) 240 241 referee2 := addTestParty(t, ctx, ps, block) 242 243 joinEvent2 := &eventspb.RefereeJoinedTeam{ 244 TeamId: team2.ID.String(), 245 Referee: referee2.ID.String(), 246 JoinedAt: block.VegaTime.UnixNano(), 247 AtEpoch: 3, 248 } 249 assert.NoError(t, ts.RefereeJoinedTeam(ctx, entities.TeamRefereeFromProto(joinEvent2, block.VegaTime))) 250 251 var got2 entities.TeamMember 252 require.NoError(t, pgxscan.Get(ctx, connectionSource, &got2, `SELECT * FROM current_team_members WHERE party_id=$1`, referee2.ID)) 253 assert.Equal(t, team2.ID, (&got2).TeamID) 254 } 255 256 func TestTeams_RefereeSwitchedTeam(t *testing.T) { 257 t.Run("Should show last joined team as current team", testTeamsShouldShowLastJoinedTeamAsCurrentTeam) 258 } 259 260 func testTeamsShouldShowLastJoinedTeamAsCurrentTeam(t *testing.T) { 261 bs, ts, ps := setupTeamsTest(t) 262 ctx := tempTransaction(t) 263 264 block := addTestBlock(t, ctx, bs) 265 referrer1 := addTestParty(t, ctx, ps, block) 266 referrer2 := addTestParty(t, ctx, ps, block) 267 268 team1 := entities.Team{ 269 ID: entities.TeamID(GenerateID()), 270 Referrer: referrer1.ID, 271 Name: "Test Team 1", 272 TeamURL: nil, 273 AvatarURL: nil, 274 CreatedAt: block.VegaTime, 275 CreatedAtEpoch: 1, 276 VegaTime: block.VegaTime, 277 } 278 require.NoError(t, ts.AddTeam(ctx, &team1)) 279 280 team2 := entities.Team{ 281 ID: entities.TeamID(GenerateID()), 282 Referrer: referrer2.ID, 283 Name: "Test Team 2", 284 TeamURL: nil, 285 AvatarURL: nil, 286 CreatedAt: block.VegaTime, 287 CreatedAtEpoch: 1, 288 VegaTime: block.VegaTime, 289 } 290 require.NoError(t, ts.AddTeam(ctx, &team2)) 291 292 referee := addTestParty(t, ctx, ps, block) 293 294 joinEvent1 := &eventspb.RefereeJoinedTeam{ 295 TeamId: team1.ID.String(), 296 Referee: referee.ID.String(), 297 JoinedAt: block.VegaTime.UnixNano(), 298 AtEpoch: 2, 299 } 300 assert.NoError(t, ts.RefereeJoinedTeam(ctx, entities.TeamRefereeFromProto(joinEvent1, block.VegaTime))) 301 302 var got1 entities.TeamMember 303 require.NoError(t, pgxscan.Get(ctx, connectionSource, &got1, `SELECT * FROM current_team_members WHERE party_id=$1`, referee.ID)) 304 assert.Equal(t, team1.ID, (&got1).TeamID) 305 306 joinEvent2 := &eventspb.RefereeJoinedTeam{ 307 TeamId: team2.ID.String(), 308 Referee: referee.ID.String(), 309 JoinedAt: block.VegaTime.UnixNano(), 310 AtEpoch: 3, 311 } 312 assert.NoError(t, ts.RefereeJoinedTeam(ctx, entities.TeamRefereeFromProto(joinEvent2, block.VegaTime))) 313 314 var got2 entities.TeamMember 315 require.NoError(t, pgxscan.Get(ctx, connectionSource, &got2, `SELECT * FROM current_team_members WHERE party_id=$1`, referee.ID)) 316 assert.Equal(t, team2.ID, (&got2).TeamID) 317 } 318 319 func TestTeams_GetTeams(t *testing.T) { 320 t.Run("Should return a team if the team ID is provided", testShouldReturnTeamIfTeamIDProvided) 321 t.Run("Should return a team if a referrer party ID is provided", testShouldReturnTeamIfReferrerPartyIDProvided) 322 t.Run("Should return a team if a referee party ID is provided", testShouldReturnTeamIfRefereePartyIDProvided) 323 t.Run("Should return an error if no team ID or party ID is provided", testShouldReturnErrorIfNoTeamIDOrPartyIDProvided) 324 } 325 326 func testShouldReturnTeamIfTeamIDProvided(t *testing.T) { 327 bs, ts, ps := setupTeamsTest(t) 328 ctx := tempTransaction(t) 329 330 teams, _ := setupTeams(t, ctx, bs, ps, ts) 331 332 want := teams[rand.Intn(len(teams))] 333 got, err := ts.GetTeam(ctx, want.ID, "") 334 require.NoError(t, err) 335 require.NotNil(t, got) 336 assert.Equal(t, want, *got) 337 } 338 339 func testShouldReturnTeamIfReferrerPartyIDProvided(t *testing.T) { 340 bs, ts, ps := setupTeamsTest(t) 341 ctx := tempTransaction(t) 342 343 teams, _ := setupTeams(t, ctx, bs, ps, ts) 344 345 want := teams[rand.Intn(len(teams))] 346 347 got, err := ts.GetTeam(ctx, "", want.Referrer) 348 require.NoError(t, err) 349 require.NotNil(t, got) 350 assert.Equal(t, want, *got) 351 } 352 353 func testShouldReturnTeamIfRefereePartyIDProvided(t *testing.T) { 354 bs, ts, ps := setupTeamsTest(t) 355 356 ctx := tempTransaction(t) 357 358 teams, teamsHistory := setupTeams(t, ctx, bs, ps, ts) 359 360 wantTeam := teams[rand.Intn(len(teams))] 361 referees := currentRefereesForTeam(teamsHistory, wantTeam.ID) 362 wantMember := referees[rand.Intn(len(referees))] 363 364 got, err := ts.GetTeam(ctx, "", wantMember.PartyID) 365 require.NoError(t, err) 366 require.NotNil(t, got) 367 assert.Equal(t, wantTeam, *got) 368 } 369 370 func testShouldReturnErrorIfNoTeamIDOrPartyIDProvided(t *testing.T) { 371 bs, ts, ps := setupTeamsTest(t) 372 ctx := tempTransaction(t) 373 374 setupTeams(t, ctx, bs, ps, ts) 375 376 _, err := ts.GetTeam(ctx, "", "") 377 require.Error(t, err) 378 } 379 380 func TestTeams_ListTeams(t *testing.T) { 381 t.Run("Should return a page of teams if no pagination is provided", testShouldReturnPageOfTeamsIfNoPaginationProvided) 382 t.Run("Should return a page of teams if no pagination is provided newest first", testShouldReturnPageOfTeamsIfNoPaginationProvidedNewestFirst) 383 t.Run("Should return the first page of teams if first N is requested", testShouldReturnFirstPageOfTeamsIfFirstNRequested) 384 t.Run("Should return the last page of teams if last N is requested", testShouldReturnLastPageOfTeamsIfLastNRequested) 385 t.Run("Should return the page of teams given the provided pagination", testShouldReturnPageOfTeamsGivenPagination) 386 } 387 388 func testShouldReturnPageOfTeamsIfNoPaginationProvided(t *testing.T) { 389 bs, ts, ps := setupTeamsTest(t) 390 ctx := tempTransaction(t) 391 392 teams, _ := setupTeams(t, ctx, bs, ps, ts) 393 394 got, pageInfo, err := ts.ListTeams(ctx, entities.CursorPagination{}) 395 require.NoError(t, err) 396 assert.Equal(t, teams, got) 397 assert.Equal(t, entities.PageInfo{ 398 HasNextPage: false, 399 HasPreviousPage: false, 400 StartCursor: teams[0].Cursor().Encode(), 401 EndCursor: teams[len(teams)-1].Cursor().Encode(), 402 }, pageInfo) 403 } 404 405 func testShouldReturnPageOfTeamsIfNoPaginationProvidedNewestFirst(t *testing.T) { 406 bs, ts, ps := setupTeamsTest(t) 407 ctx := tempTransaction(t) 408 409 teams, _ := setupTeams(t, ctx, bs, ps, ts) 410 411 got, pageInfo, err := ts.ListTeams(ctx, entities.CursorPagination{NewestFirst: true}) 412 require.NoError(t, err) 413 414 sort.Slice(teams, func(i, j int) bool { 415 return teams[i].CreatedAt.After(teams[j].CreatedAt) 416 }) 417 418 assert.Equal(t, teams, got) 419 assert.Equal(t, entities.PageInfo{ 420 HasNextPage: false, 421 HasPreviousPage: false, 422 StartCursor: teams[0].Cursor().Encode(), 423 EndCursor: teams[len(teams)-1].Cursor().Encode(), 424 }, pageInfo) 425 } 426 427 func testShouldReturnFirstPageOfTeamsIfFirstNRequested(t *testing.T) { 428 bs, ts, ps := setupTeamsTest(t) 429 ctx := tempTransaction(t) 430 431 teams, _ := setupTeams(t, ctx, bs, ps, ts) 432 433 pagination, err := entities.NewCursorPagination(ptr.From(int32(3)), nil, nil, nil, false) 434 require.NoError(t, err) 435 436 got, pageInfo, err := ts.ListTeams(ctx, pagination) 437 require.NoError(t, err) 438 439 want := teams[:3] 440 441 assert.Equal(t, want, got) 442 assert.Equal(t, entities.PageInfo{ 443 HasNextPage: true, 444 HasPreviousPage: false, 445 StartCursor: want[0].Cursor().Encode(), 446 EndCursor: want[len(want)-1].Cursor().Encode(), 447 }, pageInfo) 448 } 449 450 func testShouldReturnLastPageOfTeamsIfLastNRequested(t *testing.T) { 451 bs, ts, ps := setupTeamsTest(t) 452 ctx := tempTransaction(t) 453 454 teams, _ := setupTeams(t, ctx, bs, ps, ts) 455 456 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(3)), nil, false) 457 require.NoError(t, err) 458 459 got, pageInfo, err := ts.ListTeams(ctx, pagination) 460 require.NoError(t, err) 461 462 want := teams[len(teams)-3:] 463 464 assert.Equal(t, want, got) 465 assert.Equal(t, entities.PageInfo{ 466 HasNextPage: false, 467 HasPreviousPage: true, 468 StartCursor: want[0].Cursor().Encode(), 469 EndCursor: want[len(want)-1].Cursor().Encode(), 470 }, pageInfo) 471 } 472 473 func testShouldReturnPageOfTeamsGivenPagination(t *testing.T) { 474 bs, ts, ps := setupTeamsTest(t) 475 ctx := tempTransaction(t) 476 477 teams, _ := setupTeams(t, ctx, bs, ps, ts) 478 479 t.Run("first after", func(t *testing.T) { 480 pagination, err := entities.NewCursorPagination(ptr.From(int32(3)), ptr.From(teams[2].Cursor().Encode()), nil, nil, false) 481 require.NoError(t, err) 482 483 got, pageInfo, err := ts.ListTeams(ctx, pagination) 484 require.NoError(t, err) 485 486 want := teams[3:6] 487 488 assert.Equal(t, want, got) 489 assert.Equal(t, entities.PageInfo{ 490 HasNextPage: true, 491 HasPreviousPage: true, 492 StartCursor: want[0].Cursor().Encode(), 493 EndCursor: want[len(want)-1].Cursor().Encode(), 494 }, pageInfo) 495 }) 496 497 t.Run("last before", func(t *testing.T) { 498 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(3)), ptr.From(teams[7].Cursor().Encode()), false) 499 require.NoError(t, err) 500 501 got, pageInfo, err := ts.ListTeams(ctx, pagination) 502 require.NoError(t, err) 503 504 want := teams[4:7] 505 506 assert.Equal(t, want, got) 507 assert.Equal(t, entities.PageInfo{ 508 HasNextPage: true, 509 HasPreviousPage: true, 510 StartCursor: want[0].Cursor().Encode(), 511 EndCursor: want[len(want)-1].Cursor().Encode(), 512 }, pageInfo) 513 }) 514 } 515 516 func TestTeams_ListReferees(t *testing.T) { 517 t.Run("Should return an error if no team ID is provided", testShouldReturnErrorIfNoTeamIDProvided) 518 t.Run("Should return a page of referees if no pagination is provided", testShouldReturnPageOfRefereesIfNoPaginationProvided) 519 t.Run("Should return the first page of referees if first N is requested", testShouldReturnFirstPageOfRefereesIfFirstNRequested) 520 t.Run("Should return the last page of referees if last N is requested", testShouldReturnLastPageOfRefereesIfLastNRequested) 521 t.Run("Should return the page of referees given the provided pagination", testShouldReturnPageOfRefereesGivenPagination) 522 } 523 524 func testShouldReturnErrorIfNoTeamIDProvided(t *testing.T) { 525 _, ts, _ := setupTeamsTest(t) 526 ctx := tempTransaction(t) 527 528 _, _, err := ts.ListReferees(ctx, "", entities.CursorPagination{}) 529 require.Error(t, err) 530 } 531 532 func testShouldReturnPageOfRefereesIfNoPaginationProvided(t *testing.T) { 533 bs, ts, ps := setupTeamsTest(t) 534 ctx := tempTransaction(t) 535 536 teams, teamsHistory := setupTeams(t, ctx, bs, ps, ts) 537 team := teams[rand.Intn(len(teams))] 538 539 referees := currentRefereesForTeam(teamsHistory, team.ID) 540 541 got, pageInfo, err := ts.ListReferees(ctx, team.ID, entities.CursorPagination{}) 542 require.NoError(t, err) 543 assert.Equal(t, referees, got) 544 assert.Equal(t, entities.PageInfo{ 545 HasNextPage: false, 546 HasPreviousPage: false, 547 StartCursor: referees[0].Cursor().Encode(), 548 EndCursor: referees[len(referees)-1].Cursor().Encode(), 549 }, pageInfo) 550 } 551 552 func testShouldReturnFirstPageOfRefereesIfFirstNRequested(t *testing.T) { 553 bs, ts, ps := setupTeamsTest(t) 554 ctx := tempTransaction(t) 555 556 teams, teamsHistory := setupTeams(t, ctx, bs, ps, ts) 557 558 team := teams[rand.Intn(len(teams))] 559 560 referees := currentRefereesForTeam(teamsHistory, team.ID) 561 pagination, err := entities.NewCursorPagination(ptr.From(int32(3)), nil, nil, nil, false) 562 require.NoError(t, err) 563 got, pageInfo, err := ts.ListReferees(ctx, team.ID, pagination) 564 require.NoError(t, err) 565 want := referees[:3] 566 assert.Equal(t, want, got) 567 assert.Equal(t, entities.PageInfo{ 568 HasNextPage: true, 569 HasPreviousPage: false, 570 StartCursor: referees[0].Cursor().Encode(), 571 EndCursor: referees[2].Cursor().Encode(), 572 }, pageInfo) 573 } 574 575 func testShouldReturnLastPageOfRefereesIfLastNRequested(t *testing.T) { 576 bs, ts, ps := setupTeamsTest(t) 577 ctx := tempTransaction(t) 578 579 teams, teamsHistory := setupTeams(t, ctx, bs, ps, ts) 580 581 team := teams[rand.Intn(len(teams))] 582 583 referees := currentRefereesForTeam(teamsHistory, team.ID) 584 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(3)), nil, false) 585 require.NoError(t, err) 586 got, pageInfo, err := ts.ListReferees(ctx, team.ID, pagination) 587 require.NoError(t, err) 588 want := referees[len(referees)-3:] 589 assert.Equal(t, want, got) 590 assert.Equal(t, entities.PageInfo{ 591 HasNextPage: false, 592 HasPreviousPage: true, 593 StartCursor: want[0].Cursor().Encode(), 594 EndCursor: want[len(want)-1].Cursor().Encode(), 595 }, pageInfo) 596 } 597 598 func testShouldReturnPageOfRefereesGivenPagination(t *testing.T) { 599 bs, ts, ps := setupTeamsTest(t) 600 ctx := tempTransaction(t) 601 602 teams, teamsHistory := setupTeams(t, ctx, bs, ps, ts) 603 604 team := teams[rand.Intn(len(teams))] 605 606 referees := currentRefereesForTeam(teamsHistory, team.ID) 607 608 t.Run("first after", func(t *testing.T) { 609 pagination, err := entities.NewCursorPagination(ptr.From(int32(3)), ptr.From(referees[2].Cursor().Encode()), nil, nil, false) 610 require.NoError(t, err) 611 got, pageInfo, err := ts.ListReferees(ctx, team.ID, pagination) 612 require.NoError(t, err) 613 614 want := referees[3:6] 615 assert.Equal(t, want, got) 616 assert.Equal(t, entities.PageInfo{ 617 HasNextPage: true, 618 HasPreviousPage: true, 619 StartCursor: referees[3].Cursor().Encode(), 620 EndCursor: referees[5].Cursor().Encode(), 621 }, pageInfo) 622 }) 623 624 t.Run("last before", func(t *testing.T) { 625 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(3)), ptr.From(referees[7].Cursor().Encode()), false) 626 require.NoError(t, err) 627 got, pageInfo, err := ts.ListReferees(ctx, team.ID, pagination) 628 require.NoError(t, err) 629 want := referees[4:7] 630 assert.Equal(t, want, got) 631 assert.Equal(t, entities.PageInfo{ 632 HasNextPage: true, 633 HasPreviousPage: true, 634 StartCursor: want[0].Cursor().Encode(), 635 EndCursor: want[len(want)-1].Cursor().Encode(), 636 }, pageInfo) 637 }) 638 } 639 640 func TestTeams_ListRefereeHistory(t *testing.T) { 641 t.Run("Should return an error if the referee is not provided", testShouldReturnErrorIfRefereeNotProvided) 642 t.Run("Should return a page of referee history if no pagination is provided", testShouldReturnPageOfRefereeHistoryIfNoPaginationProvided) 643 t.Run("Should return a page of referee history if no pagination is provided newest first", testShouldReturnPageOfRefereeHistoryIfNoPaginationProvidedNewestFirst) 644 t.Run("Should return the first page of referee history if first N is requested", testShouldReturnFirstPageOfRefereeHistoryIfFirstNRequested) 645 t.Run("Should return the last page of referee history if last N is requested", testShouldReturnLastPageOfRefereeHistoryIfLastNRequested) 646 t.Run("Should return the page of referee history given the provided pagination", testShouldReturnPageOfRefereeHistoryGivenPagination) 647 } 648 649 func testShouldReturnErrorIfRefereeNotProvided(t *testing.T) { 650 _, ts, _ := setupTeamsTest(t) 651 ctx := tempTransaction(t) 652 653 _, _, err := ts.ListRefereeHistory(ctx, "", entities.CursorPagination{}) 654 require.Error(t, err) 655 } 656 657 func testShouldReturnPageOfRefereeHistoryIfNoPaginationProvided(t *testing.T) { 658 bs, ts, ps := setupTeamsTest(t) 659 ctx := tempTransaction(t) 660 661 teams, teamsHistory := setupTeams(t, ctx, bs, ps, ts) 662 referee := teamsHistory[len(teams)] // the first n elements (== len(teams) are the referrers) 663 664 refereeHistory := historyForReferee(teamsHistory, referee.PartyID) 665 666 got, pageInfo, err := ts.ListRefereeHistory(ctx, referee.PartyID, entities.CursorPagination{}) 667 require.NoError(t, err) 668 assert.Equal(t, refereeHistory, got) 669 assert.Equal(t, entities.PageInfo{ 670 HasNextPage: false, 671 HasPreviousPage: false, 672 StartCursor: refereeHistory[0].Cursor().Encode(), 673 EndCursor: refereeHistory[len(refereeHistory)-1].Cursor().Encode(), 674 }, pageInfo) 675 } 676 677 func testShouldReturnPageOfRefereeHistoryIfNoPaginationProvidedNewestFirst(t *testing.T) { 678 bs, ts, ps := setupTeamsTest(t) 679 ctx := tempTransaction(t) 680 681 teams, teamsHistory := setupTeams(t, ctx, bs, ps, ts) 682 referee := teamsHistory[len(teams)] // the first n elements (== len(teams) are the referrers) 683 684 got, pageInfo, err := ts.ListRefereeHistory(ctx, referee.PartyID, entities.CursorPagination{NewestFirst: true}) 685 require.NoError(t, err) 686 687 refereeHistory := historyForReferee(teamsHistory, referee.PartyID) 688 slices.SortStableFunc(refereeHistory, func(a, b entities.TeamMemberHistory) int { 689 return -compareUint64(a.JoinedAtEpoch, b.JoinedAtEpoch) 690 }) 691 692 assert.Equal(t, refereeHistory, got) 693 assert.Equal(t, entities.PageInfo{ 694 HasNextPage: false, 695 HasPreviousPage: false, 696 StartCursor: refereeHistory[0].Cursor().Encode(), 697 EndCursor: refereeHistory[len(refereeHistory)-1].Cursor().Encode(), 698 }, pageInfo) 699 } 700 701 func testShouldReturnFirstPageOfRefereeHistoryIfFirstNRequested(t *testing.T) { 702 bs, ts, ps := setupTeamsTest(t) 703 ctx := tempTransaction(t) 704 705 teams, teamsHistory := setupTeams(t, ctx, bs, ps, ts) 706 referee := teamsHistory[len(teams)] // the first n elements (== len(teams) are the referrers) 707 708 pagination, err := entities.NewCursorPagination(ptr.From(int32(3)), nil, nil, nil, false) 709 require.NoError(t, err) 710 got, pageInfo, err := ts.ListRefereeHistory(ctx, referee.PartyID, pagination) 711 require.NoError(t, err) 712 want := historyForReferee(teamsHistory, referee.PartyID)[:3] 713 assert.Equal(t, want, got) 714 assert.Equal(t, entities.PageInfo{ 715 HasNextPage: true, 716 HasPreviousPage: false, 717 StartCursor: want[0].Cursor().Encode(), 718 EndCursor: want[len(want)-1].Cursor().Encode(), 719 }, pageInfo) 720 } 721 722 func testShouldReturnLastPageOfRefereeHistoryIfLastNRequested(t *testing.T) { 723 bs, ts, ps := setupTeamsTest(t) 724 ctx := tempTransaction(t) 725 726 teams, teamsHistory := setupTeams(t, ctx, bs, ps, ts) 727 728 referee := teamsHistory[len(teams)] // the first n elements (== len(teams) are the referrers) 729 refereeHistory := historyForReferee(teamsHistory, referee.PartyID) 730 731 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(3)), nil, false) 732 require.NoError(t, err) 733 got, pageInfo, err := ts.ListRefereeHistory(ctx, referee.PartyID, pagination) 734 require.NoError(t, err) 735 want := refereeHistory[len(refereeHistory)-3:] 736 assert.Equal(t, want, got) 737 assert.Equal(t, entities.PageInfo{ 738 HasNextPage: false, 739 HasPreviousPage: true, 740 StartCursor: want[0].Cursor().Encode(), 741 EndCursor: want[len(want)-1].Cursor().Encode(), 742 }, pageInfo) 743 } 744 745 func testShouldReturnPageOfRefereeHistoryGivenPagination(t *testing.T) { 746 bs, ts, ps := setupTeamsTest(t) 747 ctx := tempTransaction(t) 748 749 teams, teamsHistory := setupTeams(t, ctx, bs, ps, ts) 750 751 referee := teamsHistory[len(teams)] // the first n elements (== len(teams) are the referrers) 752 refereeHistory := historyForReferee(teamsHistory, referee.PartyID) 753 754 t.Run("first after", func(t *testing.T) { 755 pagination, err := entities.NewCursorPagination(ptr.From(int32(3)), ptr.From(refereeHistory[2].Cursor().Encode()), nil, nil, false) 756 require.NoError(t, err) 757 got, pageInfo, err := ts.ListRefereeHistory(ctx, referee.PartyID, pagination) 758 require.NoError(t, err) 759 want := refereeHistory[3:6] 760 assert.Equal(t, want, got) 761 assert.Equal(t, entities.PageInfo{ 762 HasNextPage: true, 763 HasPreviousPage: true, 764 StartCursor: want[0].Cursor().Encode(), 765 EndCursor: want[len(want)-1].Cursor().Encode(), 766 }, pageInfo) 767 }) 768 769 t.Run("last before", func(t *testing.T) { 770 pagination, err := entities.NewCursorPagination(nil, nil, ptr.From(int32(3)), ptr.From(refereeHistory[7].Cursor().Encode()), false) 771 require.NoError(t, err) 772 got, pageInfo, err := ts.ListRefereeHistory(ctx, referee.PartyID, pagination) 773 require.NoError(t, err) 774 want := refereeHistory[4:7] 775 assert.Equal(t, want, got) 776 assert.Equal(t, entities.PageInfo{ 777 HasNextPage: true, 778 HasPreviousPage: true, 779 StartCursor: want[0].Cursor().Encode(), 780 EndCursor: want[len(want)-1].Cursor().Encode(), 781 }, pageInfo) 782 }) 783 } 784 785 func TestListTeamStatistics(t *testing.T) { 786 ctx := tempTransaction(t) 787 788 accountsStore := sqlstore.NewAccounts(connectionSource) 789 transfersStore := sqlstore.NewTransfers(connectionSource) 790 teamsStore := sqlstore.NewTeams(connectionSource) 791 blocksStore := sqlstore.NewBlocks(connectionSource) 792 rewardsStore := sqlstore.NewRewards(ctx, connectionSource) 793 epochStore := sqlstore.NewEpochs(connectionSource) 794 795 member11 := entities.PartyID(GenerateID()) 796 member12 := entities.PartyID(GenerateID()) 797 member21 := entities.PartyID(GenerateID()) 798 member22 := entities.PartyID(GenerateID()) 799 member31 := entities.PartyID(GenerateID()) 800 member32 := entities.PartyID(GenerateID()) 801 member41 := entities.PartyID(GenerateID()) 802 member42 := entities.PartyID(GenerateID()) 803 804 team1 := entities.TeamID(GenerateID()) 805 team2 := entities.TeamID(GenerateID()) 806 team3 := entities.TeamID(GenerateID()) 807 team4 := entities.TeamID(GenerateID()) 808 809 teams := map[entities.TeamID][]entities.PartyID{ 810 team1: {member11, member12}, 811 team2: {member21, member22}, 812 team3: {member31, member32}, 813 team4: {member41, member42}, 814 } 815 816 teamIDs := []entities.TeamID{team1, team2, team3, team4} 817 gameIDs := []entities.GameID{ 818 entities.GameID("11" + GenerateID()), 819 entities.GameID("22" + GenerateID()), 820 entities.GameID("33" + GenerateID()), 821 entities.GameID("44" + GenerateID()), 822 } 823 824 startTime := time.Now() 825 826 for _, gameID := range gameIDs { 827 fromAccount := &entities.Account{ 828 PartyID: entities.PartyID(GenerateID()), 829 AssetID: entities.AssetID(GenerateID()), 830 MarketID: entities.MarketID(GenerateID()), 831 Type: vegapb.AccountType_ACCOUNT_TYPE_GENERAL, 832 TxHash: generateTxHash(), 833 VegaTime: startTime, 834 } 835 require.NoError(t, accountsStore.Add(ctx, fromAccount)) 836 837 toAccount := &entities.Account{ 838 PartyID: entities.PartyID(GenerateID()), 839 AssetID: entities.AssetID(GenerateID()), 840 MarketID: entities.MarketID(GenerateID()), 841 Type: vegapb.AccountType_ACCOUNT_TYPE_GENERAL, 842 TxHash: generateTxHash(), 843 VegaTime: startTime, 844 } 845 require.NoError(t, accountsStore.Add(ctx, toAccount)) 846 847 require.NoError(t, transfersStore.Upsert(ctx, &entities.Transfer{ 848 ID: entities.TransferID(GenerateID()), 849 TxHash: generateTxHash(), 850 VegaTime: startTime, 851 FromAccountID: sqlstore.DeterministicIDFromAccount(fromAccount), 852 ToAccountID: sqlstore.DeterministicIDFromAccount(toAccount), 853 TransferType: 2, 854 DispatchStrategy: &vegapb.DispatchStrategy{ 855 EntityScope: 2, 856 }, 857 GameID: gameID, 858 })) 859 } 860 861 for team, members := range teams { 862 require.NoError(t, teamsStore.AddTeam(ctx, &entities.Team{ 863 ID: team, 864 Referrer: entities.PartyID(GenerateID()), 865 Name: "Name", 866 Closed: false, 867 CreatedAt: startTime, 868 CreatedAtEpoch: 1, 869 VegaTime: startTime, 870 })) 871 872 for _, member := range members { 873 require.NoError(t, teamsStore.RefereeJoinedTeam(ctx, &entities.TeamMember{ 874 TeamID: team, 875 PartyID: member, 876 JoinedAt: startTime, 877 JoinedAtEpoch: 1, 878 VegaTime: startTime, 879 })) 880 } 881 } 882 883 for epoch := int64(1); epoch < 4; epoch++ { 884 blockTime := startTime.Add(time.Duration(epoch) * time.Minute).Truncate(time.Microsecond) 885 block := entities.Block{ 886 VegaTime: blockTime, 887 Height: epoch, 888 Hash: []byte(vgcrypto.RandomHash()), 889 } 890 require.NoError(t, blocksStore.Add(ctx, block)) 891 require.NoError(t, epochStore.Add(ctx, entities.Epoch{ 892 ID: epoch, 893 StartTime: blockTime.Add(-1 * time.Minute), 894 ExpireTime: blockTime, 895 EndTime: ptr.From(blockTime), 896 TxHash: generateTxHash(), 897 VegaTime: blockTime, 898 FirstBlock: ptr.From(epoch), 899 LastBlock: ptr.From(epoch), 900 })) 901 902 j := 0 903 evt := &eventspb.TeamsStatsUpdated{ 904 AtEpoch: uint64(epoch), 905 } 906 for _, teamID := range teamIDs { 907 membersStats := []*eventspb.TeamMemberStats{} 908 909 for i, member := range teams[teamID] { 910 membersStats = append(membersStats, &eventspb.TeamMemberStats{ 911 PartyId: string(member), 912 NotionalVolume: fmt.Sprintf("%d", i+j), 913 }) 914 } 915 // Add some notional volume. 916 // It's done before the rewards as this is what will happen in the core as the 917 // MarketActivityTracker will send teams stats before the reward engine sends the 918 // the reward payout. 919 evt.Stats = append(evt.Stats, &eventspb.TeamStats{ 920 TeamId: string(teamID), 921 MembersStats: membersStats, 922 }) 923 924 j += 1 925 } 926 require.NoError(t, teamsStore.TeamsStatsUpdated(ctx, evt)) 927 928 seqNum := uint64(0) 929 for teamIdx, teamID := range teamIDs { 930 for _, member := range teams[teamID] { 931 seqNum += 1 932 933 require.NoError(t, rewardsStore.Add(ctx, entities.Reward{ 934 PartyID: member, 935 AssetID: entities.AssetID(GenerateID()), 936 MarketID: entities.MarketID(GenerateID()), 937 EpochID: epoch, 938 Amount: decimal.NewFromInt(int64(seqNum)), 939 QuantumAmount: decimal.NewFromInt(epoch + int64(seqNum)), 940 PercentOfTotal: 0.1 * float64(epoch), 941 RewardType: "NICE_BOY", 942 Timestamp: blockTime, 943 TxHash: generateTxHash(), 944 VegaTime: blockTime, 945 SeqNum: seqNum, 946 LockedUntilEpochID: epoch, 947 GameID: ptr.From(gameIDs[teamIdx]), 948 })) 949 } 950 } 951 952 // Add non-game rewards to ensure we only account for game rewards. 953 for _, teamID := range teamIDs { 954 for _, member := range teams[teamID] { 955 seqNum += 1 956 957 require.NoError(t, rewardsStore.Add(ctx, entities.Reward{ 958 PartyID: member, 959 AssetID: entities.AssetID(GenerateID()), 960 MarketID: entities.MarketID(GenerateID()), 961 EpochID: epoch, 962 Amount: decimal.NewFromInt(int64(seqNum)), 963 QuantumAmount: decimal.NewFromInt(epoch + int64(seqNum)), 964 PercentOfTotal: 0.1 * float64(epoch), 965 RewardType: "NICE_BOY", 966 Timestamp: blockTime, 967 TxHash: generateTxHash(), 968 VegaTime: blockTime.Add(time.Second), 969 SeqNum: seqNum, 970 LockedUntilEpochID: epoch, 971 })) 972 } 973 } 974 } 975 976 t.Run("Getting all stats from the last 2 epochs", func(t *testing.T) { 977 stats, _, err := teamsStore.ListTeamsStatistics(ctx, entities.DefaultCursorPagination(false), sqlstore.ListTeamsStatisticsFilters{ 978 AggregationEpochs: 2, 979 }) 980 981 require.NoError(t, err) 982 expectedStats := []entities.TeamsStatistics{ 983 { 984 TeamID: team1, 985 TotalQuantumRewards: decimal.NewFromInt(16), 986 QuantumRewards: []entities.QuantumRewardsPerEpoch{ 987 { 988 Epoch: 2, 989 Total: decimal.NewFromInt(3), 990 }, { 991 Epoch: 2, 992 Total: decimal.NewFromInt(4), 993 }, { 994 Epoch: 3, 995 Total: decimal.NewFromInt(4), 996 }, { 997 Epoch: 3, 998 Total: decimal.NewFromInt(5), 999 }, 1000 }, 1001 TotalQuantumVolumes: num.NewUint(2), 1002 QuantumVolumes: []entities.QuantumVolumesPerEpoch{ 1003 { 1004 Epoch: 2, 1005 Total: num.NewUint(0), 1006 }, { 1007 Epoch: 2, 1008 Total: num.NewUint(1), 1009 }, { 1010 Epoch: 3, 1011 Total: num.NewUint(0), 1012 }, { 1013 Epoch: 3, 1014 Total: num.NewUint(1), 1015 }, 1016 }, 1017 TotalGamesPlayed: 1, 1018 GamesPlayed: []entities.GameID{gameIDs[0]}, 1019 }, 1020 { 1021 TeamID: team2, 1022 TotalQuantumRewards: decimal.NewFromInt(24), 1023 QuantumRewards: []entities.QuantumRewardsPerEpoch{ 1024 { 1025 Epoch: 2, 1026 Total: decimal.NewFromInt(5), 1027 }, { 1028 Epoch: 2, 1029 Total: decimal.NewFromInt(6), 1030 }, { 1031 Epoch: 3, 1032 Total: decimal.NewFromInt(6), 1033 }, { 1034 Epoch: 3, 1035 Total: decimal.NewFromInt(7), 1036 }, 1037 }, 1038 TotalQuantumVolumes: num.NewUint(6), 1039 QuantumVolumes: []entities.QuantumVolumesPerEpoch{ 1040 { 1041 Epoch: 2, 1042 Total: num.NewUint(1), 1043 }, { 1044 Epoch: 2, 1045 Total: num.NewUint(2), 1046 }, { 1047 Epoch: 3, 1048 Total: num.NewUint(1), 1049 }, { 1050 Epoch: 3, 1051 Total: num.NewUint(2), 1052 }, 1053 }, 1054 TotalGamesPlayed: 1, 1055 GamesPlayed: []entities.GameID{gameIDs[1]}, 1056 }, 1057 { 1058 TeamID: team3, 1059 TotalQuantumRewards: decimal.NewFromInt(32), 1060 QuantumRewards: []entities.QuantumRewardsPerEpoch{ 1061 { 1062 Epoch: 2, 1063 Total: decimal.NewFromInt(7), 1064 }, { 1065 Epoch: 2, 1066 Total: decimal.NewFromInt(8), 1067 }, { 1068 Epoch: 3, 1069 Total: decimal.NewFromInt(8), 1070 }, { 1071 Epoch: 3, 1072 Total: decimal.NewFromInt(9), 1073 }, 1074 }, 1075 TotalQuantumVolumes: num.NewUint(10), 1076 QuantumVolumes: []entities.QuantumVolumesPerEpoch{ 1077 { 1078 Epoch: 2, 1079 Total: num.NewUint(2), 1080 }, { 1081 Epoch: 2, 1082 Total: num.NewUint(3), 1083 }, { 1084 Epoch: 3, 1085 Total: num.NewUint(2), 1086 }, { 1087 Epoch: 3, 1088 Total: num.NewUint(3), 1089 }, 1090 }, 1091 TotalGamesPlayed: 1, 1092 GamesPlayed: []entities.GameID{gameIDs[2]}, 1093 }, 1094 { 1095 TeamID: team4, 1096 TotalQuantumRewards: decimal.NewFromInt(40), 1097 QuantumRewards: []entities.QuantumRewardsPerEpoch{ 1098 { 1099 Epoch: 2, 1100 Total: decimal.NewFromInt(9), 1101 }, { 1102 Epoch: 2, 1103 Total: decimal.NewFromInt(10), 1104 }, { 1105 Epoch: 3, 1106 Total: decimal.NewFromInt(10), 1107 }, { 1108 Epoch: 3, 1109 Total: decimal.NewFromInt(11), 1110 }, 1111 }, 1112 TotalQuantumVolumes: num.NewUint(14), 1113 QuantumVolumes: []entities.QuantumVolumesPerEpoch{ 1114 { 1115 Epoch: 2, 1116 Total: num.NewUint(3), 1117 }, { 1118 Epoch: 2, 1119 Total: num.NewUint(4), 1120 }, { 1121 Epoch: 3, 1122 Total: num.NewUint(3), 1123 }, { 1124 Epoch: 3, 1125 Total: num.NewUint(4), 1126 }, 1127 }, 1128 TotalGamesPlayed: 1, 1129 GamesPlayed: []entities.GameID{gameIDs[3]}, 1130 }, 1131 } 1132 slices.SortStableFunc(expectedStats, func(a, b entities.TeamsStatistics) int { 1133 return strings.Compare(string(a.TeamID), string(b.TeamID)) 1134 }) 1135 1136 // Ugly hack to bypass deep-equal limitation with assert.Equal(). 1137 expectedStatsJson, _ := json.Marshal(expectedStats) 1138 statsJson, _ := json.Marshal(stats) 1139 assert.JSONEq(t, string(expectedStatsJson), string(statsJson)) 1140 }) 1141 1142 t.Run("Getting stats from a given team from the last 2 epochs", func(t *testing.T) { 1143 stats, _, err := teamsStore.ListTeamsStatistics(ctx, entities.DefaultCursorPagination(false), sqlstore.ListTeamsStatisticsFilters{ 1144 TeamID: ptr.From(entities.TeamID(team1.String())), 1145 AggregationEpochs: 2, 1146 }) 1147 1148 require.NoError(t, err) 1149 expectedStats := []entities.TeamsStatistics{ 1150 { 1151 TeamID: team1, 1152 TotalQuantumRewards: decimal.NewFromInt(16), 1153 QuantumRewards: []entities.QuantumRewardsPerEpoch{ 1154 { 1155 Epoch: 2, 1156 Total: decimal.NewFromInt(3), 1157 }, { 1158 Epoch: 2, 1159 Total: decimal.NewFromInt(4), 1160 }, { 1161 Epoch: 3, 1162 Total: decimal.NewFromInt(4), 1163 }, { 1164 Epoch: 3, 1165 Total: decimal.NewFromInt(5), 1166 }, 1167 }, 1168 TotalQuantumVolumes: num.NewUint(2), 1169 QuantumVolumes: []entities.QuantumVolumesPerEpoch{ 1170 { 1171 Epoch: 2, 1172 Total: num.NewUint(0), 1173 }, { 1174 Epoch: 2, 1175 Total: num.NewUint(1), 1176 }, { 1177 Epoch: 3, 1178 Total: num.NewUint(0), 1179 }, { 1180 Epoch: 3, 1181 Total: num.NewUint(1), 1182 }, 1183 }, 1184 TotalGamesPlayed: 1, 1185 GamesPlayed: []entities.GameID{gameIDs[0]}, 1186 }, 1187 } 1188 1189 // Ugly hack to bypass deep-equal limitation with assert.Equal(). 1190 expectedStatsJson, _ := json.Marshal(expectedStats) 1191 statsJson, _ := json.Marshal(stats) 1192 assert.JSONEq(t, string(expectedStatsJson), string(statsJson)) 1193 }) 1194 1195 t.Run("Getting all members stats from the last 2 epochs", func(t *testing.T) { 1196 stats, _, err := teamsStore.ListTeamMembersStatistics(ctx, entities.DefaultCursorPagination(false), sqlstore.ListTeamMembersStatisticsFilters{ 1197 TeamID: team2, 1198 AggregationEpochs: 2, 1199 }) 1200 1201 require.NoError(t, err) 1202 expectedStats := []entities.TeamMembersStatistics{ 1203 { 1204 PartyID: member21, 1205 TotalQuantumRewards: decimal.NewFromInt(11), 1206 QuantumRewards: []entities.QuantumRewardsPerEpoch{ 1207 { 1208 Epoch: 2, 1209 Total: decimal.NewFromInt(5), 1210 }, { 1211 Epoch: 3, 1212 Total: decimal.NewFromInt(6), 1213 }, 1214 }, 1215 TotalQuantumVolumes: num.NewUint(2), 1216 QuantumVolumes: []entities.QuantumVolumesPerEpoch{ 1217 { 1218 Epoch: 2, 1219 Total: num.NewUint(1), 1220 }, { 1221 Epoch: 3, 1222 Total: num.NewUint(1), 1223 }, 1224 }, 1225 TotalGamesPlayed: 1, 1226 GamesPlayed: []entities.GameID{gameIDs[1]}, 1227 }, 1228 { 1229 PartyID: member22, 1230 TotalQuantumRewards: decimal.NewFromInt(13), 1231 QuantumRewards: []entities.QuantumRewardsPerEpoch{ 1232 { 1233 Epoch: 2, 1234 Total: decimal.NewFromInt(6), 1235 }, { 1236 Epoch: 3, 1237 Total: decimal.NewFromInt(7), 1238 }, 1239 }, 1240 TotalQuantumVolumes: num.NewUint(4), 1241 QuantumVolumes: []entities.QuantumVolumesPerEpoch{ 1242 { 1243 Epoch: 2, 1244 Total: num.NewUint(2), 1245 }, { 1246 Epoch: 3, 1247 Total: num.NewUint(2), 1248 }, 1249 }, 1250 TotalGamesPlayed: 1, 1251 GamesPlayed: []entities.GameID{gameIDs[1]}, 1252 }, 1253 } 1254 slices.SortStableFunc(expectedStats, func(a, b entities.TeamMembersStatistics) int { 1255 return strings.Compare(string(a.PartyID), string(b.PartyID)) 1256 }) 1257 1258 // Ugly hack to bypass deep-equal limitation with assert.Equal(). 1259 expectedStatsJson, _ := json.Marshal(expectedStats) 1260 statsJson, _ := json.Marshal(stats) 1261 assert.JSONEq(t, string(expectedStatsJson), string(statsJson)) 1262 }) 1263 1264 t.Run("Getting stats from a given party from the last 2 epochs", func(t *testing.T) { 1265 stats, _, err := teamsStore.ListTeamMembersStatistics(ctx, entities.DefaultCursorPagination(false), sqlstore.ListTeamMembersStatisticsFilters{ 1266 TeamID: team2, 1267 PartyID: ptr.From(member21), 1268 AggregationEpochs: 2, 1269 }) 1270 1271 require.NoError(t, err) 1272 expectedStats := []entities.TeamMembersStatistics{ 1273 { 1274 PartyID: member21, 1275 TotalQuantumRewards: decimal.NewFromInt(11), 1276 QuantumRewards: []entities.QuantumRewardsPerEpoch{ 1277 { 1278 Epoch: 2, 1279 Total: decimal.NewFromInt(5), 1280 }, { 1281 Epoch: 3, 1282 Total: decimal.NewFromInt(6), 1283 }, 1284 }, 1285 TotalQuantumVolumes: num.NewUint(2), 1286 QuantumVolumes: []entities.QuantumVolumesPerEpoch{ 1287 { 1288 Epoch: 2, 1289 Total: num.NewUint(1), 1290 }, { 1291 Epoch: 3, 1292 Total: num.NewUint(1), 1293 }, 1294 }, 1295 TotalGamesPlayed: 1, 1296 GamesPlayed: []entities.GameID{gameIDs[1]}, 1297 }, 1298 } 1299 1300 // Ugly hack to bypass deep-equal limitation with assert.Equal(). 1301 expectedStatsJson, _ := json.Marshal(expectedStats) 1302 statsJson, _ := json.Marshal(stats) 1303 assert.JSONEq(t, string(expectedStatsJson), string(statsJson)) 1304 }) 1305 }