code.vegaprotocol.io/vega@v0.79.0/core/teams/engine_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 teams_test
    17  
    18  import (
    19  	"testing"
    20  	"time"
    21  
    22  	"code.vegaprotocol.io/vega/core/teams"
    23  	"code.vegaprotocol.io/vega/core/types"
    24  	vgrand "code.vegaprotocol.io/vega/libs/rand"
    25  	vgtest "code.vegaprotocol.io/vega/libs/test"
    26  	vegapb "code.vegaprotocol.io/vega/protos/vega"
    27  	commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1"
    28  
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  func TestEngine(t *testing.T) {
    34  	t.Run("Administrate a team succeeds", testAdministrateTeamSucceeds)
    35  	t.Run("Joining team succeeds", testJoiningTeamSucceeds)
    36  	t.Run("unique team names", testUniqueTeamNames)
    37  	t.Run("must be in a team for the minimum number of epochs", testMinEpochsRequired)
    38  }
    39  
    40  func testUniqueTeamNames(t *testing.T) {
    41  	ctx := vgtest.VegaContext(vgrand.RandomStr(5), vgtest.RandomI64())
    42  
    43  	te := newEngine(t)
    44  
    45  	require.False(t, te.engine.TeamExists(newTeamID(t)))
    46  
    47  	referrer1 := newPartyID(t)
    48  	referrer2 := newPartyID(t)
    49  	teamID1 := newTeamID(t)
    50  	teamID2 := newTeamID(t)
    51  	name := vgrand.RandomStr(5)
    52  	teamURL := "https://" + name + ".io"
    53  	avatarURL := "https://avatar." + name + ".io"
    54  
    55  	expectTeamCreatedEvent(t, te)
    56  
    57  	team1CreationDate := time.Now()
    58  	te.timeService.EXPECT().GetTimeNow().Return(team1CreationDate).Times(1)
    59  
    60  	require.NoError(t, te.engine.CreateTeam(ctx, referrer1, teamID1, createTeamCmd(t, name, teamURL, avatarURL)))
    61  	require.True(t, te.engine.TeamExists(teamID1))
    62  
    63  	require.EqualError(t, te.engine.CreateTeam(ctx, referrer2, teamID2, createTeamCmd(t, name, teamURL, avatarURL)),
    64  		teams.ErrTeamNameIsAlreadyInUse.Error(),
    65  	)
    66  }
    67  
    68  func testAdministrateTeamSucceeds(t *testing.T) {
    69  	ctx := vgtest.VegaContext(vgrand.RandomStr(5), vgtest.RandomI64())
    70  
    71  	te := newEngine(t)
    72  
    73  	require.False(t, te.engine.TeamExists(newTeamID(t)))
    74  
    75  	referrer1 := newPartyID(t)
    76  	teamID1 := newTeamID(t)
    77  	name := vgrand.RandomStr(5)
    78  	name2 := vgrand.RandomStr(5)
    79  	name3 := vgrand.RandomStr(5)
    80  	teamURL := "https://" + name + ".io"
    81  	avatarURL := "https://avatar." + name + ".io"
    82  
    83  	expectTeamCreatedEvent(t, te)
    84  
    85  	team1CreationDate := time.Now()
    86  	te.timeService.EXPECT().GetTimeNow().Return(team1CreationDate).Times(1)
    87  
    88  	require.NoError(t, te.engine.CreateTeam(ctx, referrer1, teamID1, createTeamCmd(t, name, teamURL, avatarURL)))
    89  	require.True(t, te.engine.TeamExists(teamID1))
    90  
    91  	assert.Equal(t, []types.Team{
    92  		{
    93  			ID: teamID1,
    94  			Referrer: &types.Membership{
    95  				PartyID:        referrer1,
    96  				JoinedAt:       team1CreationDate,
    97  				StartedAtEpoch: te.currentEpoch,
    98  			},
    99  			Referees:  nil,
   100  			Name:      name,
   101  			TeamURL:   teamURL,
   102  			AvatarURL: avatarURL,
   103  			CreatedAt: team1CreationDate,
   104  			Closed:    false,
   105  			AllowList: nil,
   106  		},
   107  	}, te.engine.ListTeams())
   108  
   109  	referrer2 := newPartyID(t)
   110  	teamID2 := newTeamID(t)
   111  
   112  	expectTeamCreatedEvent(t, te)
   113  
   114  	team2CreationDate := time.Now()
   115  	te.timeService.EXPECT().GetTimeNow().Return(team2CreationDate).Times(1)
   116  
   117  	// Using same team URL and avatar URL as the first team is permitted.
   118  	require.NoError(t, te.engine.CreateTeam(ctx, referrer2, teamID2, createTeamCmd(t, name2, teamURL, avatarURL)))
   119  	require.True(t, te.engine.TeamExists(teamID2))
   120  	assert.NotEqual(t, teamID1, teamID2, "Creating a team should generate an unique ID")
   121  
   122  	assertEqualTeams(t, []types.Team{
   123  		{
   124  			ID: teamID1,
   125  			Referrer: &types.Membership{
   126  				PartyID:        referrer1,
   127  				JoinedAt:       team1CreationDate,
   128  				StartedAtEpoch: te.currentEpoch,
   129  			},
   130  			Name:      name,
   131  			TeamURL:   teamURL,
   132  			AvatarURL: avatarURL,
   133  			CreatedAt: team1CreationDate,
   134  		}, {
   135  			ID: teamID2,
   136  			Referrer: &types.Membership{
   137  				PartyID:        referrer2,
   138  				JoinedAt:       team2CreationDate,
   139  				StartedAtEpoch: te.currentEpoch,
   140  			},
   141  			Name:      name2,
   142  			TeamURL:   teamURL,
   143  			AvatarURL: avatarURL,
   144  			CreatedAt: team2CreationDate,
   145  		},
   146  	}, te.engine.ListTeams())
   147  
   148  	teamID3 := newTeamID(t)
   149  
   150  	// A party can only create one team.
   151  	require.EqualError(t,
   152  		te.engine.CreateTeam(ctx, referrer2, teamID3, createTeamCmd(t, name3, teamURL, avatarURL)),
   153  		teams.ErrPartyAlreadyBelongsToTeam(referrer2).Error(),
   154  	)
   155  
   156  	assertEqualTeams(t, []types.Team{
   157  		{
   158  			ID: teamID1,
   159  			Referrer: &types.Membership{
   160  				PartyID:        referrer1,
   161  				JoinedAt:       team1CreationDate,
   162  				StartedAtEpoch: te.currentEpoch,
   163  			},
   164  			Name:      name,
   165  			TeamURL:   teamURL,
   166  			AvatarURL: avatarURL,
   167  			CreatedAt: team1CreationDate,
   168  		}, {
   169  			ID: teamID2,
   170  			Referrer: &types.Membership{
   171  				PartyID:        referrer2,
   172  				JoinedAt:       team2CreationDate,
   173  				StartedAtEpoch: te.currentEpoch,
   174  			},
   175  			Name:      name2,
   176  			TeamURL:   teamURL,
   177  			AvatarURL: avatarURL,
   178  			CreatedAt: team2CreationDate,
   179  		},
   180  	}, te.engine.ListTeams())
   181  
   182  	referrer3 := newPartyID(t)
   183  	teamID4 := newTeamID(t)
   184  
   185  	expectTeamCreatedEvent(t, te)
   186  
   187  	team4CreationDate := time.Now()
   188  	te.timeService.EXPECT().GetTimeNow().Return(team4CreationDate).Times(1)
   189  
   190  	// Team URL and avatar URL are optional.
   191  	team4Name := vgrand.RandomStr(5)
   192  	require.NoError(t, te.engine.CreateTeam(ctx, referrer3, teamID4, createTeamCmd(t, team4Name, "", "")))
   193  	require.True(t, te.engine.TeamExists(teamID4))
   194  	assert.NotEqual(t, teamID1, teamID4, "Creating a team should generate an unique ID")
   195  	assert.NotEqual(t, teamID2, teamID4, "Creating a team should generate an unique ID")
   196  
   197  	assertEqualTeams(t, []types.Team{
   198  		{
   199  			ID: teamID1,
   200  			Referrer: &types.Membership{
   201  				PartyID:        referrer1,
   202  				JoinedAt:       team1CreationDate,
   203  				StartedAtEpoch: te.currentEpoch,
   204  			},
   205  			Name:      name,
   206  			TeamURL:   teamURL,
   207  			AvatarURL: avatarURL,
   208  			CreatedAt: team1CreationDate,
   209  		}, {
   210  			ID: teamID2,
   211  			Referrer: &types.Membership{
   212  				PartyID:        referrer2,
   213  				JoinedAt:       team2CreationDate,
   214  				StartedAtEpoch: te.currentEpoch,
   215  			},
   216  			Name:      name2,
   217  			TeamURL:   teamURL,
   218  			AvatarURL: avatarURL,
   219  			CreatedAt: team2CreationDate,
   220  		}, {
   221  			ID: teamID4,
   222  			Referrer: &types.Membership{
   223  				PartyID:        referrer3,
   224  				JoinedAt:       team4CreationDate,
   225  				StartedAtEpoch: te.currentEpoch,
   226  			},
   227  			Referees:  nil,
   228  			Name:      team4Name,
   229  			TeamURL:   "",
   230  			AvatarURL: "",
   231  			CreatedAt: team4CreationDate,
   232  		},
   233  	}, te.engine.ListTeams())
   234  
   235  	// Updating first team
   236  	updatedName := vgrand.RandomStr(5)
   237  	updatedTeamURL := "https://" + name + ".io"
   238  	updatedAvatarURL := "https://avatar." + name + ".io"
   239  
   240  	expectTeamUpdatedEvent(t, te)
   241  
   242  	require.NoError(t, te.engine.UpdateTeam(ctx, referrer1, teamID1, updateTeamCmd(t, updatedName, updatedTeamURL, updatedAvatarURL, false, nil)))
   243  
   244  	assertEqualTeams(t, []types.Team{
   245  		{
   246  			ID: teamID1,
   247  			Referrer: &types.Membership{
   248  				PartyID:        referrer1,
   249  				JoinedAt:       team1CreationDate,
   250  				StartedAtEpoch: te.currentEpoch,
   251  			},
   252  			Name:      updatedName,
   253  			TeamURL:   updatedTeamURL,
   254  			AvatarURL: updatedAvatarURL,
   255  			CreatedAt: team1CreationDate,
   256  		}, {
   257  			ID: teamID2,
   258  			Referrer: &types.Membership{
   259  				PartyID:        referrer2,
   260  				JoinedAt:       team2CreationDate,
   261  				StartedAtEpoch: te.currentEpoch,
   262  			},
   263  			Name:      name2,
   264  			TeamURL:   teamURL,
   265  			AvatarURL: avatarURL,
   266  			CreatedAt: team2CreationDate,
   267  		}, {
   268  			ID: teamID4,
   269  			Referrer: &types.Membership{
   270  				PartyID:        referrer3,
   271  				JoinedAt:       team4CreationDate,
   272  				StartedAtEpoch: te.currentEpoch,
   273  			},
   274  			Name:      team4Name,
   275  			TeamURL:   "",
   276  			AvatarURL: "",
   277  			CreatedAt: team4CreationDate,
   278  		},
   279  	}, te.engine.ListTeams())
   280  
   281  	unknownTeamID := types.NewTeamID()
   282  	require.EqualError(t,
   283  		te.engine.UpdateTeam(ctx, referrer1, unknownTeamID, updateTeamCmd(t, updatedName, updatedTeamURL, updatedAvatarURL, false, nil)),
   284  		teams.ErrNoTeamMatchesID(unknownTeamID).Error(),
   285  	)
   286  
   287  	require.ErrorIs(t,
   288  		teams.ErrOnlyReferrerCanUpdateTeam,
   289  		te.engine.UpdateTeam(ctx, referrer2, teamID1, updateTeamCmd(t, updatedName, updatedTeamURL, updatedAvatarURL, false, nil)),
   290  	)
   291  }
   292  
   293  func testJoiningTeamSucceeds(t *testing.T) {
   294  	ctx := vgtest.VegaContext(vgrand.RandomStr(5), vgtest.RandomI64())
   295  
   296  	te := newEngine(t)
   297  
   298  	team1CreationDate := time.Now()
   299  	te.timeService.EXPECT().GetTimeNow().Return(team1CreationDate).Times(1)
   300  	teamID1, referrer1, team1Name := newTeam(t, ctx, te)
   301  
   302  	team2CreationDate := time.Now()
   303  	te.timeService.EXPECT().GetTimeNow().Return(team2CreationDate).Times(1)
   304  	teamID2, referrer2, team2Name := newTeam(t, ctx, te)
   305  
   306  	require.ErrorIs(t, te.engine.JoinTeam(ctx, referrer1, joinTeamCmd(t, teamID1)), teams.ErrReferrerCannotJoinAnotherTeam)
   307  	require.ErrorIs(t, te.engine.JoinTeam(ctx, referrer2, joinTeamCmd(t, teamID1)), teams.ErrReferrerCannotJoinAnotherTeam)
   308  
   309  	referee1 := newPartyID(t)
   310  	referee1JoiningDate := time.Now()
   311  	te.timeService.EXPECT().GetTimeNow().Return(referee1JoiningDate).Times(1)
   312  	expectRefereeJoinedTeamEvent(t, te)
   313  	require.NoError(t, te.engine.JoinTeam(ctx, referee1, joinTeamCmd(t, teamID1)))
   314  	require.True(t, te.engine.IsTeamMember(referee1))
   315  
   316  	referee2 := newPartyID(t)
   317  
   318  	// referee2 tries to join a non-existing team.
   319  	unknownTeamID := types.NewTeamID()
   320  	require.EqualError(t,
   321  		te.engine.JoinTeam(ctx, referee2, joinTeamCmd(t, unknownTeamID)),
   322  		teams.ErrNoTeamMatchesID(unknownTeamID).Error(),
   323  	)
   324  	require.False(t, te.engine.IsTeamMember(referee2))
   325  
   326  	// referee2 joins an existing team.
   327  	expectRefereeJoinedTeamEvent(t, te)
   328  	referee2JoiningDate := time.Now()
   329  	te.timeService.EXPECT().GetTimeNow().Return(referee2JoiningDate).Times(1)
   330  	require.NoError(t, te.engine.JoinTeam(ctx, referee2, joinTeamCmd(t, teamID1)))
   331  	require.True(t, te.engine.IsTeamMember(referee2))
   332  
   333  	// referee2 just joined another team and want to move on next epoch.
   334  	require.NoError(t, te.engine.JoinTeam(ctx, referee2, joinTeamCmd(t, teamID2)))
   335  	require.True(t, te.engine.IsTeamMember(referee2))
   336  
   337  	// This shows the referee2 joined the first team he applied to, despite
   338  	// his second application to team 2.
   339  	assertEqualTeams(t, []types.Team{
   340  		{
   341  			ID: teamID1,
   342  			Referrer: &types.Membership{
   343  				PartyID:        referrer1,
   344  				JoinedAt:       team1CreationDate,
   345  				StartedAtEpoch: te.currentEpoch,
   346  			},
   347  			Referees: []*types.Membership{
   348  				{
   349  					PartyID:        referee1,
   350  					JoinedAt:       referee1JoiningDate,
   351  					StartedAtEpoch: te.currentEpoch,
   352  				}, {
   353  					PartyID:        referee2,
   354  					JoinedAt:       referee2JoiningDate,
   355  					StartedAtEpoch: te.currentEpoch,
   356  				},
   357  			},
   358  			Name:      team1Name,
   359  			CreatedAt: team1CreationDate,
   360  		}, {
   361  			ID: teamID2,
   362  			Referrer: &types.Membership{
   363  				PartyID:        referrer2,
   364  				JoinedAt:       team2CreationDate,
   365  				StartedAtEpoch: te.currentEpoch,
   366  			},
   367  			Name:      team2Name,
   368  			CreatedAt: team2CreationDate,
   369  		},
   370  	}, te.engine.ListTeams())
   371  
   372  	// Simulating moving to next epoch.
   373  	expectRefereeSwitchedTeamEvent(t, te)
   374  	referee2JoiningDate2 := time.Now()
   375  	nextEpoch(t, ctx, te, referee2JoiningDate2)
   376  
   377  	// This shows the referee2 moved from team 1 to team 2.
   378  	assertEqualTeams(t, []types.Team{
   379  		{
   380  			ID: teamID1,
   381  			Referrer: &types.Membership{
   382  				PartyID:        referrer1,
   383  				JoinedAt:       team1CreationDate,
   384  				StartedAtEpoch: te.currentEpoch - 1,
   385  			},
   386  			Referees: []*types.Membership{
   387  				{
   388  					PartyID:        referee1,
   389  					JoinedAt:       referee1JoiningDate,
   390  					StartedAtEpoch: te.currentEpoch - 1,
   391  				},
   392  			},
   393  			Name:      team1Name,
   394  			CreatedAt: team1CreationDate,
   395  		}, {
   396  			ID: teamID2,
   397  			Referrer: &types.Membership{
   398  				PartyID:        referrer2,
   399  				JoinedAt:       team2CreationDate,
   400  				StartedAtEpoch: te.currentEpoch - 1,
   401  			},
   402  			Referees: []*types.Membership{
   403  				{
   404  					PartyID:        referee2,
   405  					JoinedAt:       referee2JoiningDate2,
   406  					StartedAtEpoch: te.currentEpoch,
   407  				},
   408  			},
   409  			Name:      team2Name,
   410  			CreatedAt: team2CreationDate,
   411  		},
   412  	}, te.engine.ListTeams())
   413  
   414  	// referee2 just re-joins team 1.
   415  	referee2JoiningDate3 := time.Now()
   416  	require.NoError(t, te.engine.JoinTeam(ctx, referee2, joinTeamCmd(t, teamID1)))
   417  	require.True(t, te.engine.IsTeamMember(referee2))
   418  
   419  	// Simulating moving to next epoch.
   420  	expectRefereeSwitchedTeamEvent(t, te)
   421  	nextEpoch(t, ctx, te, referee2JoiningDate3)
   422  
   423  	// This shows the referee2 moved from team 1 to team 2.
   424  	assertEqualTeams(t, []types.Team{
   425  		{
   426  			ID: teamID1,
   427  			Referrer: &types.Membership{
   428  				PartyID:        referrer1,
   429  				JoinedAt:       team1CreationDate,
   430  				StartedAtEpoch: te.currentEpoch - 2,
   431  			},
   432  			Referees: []*types.Membership{
   433  				{
   434  					PartyID:        referee1,
   435  					JoinedAt:       referee1JoiningDate,
   436  					StartedAtEpoch: te.currentEpoch - 2,
   437  				}, {
   438  					PartyID:        referee2,
   439  					JoinedAt:       referee2JoiningDate3,
   440  					StartedAtEpoch: te.currentEpoch,
   441  				},
   442  			},
   443  			Name:      team1Name,
   444  			CreatedAt: team1CreationDate,
   445  		}, {
   446  			ID: teamID2,
   447  			Referrer: &types.Membership{
   448  				PartyID:        referrer2,
   449  				JoinedAt:       team2CreationDate,
   450  				StartedAtEpoch: te.currentEpoch - 2,
   451  			},
   452  			Referees:  []*types.Membership{},
   453  			Name:      team2Name,
   454  			CreatedAt: team2CreationDate,
   455  		},
   456  	}, te.engine.ListTeams())
   457  
   458  	expectTeamUpdatedEvent(t, te)
   459  
   460  	// Closing the team.
   461  	require.NoError(t, te.engine.UpdateTeam(ctx, referrer2, teamID2, updateTeamCmd(t, "", "", "", true, nil)))
   462  
   463  	// referee2 try to re-join team 2, but joining a closed team without allow-list is disallow.
   464  	require.Error(t, te.engine.JoinTeam(ctx, referee2, joinTeamCmd(t, teamID2)))
   465  
   466  	// Simulating moving to next epoch.
   467  	nextEpoch(t, ctx, te, referee2JoiningDate3)
   468  
   469  	// This shows the referee2 stayed in team 1.
   470  	assertEqualTeams(t, []types.Team{
   471  		{
   472  			ID: teamID1,
   473  			Referrer: &types.Membership{
   474  				PartyID:        referrer1,
   475  				JoinedAt:       team1CreationDate,
   476  				StartedAtEpoch: te.currentEpoch - 3,
   477  			},
   478  			Referees: []*types.Membership{
   479  				{
   480  					PartyID:        referee1,
   481  					JoinedAt:       referee1JoiningDate,
   482  					StartedAtEpoch: te.currentEpoch - 3,
   483  				}, {
   484  					PartyID:        referee2,
   485  					JoinedAt:       referee2JoiningDate3,
   486  					StartedAtEpoch: te.currentEpoch - 1,
   487  				},
   488  			},
   489  			Name:      team1Name,
   490  			CreatedAt: team1CreationDate,
   491  		}, {
   492  			ID: teamID2,
   493  			Referrer: &types.Membership{
   494  				PartyID:        referrer2,
   495  				JoinedAt:       team2CreationDate,
   496  				StartedAtEpoch: te.currentEpoch - 3,
   497  			},
   498  			Referees:  []*types.Membership{},
   499  			Name:      team2Name,
   500  			CreatedAt: team2CreationDate,
   501  			Closed:    true,
   502  		},
   503  	}, te.engine.ListTeams())
   504  
   505  	// Allow referee2 to join the closed team.
   506  	expectTeamUpdatedEvent(t, te)
   507  	require.NoError(t, te.engine.UpdateTeam(ctx, referrer2, teamID2, updateTeamCmd(t, "", "", "", true, []string{referee2.String()})))
   508  
   509  	// referee2 can re-join team 2, because that party is specified in allow-list.
   510  	require.NoError(t, te.engine.JoinTeam(ctx, referee2, joinTeamCmd(t, teamID2)))
   511  
   512  	// Simulating moving to next epoch.
   513  	expectRefereeSwitchedTeamEvent(t, te)
   514  	nextEpoch(t, ctx, te, referee2JoiningDate3)
   515  
   516  	// This shows the referee2 moved to team 2.
   517  	assertEqualTeams(t, []types.Team{
   518  		{
   519  			ID: teamID1,
   520  			Referrer: &types.Membership{
   521  				PartyID:        referrer1,
   522  				JoinedAt:       team1CreationDate,
   523  				StartedAtEpoch: te.currentEpoch - 4,
   524  			},
   525  			Referees: []*types.Membership{
   526  				{
   527  					PartyID:        referee1,
   528  					JoinedAt:       referee1JoiningDate,
   529  					StartedAtEpoch: te.currentEpoch - 4,
   530  				},
   531  			},
   532  			Name:      team1Name,
   533  			CreatedAt: team1CreationDate,
   534  		}, {
   535  			ID: teamID2,
   536  			Referrer: &types.Membership{
   537  				PartyID:        referrer2,
   538  				JoinedAt:       team2CreationDate,
   539  				StartedAtEpoch: te.currentEpoch - 4,
   540  			},
   541  			Referees: []*types.Membership{
   542  				{
   543  					PartyID:        referee2,
   544  					JoinedAt:       referee2JoiningDate3,
   545  					StartedAtEpoch: te.currentEpoch,
   546  				},
   547  			},
   548  			Name:      team2Name,
   549  			CreatedAt: team2CreationDate,
   550  			Closed:    true,
   551  			AllowList: []types.PartyID{referee2},
   552  		},
   553  	}, te.engine.ListTeams())
   554  
   555  	// referee1 cannot join team 2, because that party is not specified in allow-list.
   556  	require.Error(t, te.engine.JoinTeam(ctx, referee1, joinTeamCmd(t, teamID2)))
   557  
   558  	// Simulating moving to next epoch.
   559  	nextEpoch(t, ctx, te, referee2JoiningDate3)
   560  
   561  	// This shows the referee1 did not moved to team 2.
   562  	assertEqualTeams(t, []types.Team{
   563  		{
   564  			ID: teamID1,
   565  			Referrer: &types.Membership{
   566  				PartyID:        referrer1,
   567  				JoinedAt:       team1CreationDate,
   568  				StartedAtEpoch: te.currentEpoch - 5,
   569  			},
   570  			Referees: []*types.Membership{
   571  				{
   572  					PartyID:        referee1,
   573  					JoinedAt:       referee1JoiningDate,
   574  					StartedAtEpoch: te.currentEpoch - 5,
   575  				},
   576  			},
   577  			Name:      team1Name,
   578  			CreatedAt: team1CreationDate,
   579  		}, {
   580  			ID: teamID2,
   581  			Referrer: &types.Membership{
   582  				PartyID:        referrer2,
   583  				JoinedAt:       team2CreationDate,
   584  				StartedAtEpoch: te.currentEpoch - 5,
   585  			},
   586  			Referees: []*types.Membership{
   587  				{
   588  					PartyID:        referee2,
   589  					JoinedAt:       referee2JoiningDate3,
   590  					StartedAtEpoch: te.currentEpoch - 1,
   591  				},
   592  			},
   593  			Name:      team2Name,
   594  			CreatedAt: team2CreationDate,
   595  			Closed:    true,
   596  			AllowList: []types.PartyID{referee2},
   597  		},
   598  	}, te.engine.ListTeams())
   599  
   600  	referee4 := newPartyID(t)
   601  
   602  	team3CreationDate := time.Now()
   603  	te.timeService.EXPECT().GetTimeNow().Return(team3CreationDate).Times(1)
   604  	team3Name := vgrand.RandomStr(5)
   605  	teamID3, referrer3 := newTeamWithCmd(t, ctx, te, &commandspb.CreateReferralSet_Team{
   606  		Name:      team3Name,
   607  		Closed:    true,
   608  		AllowList: []string{referee4.String()},
   609  	})
   610  
   611  	expectRefereeJoinedTeamEvent(t, te)
   612  	referee4JoiningDate := time.Now()
   613  	te.timeService.EXPECT().GetTimeNow().Return(referee4JoiningDate).Times(1)
   614  	require.NoError(t, te.engine.JoinTeam(ctx, referee4, joinTeamCmd(t, teamID3)))
   615  	require.True(t, te.engine.IsTeamMember(referee4))
   616  
   617  	// This shows the referee1 did not moved to team 2.
   618  	assertEqualTeams(t, []types.Team{
   619  		{
   620  			ID: teamID1,
   621  			Referrer: &types.Membership{
   622  				PartyID:        referrer1,
   623  				JoinedAt:       team1CreationDate,
   624  				StartedAtEpoch: te.currentEpoch - 5,
   625  			},
   626  			Referees: []*types.Membership{
   627  				{
   628  					PartyID:        referee1,
   629  					JoinedAt:       referee1JoiningDate,
   630  					StartedAtEpoch: te.currentEpoch - 5,
   631  				},
   632  			},
   633  			Name:      team1Name,
   634  			CreatedAt: team1CreationDate,
   635  		}, {
   636  			ID: teamID2,
   637  			Referrer: &types.Membership{
   638  				PartyID:        referrer2,
   639  				JoinedAt:       team2CreationDate,
   640  				StartedAtEpoch: te.currentEpoch - 5,
   641  			},
   642  			Referees: []*types.Membership{
   643  				{
   644  					PartyID:        referee2,
   645  					JoinedAt:       referee2JoiningDate3,
   646  					StartedAtEpoch: te.currentEpoch - 1,
   647  				},
   648  			},
   649  			Name:      team2Name,
   650  			CreatedAt: team2CreationDate,
   651  			Closed:    true,
   652  			AllowList: []types.PartyID{referee2},
   653  		}, {
   654  			ID: teamID3,
   655  			Referrer: &types.Membership{
   656  				PartyID:        referrer3,
   657  				JoinedAt:       team3CreationDate,
   658  				StartedAtEpoch: te.currentEpoch,
   659  			},
   660  			Referees: []*types.Membership{
   661  				{
   662  					PartyID:        referee4,
   663  					JoinedAt:       referee4JoiningDate,
   664  					StartedAtEpoch: te.currentEpoch,
   665  				},
   666  			},
   667  			Name:      team3Name,
   668  			CreatedAt: team3CreationDate,
   669  			Closed:    true,
   670  			AllowList: []types.PartyID{referee4},
   671  		},
   672  	}, te.engine.ListTeams())
   673  }
   674  
   675  func testMinEpochsRequired(t *testing.T) {
   676  	ctx := vgtest.VegaContext(vgrand.RandomStr(5), vgtest.RandomI64())
   677  
   678  	te := newEngine(t)
   679  
   680  	team1CreationDate := time.Now()
   681  	te.timeService.EXPECT().GetTimeNow().Return(team1CreationDate).Times(1)
   682  	teamID1, _, _ := newTeam(t, ctx, te)
   683  
   684  	// if we set min epochs to 0 then we see the referrer
   685  	members := te.engine.GetTeamMembers(string(teamID1), 0)
   686  	assert.Len(t, members, 1)
   687  
   688  	// referrer made a team, but does not get returned as a team member until its been the minimum epochs
   689  	members = te.engine.GetTeamMembers(string(teamID1), 5)
   690  	assert.Len(t, members, 0)
   691  
   692  	// move to epoch 11 and add a team member
   693  	te.engine.OnEpoch(ctx, types.Epoch{Seq: 11, Action: vegapb.EpochAction_EPOCH_ACTION_START})
   694  
   695  	expectRefereeJoinedTeamEvent(t, te)
   696  	referee := newPartyID(t)
   697  	refereeJoiningDate := time.Now()
   698  	te.timeService.EXPECT().GetTimeNow().Return(refereeJoiningDate).Times(1)
   699  	require.NoError(t, te.engine.JoinTeam(ctx, referee, joinTeamCmd(t, teamID1)))
   700  
   701  	// referrer joined at epoch 10, team member at 11 lets move to epoch 15
   702  	te.engine.OnEpoch(ctx, types.Epoch{Seq: 15, Action: vegapb.EpochAction_EPOCH_ACTION_START})
   703  	members = te.engine.GetTeamMembers(string(teamID1), 5)
   704  	assert.Len(t, members, 1)
   705  
   706  	// now at epoch 16 both should be there
   707  	te.engine.OnEpoch(ctx, types.Epoch{Seq: 16, Action: vegapb.EpochAction_EPOCH_ACTION_START})
   708  	members = te.engine.GetTeamMembers(string(teamID1), 5)
   709  	assert.Len(t, members, 2)
   710  }
   711  
   712  func TestRemoveFromAllowListRemoveFromTheTeam(t *testing.T) {
   713  	ctx := vgtest.VegaContext(vgrand.RandomStr(5), vgtest.RandomI64())
   714  
   715  	te := newEngine(t)
   716  
   717  	require.False(t, te.engine.TeamExists(newTeamID(t)))
   718  
   719  	referrer1 := newPartyID(t)
   720  	referee1 := newPartyID(t)
   721  	teamID1 := newTeamID(t)
   722  	name := vgrand.RandomStr(5)
   723  	teamURL := "https://" + name + ".io"
   724  	avatarURL := "https://avatar." + name + ".io"
   725  
   726  	// create the team
   727  	expectTeamCreatedEvent(t, te)
   728  	team1CreationDate := time.Now()
   729  	te.timeService.EXPECT().GetTimeNow().Return(team1CreationDate).Times(1)
   730  
   731  	te.engine.OnEpoch(ctx, types.Epoch{Seq: 1, Action: vegapb.EpochAction_EPOCH_ACTION_START})
   732  
   733  	require.NoError(t, te.engine.CreateTeam(ctx, referrer1, teamID1,
   734  		createTeamWithAllowListCmd(t, name, teamURL, avatarURL, true, []string{referee1.String()})))
   735  	require.True(t, te.engine.TeamExists(teamID1))
   736  
   737  	// referee join the team
   738  	expectRefereeJoinedTeamEvent(t, te)
   739  	te.timeService.EXPECT().GetTimeNow().Return(team1CreationDate.Add(10 * time.Second)).Times(1)
   740  
   741  	require.NoError(t, te.engine.JoinTeam(ctx, referee1, joinTeamCmd(t, teamID1)))
   742  	require.True(t, te.engine.IsTeamMember(referee1))
   743  	// two members in the team
   744  	assert.Len(t, te.engine.GetTeamMembers(string(teamID1), 0), 2)
   745  
   746  	te.engine.OnEpoch(ctx, types.Epoch{Seq: 2, Action: vegapb.EpochAction_EPOCH_ACTION_START})
   747  
   748  	// referrer update the team to remove all allowlisted parties
   749  	expectTeamUpdatedEvent(t, te)
   750  	// te.timeService.EXPECT().GetTimeNow().Return(team1CreationDate.Add(20 * time.Second)).Times(1)
   751  	require.NoError(t, te.engine.UpdateTeam(ctx, referrer1, teamID1,
   752  		updateTeamCmd(t, name, teamURL, avatarURL, true, []string{})))
   753  	require.True(t, te.engine.TeamExists(teamID1))
   754  
   755  	// move to the next epoch
   756  	expectRefereeSwitchedTeamEvent(t, te)
   757  	te.engine.OnEpoch(ctx, types.Epoch{Seq: 2, Action: vegapb.EpochAction_EPOCH_ACTION_START})
   758  	require.False(t, te.engine.IsTeamMember(referee1))
   759  	// only referrer is team member now
   760  	assert.Len(t, te.engine.GetTeamMembers(string(teamID1), 0), 1)
   761  }