From 901f0cbbf242541aaf64576afd550efe8a7f4f8d Mon Sep 17 00:00:00 2001 From: Mark Mandel Date: Fri, 4 Sep 2026 19:52:57 -0700 Subject: [PATCH] PlayerTracking Removal: Delete Unit Tests Removal of the unit tests for PlayerTracking as part of removing this functionality from Agones. Work on #4491 Signed-off-by: Mark Mandel --- pkg/apis/agones/v1/gameserver_test.go | 43 ---- pkg/fleets/controller_test.go | 51 ---- pkg/gameserversets/controller_test.go | 34 --- pkg/sdkserver/localsdk_test.go | 310 ----------------------- pkg/sdkserver/sdk_test.go | 29 --- pkg/sdkserver/sdkserver_test.go | 337 -------------------------- 6 files changed, 804 deletions(-) diff --git a/pkg/apis/agones/v1/gameserver_test.go b/pkg/apis/agones/v1/gameserver_test.go index 7fd85c35db..1fa579ab3d 100644 --- a/pkg/apis/agones/v1/gameserver_test.go +++ b/pkg/apis/agones/v1/gameserver_test.go @@ -140,8 +140,6 @@ func TestIsBeingDeleted(t *testing.T) { func TestGameServerApplyDefaults(t *testing.T) { t.Parallel() - ten := int64(10) - defaultGameServerAnd := func(f func(gss *GameServerSpec)) GameServer { gs := GameServer{ Spec: GameServerSpec{ @@ -206,15 +204,6 @@ func TestGameServerApplyDefaults(t *testing.T) { gameServer: defaultGameServerAnd(func(_ *GameServerSpec) {}), expected: wantDefaultAnd(func(_ *expected) {}), }, - "PlayerTracking=true": { - featureFlags: string(runtime.FeaturePlayerTracking) + "=true", - gameServer: defaultGameServerAnd(func(gss *GameServerSpec) { - gss.Players = &PlayersSpec{InitialCapacity: 10} - }), - expected: wantDefaultAnd(func(e *expected) { - e.alphaPlayerCapacity = &ten - }), - }, "CountsAndLists=true, Counters": { featureFlags: string(runtime.FeatureCountsAndLists) + "=true", gameServer: defaultGameServerAnd(func(gss *GameServerSpec) { @@ -1437,38 +1426,6 @@ func TestGameServerValidateFeatures(t *testing.T) { }, }, }, - { - description: "PlayerTracking is disabled, Players field specified", - feature: fmt.Sprintf("%s=false", runtime.FeaturePlayerTracking), - gs: GameServer{ - Spec: GameServerSpec{ - Container: "testing", - Players: &PlayersSpec{InitialCapacity: 10}, - Template: corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "testing", Image: "testing/image"}}}, - }, - }, - }, - want: field.ErrorList{ - field.Forbidden( - field.NewPath("spec", "players"), - "Value cannot be set unless feature flag PlayerTracking is enabled", - ), - }, - }, - { - description: "PlayerTracking is enabled, Players field specified", - feature: fmt.Sprintf("%s=true", runtime.FeaturePlayerTracking), - gs: GameServer{ - Spec: GameServerSpec{ - Container: "testing", - Players: &PlayersSpec{InitialCapacity: 10}, - Template: corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "testing", Image: "testing/image"}}}, - }, - }, - }, - }, { description: "CountsAndLists is disabled, Counters field specified", feature: fmt.Sprintf("%s=false", runtime.FeatureCountsAndLists), diff --git a/pkg/fleets/controller_test.go b/pkg/fleets/controller_test.go index b02bdf7c1b..ddf66b27f8 100644 --- a/pkg/fleets/controller_test.go +++ b/pkg/fleets/controller_test.go @@ -569,57 +569,6 @@ func TestControllerUpdateFleetStatus(t *testing.T) { } -func TestControllerUpdateFleetPlayerStatus(t *testing.T) { - t.Parallel() - - utilruntime.FeatureTestMutex.Lock() - defer utilruntime.FeatureTestMutex.Unlock() - - require.NoError(t, utilruntime.ParseFeatures(string(utilruntime.FeaturePlayerTracking)+"=true")) - - fleet := defaultFixture() - c, m := newFakeController() - - gsSet1 := fleet.GameServerSet() - gsSet1.ObjectMeta.Name = "gsSet1" - gsSet1.Status.Players = &agonesv1.AggregatedPlayerStatus{ - Count: 5, - Capacity: 10, - } - - gsSet2 := fleet.GameServerSet() - gsSet2.ObjectMeta.Name = "gsSet2" - gsSet2.Status.Players = &agonesv1.AggregatedPlayerStatus{ - Count: 10, - Capacity: 20, - } - - m.AgonesClient.AddReactor("list", "gameserversets", - func(_ k8stesting.Action) (bool, runtime.Object, error) { - return true, &agonesv1.GameServerSetList{Items: []agonesv1.GameServerSet{*gsSet1, *gsSet2}}, nil - }) - - updated := false - m.AgonesClient.AddReactor("update", "fleets", - func(action k8stesting.Action) (bool, runtime.Object, error) { - updated = true - ua := action.(k8stesting.UpdateAction) - fleet := ua.GetObject().(*agonesv1.Fleet) - - assert.Equal(t, gsSet1.Status.Players.Count+gsSet2.Status.Players.Count, fleet.Status.Players.Count) - assert.Equal(t, gsSet1.Status.Players.Capacity+gsSet2.Status.Players.Capacity, fleet.Status.Players.Capacity) - - return true, fleet, nil - }) - - ctx, cancel := agtesting.StartInformers(m, c.fleetSynced, c.gameServerSetSynced) - defer cancel() - - err := c.updateFleetStatus(ctx, fleet) - assert.NoError(t, err) - assert.True(t, updated) -} - // nolint:dupl // Linter errors on lines are duplicate of TestControllerUpdateFleetListStatus func TestControllerUpdateFleetCounterStatus(t *testing.T) { t.Parallel() diff --git a/pkg/gameserversets/controller_test.go b/pkg/gameserversets/controller_test.go index 2dd44e7191..eb314d1876 100644 --- a/pkg/gameserversets/controller_test.go +++ b/pkg/gameserversets/controller_test.go @@ -314,40 +314,6 @@ func TestComputeStatus(t *testing.T) { } }) - t.Run("player tracking", func(t *testing.T) { - utilruntime.FeatureTestMutex.Lock() - defer utilruntime.FeatureTestMutex.Unlock() - - require.NoError(t, utilruntime.ParseFeatures(fmt.Sprintf("%s=true", utilruntime.FeaturePlayerTracking))) - - gsSet := defaultFixture() - var list []*agonesv1.GameServer - gs1 := gsWithState(agonesv1.GameServerStateAllocated) - gs1.Status.Players = &agonesv1.PlayerStatus{Count: 5, Capacity: 10} - gs2 := gsWithState(agonesv1.GameServerStateReserved) - gs2.Status.Players = &agonesv1.PlayerStatus{Count: 10, Capacity: 15} - gs3 := gsWithState(agonesv1.GameServerStateCreating) - gs3.Status.Players = &agonesv1.PlayerStatus{Count: 20, Capacity: 30} - gs4 := gsWithState(agonesv1.GameServerStateReady) - gs4.Status.Players = &agonesv1.PlayerStatus{Count: 15, Capacity: 30} - list = append(list, gs1, gs2, gs3, gs4) - - expected := agonesv1.GameServerSetStatus{ - Replicas: 4, - ReadyReplicas: 1, - ReservedReplicas: 1, - AllocatedReplicas: 1, - Players: &agonesv1.AggregatedPlayerStatus{ - Count: 30, - Capacity: 55, - }, - Counters: map[string]agonesv1.AggregatedCounterStatus{}, - Lists: map[string]agonesv1.AggregatedListStatus{}, - } - - assert.Equal(t, expected, computeStatus(gsSet, list)) - }) - t.Run("counters", func(t *testing.T) { utilruntime.FeatureTestMutex.Lock() defer utilruntime.FeatureTestMutex.Unlock() diff --git a/pkg/sdkserver/localsdk_test.go b/pkg/sdkserver/localsdk_test.go index c1e931fcdf..a5703977f4 100644 --- a/pkg/sdkserver/localsdk_test.go +++ b/pkg/sdkserver/localsdk_test.go @@ -19,7 +19,6 @@ import ( "encoding/json" "fmt" "os" - "strconv" "sync" "testing" "time" @@ -36,7 +35,6 @@ import ( agonesv1 "agones.dev/agones/pkg/apis/agones/v1" "agones.dev/agones/pkg/sdk" - "agones.dev/agones/pkg/sdk/alpha" "agones.dev/agones/pkg/sdk/beta" "agones.dev/agones/pkg/util/runtime" ) @@ -321,294 +319,6 @@ func TestLocalSDKServerWatchGameServer(t *testing.T) { }) } -func TestLocalSDKServerPlayerCapacity(t *testing.T) { - t.Parallel() - - runtime.FeatureTestMutex.Lock() - defer runtime.FeatureTestMutex.Unlock() - require.NoError(t, runtime.ParseFeatures(string(runtime.FeaturePlayerTracking)+"=true")) - - fixture := &agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "stuff"}} - - e := &alpha.Empty{} - path, err := gsToTmpFile(fixture) - assert.NoError(t, err) - l, err := NewLocalSDKServer(path, "") - assert.NoError(t, err) - - stream := newGameServerMockStream() - go func() { - err := l.WatchGameServer(&sdk.Empty{}, stream) - assert.NoError(t, err) - }() - assertInitialWatchUpdate(t, stream) - - // wait for watching to begin - err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { - found := false - l.updateObservers.Range(func(_, _ interface{}) bool { - found = true - return false - }) - return found, nil - }) - assert.NoError(t, err) - - c, err := l.GetPlayerCapacity(context.Background(), e) - assert.NoError(t, err) - assert.Equal(t, int64(0), c.Count) - - _, err = l.SetPlayerCapacity(context.Background(), &alpha.Count{Count: 10}) - assert.NoError(t, err) - - select { - case msg := <-stream.msgs: - assert.Equal(t, int64(10), msg.Status.Players.Capacity) - case <-time.After(10 * time.Second): - assert.Fail(t, "timeout getting watch") - } - - c, err = l.GetPlayerCapacity(context.Background(), e) - assert.NoError(t, err) - assert.Equal(t, int64(10), c.Count) - - gs, err := l.GetGameServer(context.Background(), &sdk.Empty{}) - assert.NoError(t, err) - assert.Equal(t, int64(10), gs.Status.Players.Capacity) -} - -func TestLocalSDKServerPlayerConnectAndDisconnectWithoutPlayerTracking(t *testing.T) { - t.Parallel() - runtime.FeatureTestMutex.Lock() - defer runtime.FeatureTestMutex.Unlock() - - require.NoError(t, runtime.ParseFeatures(string(runtime.FeaturePlayerTracking)+"=false")) - - l, err := NewLocalSDKServer("", "") - assert.NoError(t, err) - - e := &alpha.Empty{} - capacity, err := l.GetPlayerCapacity(context.Background(), e) - assert.Nil(t, capacity) - assert.Error(t, err) - - count, err := l.GetPlayerCount(context.Background(), e) - assert.Error(t, err) - assert.Nil(t, count) - - list, err := l.GetConnectedPlayers(context.Background(), e) - assert.Error(t, err) - assert.Nil(t, list) - - id := &alpha.PlayerID{PlayerID: "test-player"} - - ok, err := l.PlayerConnect(context.Background(), id) - assert.Error(t, err) - assert.False(t, ok.Bool) - - ok, err = l.IsPlayerConnected(context.Background(), id) - assert.Error(t, err) - assert.False(t, ok.Bool) - - ok, err = l.PlayerDisconnect(context.Background(), id) - assert.Error(t, err) - assert.False(t, ok.Bool) -} - -func TestLocalSDKServerPlayerConnectAndDisconnect(t *testing.T) { - t.Parallel() - - runtime.FeatureTestMutex.Lock() - defer runtime.FeatureTestMutex.Unlock() - require.NoError(t, runtime.ParseFeatures(string(runtime.FeaturePlayerTracking)+"=true")) - - gs := func() *agonesv1.GameServer { - return &agonesv1.GameServer{ - ObjectMeta: metav1.ObjectMeta{Name: "stuff"}, - Status: agonesv1.GameServerStatus{ - Players: &agonesv1.PlayerStatus{ - Capacity: 1, - }, - }} - } - - e := &alpha.Empty{} - - fixtures := map[string]struct { - testMode bool - gs *agonesv1.GameServer - useFile bool - }{ - "test mode on, gs with Status.Players": { - testMode: true, - gs: gs(), - useFile: true, - }, - "test mode off, gs with Status.Players": { - testMode: false, - gs: gs(), - useFile: true, - }, - "test mode on, gs without Status.Players": { - testMode: true, - useFile: true, - }, - "test mode off, gs without Status.Players": { - testMode: false, - useFile: true, - }, - "test mode on, no filePath": { - testMode: true, - useFile: false, - }, - "test mode off, no filePath": { - testMode: false, - useFile: false, - }, - } - - for k, v := range fixtures { - t.Run(k, func(t *testing.T) { - var l *LocalSDKServer - var err error - if v.useFile { - path, pathErr := gsToTmpFile(v.gs) - assert.NoError(t, pathErr) - l, err = NewLocalSDKServer(path, "") - } else { - l, err = NewLocalSDKServer("", "") - } - assert.NoError(t, err) - l.SetTestMode(v.testMode) - - stream := newGameServerMockStream() - go func() { - err := l.WatchGameServer(&sdk.Empty{}, stream) - assert.NoError(t, err) - }() - assertInitialWatchUpdate(t, stream) - - // wait for watching to begin - err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { - found := false - l.updateObservers.Range(func(_, _ interface{}) bool { - found = true - return false - }) - return found, nil - }) - assert.NoError(t, err) - - if !v.useFile || v.gs == nil { - _, err := l.SetPlayerCapacity(context.Background(), &alpha.Count{ - Count: 1, - }) - assert.NoError(t, err) - expected := &sdk.GameServer_Status_PlayerStatus{ - Capacity: 1, - } - assertWatchUpdate(t, stream, expected, func(gs *sdk.GameServer) interface{} { - return gs.Status.Players - }) - } - - id := &alpha.PlayerID{PlayerID: "one"} - ok, err := l.IsPlayerConnected(context.Background(), id) - assert.NoError(t, err) - if assert.NotNil(t, ok) { - assert.False(t, ok.Bool, "player should not be connected") - } - - count, err := l.GetPlayerCount(context.Background(), e) - assert.NoError(t, err) - assert.Equal(t, int64(0), count.Count) - - list, err := l.GetConnectedPlayers(context.Background(), e) - assert.NoError(t, err) - assert.Empty(t, list.List) - - // connect a player - ok, err = l.PlayerConnect(context.Background(), id) - assert.NoError(t, err) - assert.True(t, ok.Bool, "Player should not exist yet") - - count, err = l.GetPlayerCount(context.Background(), e) - assert.NoError(t, err) - assert.Equal(t, int64(1), count.Count) - - expected := &sdk.GameServer_Status_PlayerStatus{ - Count: 1, - Capacity: 1, - Ids: []string{id.PlayerID}, - } - assertWatchUpdate(t, stream, expected, func(gs *sdk.GameServer) interface{} { - return gs.Status.Players - }) - - ok, err = l.IsPlayerConnected(context.Background(), id) - assert.NoError(t, err) - assert.True(t, ok.Bool, "player should be connected") - - list, err = l.GetConnectedPlayers(context.Background(), e) - assert.NoError(t, err) - assert.Equal(t, []string{id.PlayerID}, list.List) - - // add same player - ok, err = l.PlayerConnect(context.Background(), id) - assert.NoError(t, err) - assert.False(t, ok.Bool, "Player already exists") - - count, err = l.GetPlayerCount(context.Background(), e) - assert.NoError(t, err) - assert.Equal(t, int64(1), count.Count) - assertNoWatchUpdate(t, stream) - - list, err = l.GetConnectedPlayers(context.Background(), e) - assert.NoError(t, err) - assert.Equal(t, []string{id.PlayerID}, list.List) - - // should return an error if we try to add another, since we're at capacity - nopePlayer := &alpha.PlayerID{PlayerID: "nope"} - _, err = l.PlayerConnect(context.Background(), nopePlayer) - assert.EqualError(t, err, "Players are already at capacity") - - ok, err = l.IsPlayerConnected(context.Background(), nopePlayer) - assert.NoError(t, err) - assert.False(t, ok.Bool) - - // disconnect a player - ok, err = l.PlayerDisconnect(context.Background(), id) - assert.NoError(t, err) - assert.True(t, ok.Bool, "Player should be removed") - count, err = l.GetPlayerCount(context.Background(), e) - assert.NoError(t, err) - assert.Equal(t, int64(0), count.Count) - - expected = &sdk.GameServer_Status_PlayerStatus{ - Count: 0, - Capacity: 1, - Ids: []string{}, - } - assertWatchUpdate(t, stream, expected, func(gs *sdk.GameServer) interface{} { - return gs.Status.Players - }) - - list, err = l.GetConnectedPlayers(context.Background(), e) - assert.NoError(t, err) - assert.Empty(t, list.List) - - // remove same player - ok, err = l.PlayerDisconnect(context.Background(), id) - assert.NoError(t, err) - assert.False(t, ok.Bool, "Player already be gone") - count, err = l.GetPlayerCount(context.Background(), e) - assert.NoError(t, err) - assert.Equal(t, int64(0), count.Count) - assertNoWatchUpdate(t, stream) - }) - } -} - func TestLocalSDKServerGetCounter(t *testing.T) { t.Parallel() @@ -1357,26 +1067,6 @@ func TestSDKConformanceFunctionality(t *testing.T) { assert.True(t, b, "we should receive strings from all go routines %v %v", l.expectedSequence, l.requestSequence) } -func TestAlphaSDKConformanceFunctionality(t *testing.T) { - t.Parallel() - lStable, err := NewLocalSDKServer("", "") - assert.NoError(t, err) - v := int64(0) - lStable.recordRequestWithValue("setplayercapacity", strconv.FormatInt(v, 10), "PlayerCapacity") - lStable.recordRequestWithValue("isplayerconnected", "", "PlayerIDs") - - runtime.FeatureTestMutex.Lock() - defer runtime.FeatureTestMutex.Unlock() - - require.NoError(t, runtime.ParseFeatures(string(runtime.FeaturePlayerTracking)+"=true")) - l, err := NewLocalSDKServer("", "") - assert.NoError(t, err) - l.testMode = true - l.recordRequestWithValue("setplayercapacity", strconv.FormatInt(v, 10), "PlayerCapacity") - l.recordRequestWithValue("isplayerconnected", "", "PlayerIDs") - -} - func gsToTmpFile(gs *agonesv1.GameServer) (string, error) { file, err := os.CreateTemp(os.TempDir(), "gameserver-") if err != nil { diff --git a/pkg/sdkserver/sdk_test.go b/pkg/sdkserver/sdk_test.go index bbd96041ab..bae197f149 100644 --- a/pkg/sdkserver/sdk_test.go +++ b/pkg/sdkserver/sdk_test.go @@ -80,35 +80,6 @@ func TestConvert(t *testing.T) { } } - t.Run(string(runtime.FeaturePlayerTracking)+" disabled", func(t *testing.T) { - runtime.FeatureTestMutex.Lock() - defer runtime.FeatureTestMutex.Unlock() - require.NoError(t, runtime.ParseFeatures("")) - - gs := fixture.DeepCopy() - - sdkGs := convert(gs) - eq(t, fixture, sdkGs) - assert.Zero(t, sdkGs.ObjectMeta.DeletionTimestamp) - assert.Nil(t, sdkGs.Status.Players) - }) - - t.Run(string(runtime.FeaturePlayerTracking)+" enabled", func(t *testing.T) { - runtime.FeatureTestMutex.Lock() - defer runtime.FeatureTestMutex.Unlock() - require.NoError(t, runtime.ParseFeatures(string(runtime.FeaturePlayerTracking)+"=true")) - - gs := fixture.DeepCopy() - gs.Status.Players = &agonesv1.PlayerStatus{Capacity: 10, Count: 5, IDs: []string{"one", "two"}} - - sdkGs := convert(gs) - eq(t, fixture, sdkGs) - assert.Zero(t, sdkGs.ObjectMeta.DeletionTimestamp) - assert.Equal(t, gs.Status.Players.Capacity, sdkGs.Status.Players.Capacity) - assert.Equal(t, gs.Status.Players.Count, sdkGs.Status.Players.Count) - assert.Equal(t, gs.Status.Players.IDs, sdkGs.Status.Players.Ids) - }) - t.Run(string(runtime.FeatureCountsAndLists)+" disabled", func(t *testing.T) { runtime.FeatureTestMutex.Lock() defer runtime.FeatureTestMutex.Unlock() diff --git a/pkg/sdkserver/sdkserver_test.go b/pkg/sdkserver/sdkserver_test.go index 96fc50dd60..a03723030b 100644 --- a/pkg/sdkserver/sdkserver_test.go +++ b/pkg/sdkserver/sdkserver_test.go @@ -18,7 +18,6 @@ import ( "context" "encoding/json" "net/http" - "strconv" "strings" "sync" "testing" @@ -30,7 +29,6 @@ import ( agonesv1 "agones.dev/agones/pkg/apis/agones/v1" "agones.dev/agones/pkg/gameserverallocations" "agones.dev/agones/pkg/sdk" - "agones.dev/agones/pkg/sdk/alpha" "agones.dev/agones/pkg/sdk/beta" agtesting "agones.dev/agones/pkg/testing" agruntime "agones.dev/agones/pkg/util/runtime" @@ -1923,341 +1921,6 @@ func TestDeleteValues(t *testing.T) { assert.Len(t, newList, len(list)-len(toDeleteMap)) } -func TestSDKServerPlayerCapacity(t *testing.T) { - t.Parallel() - agruntime.FeatureTestMutex.Lock() - defer agruntime.FeatureTestMutex.Unlock() - - err := agruntime.ParseFeatures(string(agruntime.FeaturePlayerTracking) + "=true") - require.NoError(t, err, "Can not parse FeaturePlayerTracking feature") - - m := agtesting.NewMocks() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - sc, err := defaultSidecar(m) - require.NoError(t, err) - - gs := agonesv1.GameServer{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", Namespace: "default", ResourceVersion: "0", - }, - Spec: agonesv1.GameServerSpec{ - SdkServer: agonesv1.SdkServer{ - LogLevel: "Debug", - }, - Players: &agonesv1.PlayersSpec{ - InitialCapacity: 10, - }, - }, - } - gs.ApplyDefaults() - - m.AgonesClient.AddReactor("list", "gameservers", func(_ k8stesting.Action) (bool, runtime.Object, error) { - return true, &agonesv1.GameServerList{Items: []agonesv1.GameServer{*gs.DeepCopy()}}, nil - }) - - updated := make(chan int64, 10) - m.AgonesClient.AddReactor("patch", "gameservers", func(action k8stesting.Action) (bool, runtime.Object, error) { - - gsCopy := patchGameServer(t, action, &gs) - - updated <- gsCopy.Status.Players.Capacity - return true, gsCopy, nil - }) - - assert.NoError(t, sc.WaitForConnection(ctx)) - sc.informerFactory.Start(ctx.Done()) - assert.True(t, cache.WaitForCacheSync(ctx.Done(), sc.gameServerSynced)) - - go func() { - err = sc.Run(ctx) - assert.NoError(t, err) - }() - - // check initial value comes through - - // async, so check after a period - err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { - count, err := sc.GetPlayerCapacity(context.Background(), &alpha.Empty{}) - return count.Count == 10, err - }) - assert.NoError(t, err) - - // on update from the SDK, the value is available from GetPlayerCapacity - _, err = sc.SetPlayerCapacity(context.Background(), &alpha.Count{Count: 20}) - assert.NoError(t, err) - - count, err := sc.GetPlayerCapacity(context.Background(), &alpha.Empty{}) - require.NoError(t, err) - assert.Equal(t, int64(20), count.Count) - - // on an update, confirm that the update hits the K8s api - select { - case value := <-updated: - assert.Equal(t, int64(20), value) - case <-time.After(time.Minute): - assert.Fail(t, "Should have been patched") - } - - agtesting.AssertEventContains(t, m.FakeRecorder.Events, "PlayerCapacity Set to 20") -} - -func TestSDKServerPlayerConnectAndDisconnectWithoutPlayerTracking(t *testing.T) { - t.Parallel() - agruntime.FeatureTestMutex.Lock() - defer agruntime.FeatureTestMutex.Unlock() - - err := agruntime.ParseFeatures(string(agruntime.FeaturePlayerTracking) + "=false") - require.NoError(t, err, "Can not parse FeaturePlayerTracking feature") - - fixture := &agonesv1.GameServer{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - }, - Status: agonesv1.GameServerStatus{ - State: agonesv1.GameServerStateReady, - }, - } - - m := agtesting.NewMocks() - m.AgonesClient.AddReactor("list", "gameservers", func(_ k8stesting.Action) (bool, runtime.Object, error) { - return true, &agonesv1.GameServerList{Items: []agonesv1.GameServer{*fixture}}, nil - }) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - sc, err := defaultSidecar(m) - require.NoError(t, err) - - assert.NoError(t, sc.WaitForConnection(ctx)) - sc.informerFactory.Start(ctx.Done()) - assert.True(t, cache.WaitForCacheSync(ctx.Done(), sc.gameServerSynced)) - - go func() { - err = sc.Run(ctx) - assert.NoError(t, err) - }() - - // check initial value comes through - // async, so check after a period - e := &alpha.Empty{} - err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { - count, err := sc.GetPlayerCapacity(context.Background(), e) - - assert.Nil(t, count) - return false, err - }) - assert.Error(t, err) - - count, err := sc.GetPlayerCount(context.Background(), e) - require.Error(t, err) - assert.Nil(t, count) - - list, err := sc.GetConnectedPlayers(context.Background(), e) - require.Error(t, err) - assert.Nil(t, list) - - id := &alpha.PlayerID{PlayerID: "test-player"} - - ok, err := sc.PlayerConnect(context.Background(), id) - require.Error(t, err) - assert.False(t, ok.Bool) - - ok, err = sc.IsPlayerConnected(context.Background(), id) - require.Error(t, err) - assert.False(t, ok.Bool) - - ok, err = sc.PlayerDisconnect(context.Background(), id) - require.Error(t, err) - assert.False(t, ok.Bool) -} - -func TestSDKServerPlayerConnectAndDisconnect(t *testing.T) { - t.Parallel() - agruntime.FeatureTestMutex.Lock() - defer agruntime.FeatureTestMutex.Unlock() - - err := agruntime.ParseFeatures(string(agruntime.FeaturePlayerTracking) + "=true") - require.NoError(t, err, "Can not parse FeaturePlayerTracking feature") - - m := agtesting.NewMocks() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - sc, err := defaultSidecar(m) - require.NoError(t, err) - - capacity := int64(3) - gs := agonesv1.GameServer{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", Namespace: "default", ResourceVersion: "0", - }, - Spec: agonesv1.GameServerSpec{ - SdkServer: agonesv1.SdkServer{ - LogLevel: "Debug", - }, - // this is here to give us a reference, so we know when sc.Run() has completed. - Players: &agonesv1.PlayersSpec{ - InitialCapacity: capacity, - }, - }, - } - gs.ApplyDefaults() - - m.AgonesClient.AddReactor("list", "gameservers", func(_ k8stesting.Action) (bool, runtime.Object, error) { - return true, &agonesv1.GameServerList{Items: []agonesv1.GameServer{*gs.DeepCopy()}}, nil - }) - - updated := make(chan *agonesv1.PlayerStatus, 10) - m.AgonesClient.AddReactor("patch", "gameservers", func(action k8stesting.Action) (bool, runtime.Object, error) { - gsCopy := patchGameServer(t, action, &gs) - updated <- gsCopy.Status.Players - return true, gsCopy, nil - }) - - assert.NoError(t, sc.WaitForConnection(ctx)) - sc.informerFactory.Start(ctx.Done()) - assert.True(t, cache.WaitForCacheSync(ctx.Done(), sc.gameServerSynced)) - - go func() { - err = sc.Run(ctx) - assert.NoError(t, err) - }() - - // check initial value comes through - // async, so check after a period - e := &alpha.Empty{} - err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { - count, err := sc.GetPlayerCapacity(context.Background(), e) - return count.Count == capacity, err - }) - assert.NoError(t, err) - - count, err := sc.GetPlayerCount(context.Background(), e) - require.NoError(t, err) - assert.Equal(t, int64(0), count.Count) - - list, err := sc.GetConnectedPlayers(context.Background(), e) - require.NoError(t, err) - assert.Empty(t, list.List) - - ok, err := sc.IsPlayerConnected(context.Background(), &alpha.PlayerID{PlayerID: "1"}) - require.NoError(t, err) - assert.False(t, ok.Bool, "no player connected yet") - - // sdk value should always be correct, even if we send more than one update per second. - for i := int64(0); i < capacity; i++ { - token := strconv.FormatInt(i, 10) - id := &alpha.PlayerID{PlayerID: token} - ok, err := sc.PlayerConnect(context.Background(), id) - require.NoError(t, err) - assert.True(t, ok.Bool, "Player "+token+" should not yet be connected") - - ok, err = sc.IsPlayerConnected(context.Background(), id) - require.NoError(t, err) - assert.True(t, ok.Bool, "Player "+token+" should be connected") - } - count, err = sc.GetPlayerCount(context.Background(), e) - require.NoError(t, err) - assert.Equal(t, capacity, count.Count) - - list, err = sc.GetConnectedPlayers(context.Background(), e) - require.NoError(t, err) - assert.Equal(t, []string{"0", "1", "2"}, list.List) - - // on an update, confirm that the update hits the K8s api, only once - select { - case value := <-updated: - assert.Equal(t, capacity, value.Count) - assert.Equal(t, []string{"0", "1", "2"}, value.IDs) - case <-time.After(5 * time.Second): - assert.Fail(t, "Should have been updated") - } - agtesting.AssertEventContains(t, m.FakeRecorder.Events, "PlayerCount Set to 3") - - // confirm there was only one update - select { - case <-updated: - assert.Fail(t, "There should be only one update for the player connections") - case <-time.After(2 * time.Second): - } - - // should return an error if we try and add another, since we're at capacity - nopePlayer := &alpha.PlayerID{PlayerID: "nope"} - _, err = sc.PlayerConnect(context.Background(), nopePlayer) - assert.EqualError(t, err, "players are already at capacity") - - // sdk value should always be correct, even if we send more than one update per second. - // let's leave one player behind - for i := int64(0); i < capacity-1; i++ { - token := strconv.FormatInt(i, 10) - id := &alpha.PlayerID{PlayerID: token} - ok, err := sc.PlayerDisconnect(context.Background(), id) - require.NoError(t, err) - assert.Truef(t, ok.Bool, "Player %s should be disconnected", token) - - ok, err = sc.IsPlayerConnected(context.Background(), id) - require.NoError(t, err) - assert.Falsef(t, ok.Bool, "Player %s should be connected", token) - } - count, err = sc.GetPlayerCount(context.Background(), e) - require.NoError(t, err) - assert.Equal(t, int64(1), count.Count) - - list, err = sc.GetConnectedPlayers(context.Background(), e) - require.NoError(t, err) - assert.Equal(t, []string{"2"}, list.List) - - // on an update, confirm that the update hits the K8s api, only once - select { - case value := <-updated: - assert.Equal(t, int64(1), value.Count) - assert.Equal(t, []string{"2"}, value.IDs) - case <-time.After(5 * time.Second): - assert.Fail(t, "Should have been updated") - } - agtesting.AssertEventContains(t, m.FakeRecorder.Events, "PlayerCount Set to 1") - - // confirm there was only one update - select { - case <-updated: - assert.Fail(t, "There should be only one update for the player disconnections") - case <-time.After(2 * time.Second): - } - - // last player is still there - ok, err = sc.IsPlayerConnected(context.Background(), &alpha.PlayerID{PlayerID: "2"}) - require.NoError(t, err) - assert.True(t, ok.Bool, "Player 2 should be connected") - - // finally, check idempotency of connect and disconnect - id := &alpha.PlayerID{PlayerID: "2"} // only one left behind - ok, err = sc.PlayerConnect(context.Background(), id) - require.NoError(t, err) - assert.False(t, ok.Bool, "Player 2 should already be connected") - count, err = sc.GetPlayerCount(context.Background(), e) - require.NoError(t, err) - assert.Equal(t, int64(1), count.Count) - - // no longer there. - id.PlayerID = "0" - ok, err = sc.PlayerDisconnect(context.Background(), id) - require.NoError(t, err) - assert.False(t, ok.Bool, "Player 2 should already be disconnected") - count, err = sc.GetPlayerCount(context.Background(), e) - require.NoError(t, err) - assert.Equal(t, int64(1), count.Count) - - agtesting.AssertNoEvent(t, m.FakeRecorder.Events) - - list, err = sc.GetConnectedPlayers(context.Background(), e) - require.NoError(t, err) - assert.Equal(t, []string{"2"}, list.List) -} - func TestSDKServerGracefulTerminationInterrupt(t *testing.T) { t.Parallel() agruntime.FeatureTestMutex.Lock()