agones.dev/agones@v1.54.0/pkg/sdkserver/localsdk_test.go (about) 1 // Copyright 2018 Google LLC All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sdkserver 16 17 import ( 18 "context" 19 "encoding/json" 20 "fmt" 21 "os" 22 "strconv" 23 "sync" 24 "testing" 25 "time" 26 27 "github.com/google/go-cmp/cmp" 28 "github.com/pkg/errors" 29 "github.com/stretchr/testify/assert" 30 "google.golang.org/protobuf/testing/protocmp" 31 "google.golang.org/protobuf/types/known/fieldmaskpb" 32 "google.golang.org/protobuf/types/known/wrapperspb" 33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 "k8s.io/apimachinery/pkg/util/wait" 35 36 agonesv1 "agones.dev/agones/pkg/apis/agones/v1" 37 "agones.dev/agones/pkg/sdk" 38 "agones.dev/agones/pkg/sdk/alpha" 39 "agones.dev/agones/pkg/sdk/beta" 40 "agones.dev/agones/pkg/util/runtime" 41 ) 42 43 func TestLocal(t *testing.T) { 44 ctx := context.Background() 45 e := &sdk.Empty{} 46 l, err := NewLocalSDKServer("", "") 47 assert.Nil(t, err) 48 49 _, err = l.Ready(ctx, e) 50 assert.Nil(t, err, "Ready should not error") 51 52 _, err = l.Shutdown(ctx, e) 53 assert.Nil(t, err, "Shutdown should not error") 54 55 wg := sync.WaitGroup{} 56 wg.Add(1) 57 stream := newEmptyMockStream() 58 59 go func() { 60 err = l.Health(stream) 61 assert.Nil(t, err) 62 wg.Done() 63 }() 64 65 stream.msgs <- &sdk.Empty{} 66 close(stream.msgs) 67 68 wg.Wait() 69 70 gs, err := l.GetGameServer(ctx, e) 71 assert.Nil(t, err) 72 73 defaultGameServer := defaultGs() 74 // do this to adjust for any time differences. 75 // we only care about all the other values to be compared. 76 defaultGameServer.ObjectMeta.CreationTimestamp = gs.GetObjectMeta().CreationTimestamp 77 78 assert.Equal(t, defaultGameServer.GetObjectMeta(), gs.GetObjectMeta()) 79 assert.Equal(t, defaultGameServer.GetSpec(), gs.GetSpec()) 80 gsStatus := defaultGameServer.GetStatus() 81 gsStatus.State = "Shutdown" 82 assert.Equal(t, gsStatus, gs.GetStatus()) 83 } 84 85 func TestLocalSDKWithTestMode(t *testing.T) { 86 l, err := NewLocalSDKServer("", "") 87 assert.NoError(t, err, "Should be able to create local SDK server") 88 a := []string{"ready", "allocate", "setlabel", "setannotation", "gameserver", "health", "shutdown", "watch"} 89 b := []string{"ready", "health", "ready", "watch", "allocate", "gameserver", "setlabel", "setannotation", "health", "health", "shutdown"} 90 assert.True(t, l.EqualSets(a, a)) 91 assert.True(t, l.EqualSets(a, b)) 92 assert.True(t, l.EqualSets(b, a)) 93 assert.True(t, l.EqualSets(b, b)) 94 a[0] = "rady" 95 assert.False(t, l.EqualSets(a, b)) 96 assert.False(t, l.EqualSets(b, a)) 97 a[0] = "ready" 98 b[1] = "halth" 99 assert.False(t, l.EqualSets(a, b)) 100 assert.False(t, l.EqualSets(b, a)) 101 } 102 103 func TestLocalSDKWithGameServer(t *testing.T) { 104 ctx := context.Background() 105 e := &sdk.Empty{} 106 107 fixture := &agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "stuff"}} 108 path, err := gsToTmpFile(fixture.DeepCopy()) 109 assert.Nil(t, err) 110 111 l, err := NewLocalSDKServer(path, "") 112 assert.Nil(t, err) 113 114 gs, err := l.GetGameServer(ctx, e) 115 assert.Nil(t, err) 116 117 assert.Equal(t, fixture.ObjectMeta.Name, gs.ObjectMeta.Name) 118 } 119 120 // nolint:dupl 121 func TestLocalSDKWithLogLevel(t *testing.T) { 122 ctx := context.Background() 123 e := &sdk.Empty{} 124 125 fixture := &agonesv1.GameServer{ 126 ObjectMeta: metav1.ObjectMeta{Name: "stuff"}, 127 Spec: agonesv1.GameServerSpec{ 128 SdkServer: agonesv1.SdkServer{LogLevel: "debug"}, 129 }, 130 } 131 path, err := gsToTmpFile(fixture.DeepCopy()) 132 assert.Nil(t, err) 133 134 l, err := NewLocalSDKServer(path, "test") 135 assert.Nil(t, err) 136 137 _, err = l.GetGameServer(ctx, e) 138 assert.Nil(t, err) 139 140 // Check if the LocalSDKServer's logger.LogLevel equal fixture's 141 assert.Equal(t, string(fixture.Spec.SdkServer.LogLevel), l.logger.Logger.Level.String()) 142 } 143 144 // nolint:dupl 145 func TestLocalSDKServerSetLabel(t *testing.T) { 146 t.Parallel() 147 148 fixtures := map[string]struct { 149 gs *agonesv1.GameServer 150 }{ 151 "default": { 152 gs: nil, 153 }, 154 "no labels": { 155 gs: &agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "empty"}}, 156 }, 157 "empty": { 158 gs: &agonesv1.GameServer{}, 159 }, 160 } 161 162 for k, v := range fixtures { 163 t.Run(k, func(t *testing.T) { 164 ctx := context.Background() 165 e := &sdk.Empty{} 166 path, err := gsToTmpFile(v.gs) 167 assert.Nil(t, err) 168 169 l, err := NewLocalSDKServer(path, "") 170 assert.Nil(t, err) 171 kv := &sdk.KeyValue{Key: "foo", Value: "bar"} 172 173 stream := newGameServerMockStream() 174 wg := sync.WaitGroup{} 175 wg.Add(1) 176 go func() { 177 defer wg.Done() 178 err := l.WatchGameServer(e, stream) 179 assert.Nil(t, err) 180 }() 181 assertInitialWatchUpdate(t, stream) 182 183 // make sure length of l.updateObservers is at least 1 184 err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { 185 ret := false 186 l.updateObservers.Range(func(_, _ interface{}) bool { 187 ret = true 188 return false 189 }) 190 191 return ret, nil 192 }) 193 assert.Nil(t, err) 194 195 _, err = l.SetLabel(ctx, kv) 196 assert.Nil(t, err) 197 198 gs, err := l.GetGameServer(ctx, e) 199 assert.Nil(t, err) 200 assert.Equal(t, gs.ObjectMeta.Labels[metadataPrefix+"foo"], "bar") 201 202 assertWatchUpdate(t, stream, "bar", func(gs *sdk.GameServer) interface{} { 203 return gs.ObjectMeta.Labels[metadataPrefix+"foo"] 204 }) 205 206 l.Close() 207 wg.Wait() 208 }) 209 } 210 } 211 212 // nolint:dupl 213 func TestLocalSDKServerSetAnnotation(t *testing.T) { 214 t.Parallel() 215 216 fixtures := map[string]struct { 217 gs *agonesv1.GameServer 218 }{ 219 "default": { 220 gs: nil, 221 }, 222 "no annotation": { 223 gs: &agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "empty"}}, 224 }, 225 "empty": { 226 gs: &agonesv1.GameServer{}, 227 }, 228 } 229 230 for k, v := range fixtures { 231 t.Run(k, func(t *testing.T) { 232 ctx := context.Background() 233 e := &sdk.Empty{} 234 path, err := gsToTmpFile(v.gs) 235 assert.Nil(t, err) 236 237 l, err := NewLocalSDKServer(path, "") 238 assert.Nil(t, err) 239 240 kv := &sdk.KeyValue{Key: "bar", Value: "foo"} 241 242 stream := newGameServerMockStream() 243 wg := sync.WaitGroup{} 244 wg.Add(1) 245 go func() { 246 defer wg.Done() 247 err := l.WatchGameServer(e, stream) 248 assert.Nil(t, err) 249 }() 250 assertInitialWatchUpdate(t, stream) 251 252 // make sure length of l.updateObservers is at least 1 253 err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { 254 ret := false 255 l.updateObservers.Range(func(_, _ interface{}) bool { 256 ret = true 257 return false 258 }) 259 260 return ret, nil 261 }) 262 assert.Nil(t, err) 263 264 _, err = l.SetAnnotation(ctx, kv) 265 assert.Nil(t, err) 266 267 gs, err := l.GetGameServer(ctx, e) 268 assert.Nil(t, err) 269 assert.Equal(t, gs.ObjectMeta.Annotations[metadataPrefix+"bar"], "foo") 270 271 assertWatchUpdate(t, stream, "foo", func(gs *sdk.GameServer) interface{} { 272 return gs.ObjectMeta.Annotations[metadataPrefix+"bar"] 273 }) 274 275 l.Close() 276 wg.Wait() 277 }) 278 } 279 } 280 281 func TestLocalSDKServerWatchGameServer(t *testing.T) { 282 t.Parallel() 283 284 fixture := &agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "stuff"}} 285 path, err := gsToTmpFile(fixture) 286 assert.Nil(t, err) 287 288 e := &sdk.Empty{} 289 l, err := NewLocalSDKServer(path, "") 290 assert.Nil(t, err) 291 292 stream := newGameServerMockStream() 293 go func() { 294 err := l.WatchGameServer(e, stream) 295 assert.Nil(t, err) 296 }() 297 assertInitialWatchUpdate(t, stream) 298 299 // wait for watching to begin 300 err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { 301 found := false 302 l.updateObservers.Range(func(_, _ interface{}) bool { 303 found = true 304 return false 305 }) 306 return found, nil 307 }) 308 assert.NoError(t, err) 309 310 assertNoWatchUpdate(t, stream) 311 fixture.ObjectMeta.Annotations = map[string]string{"foo": "bar"} 312 j, err := json.Marshal(fixture) 313 assert.Nil(t, err) 314 315 err = os.WriteFile(path, j, os.ModeDevice) 316 assert.Nil(t, err) 317 318 assertWatchUpdate(t, stream, "bar", func(gs *sdk.GameServer) interface{} { 319 return gs.ObjectMeta.Annotations["foo"] 320 }) 321 } 322 323 func TestLocalSDKServerPlayerCapacity(t *testing.T) { 324 t.Parallel() 325 326 runtime.FeatureTestMutex.Lock() 327 defer runtime.FeatureTestMutex.Unlock() 328 assert.NoError(t, runtime.ParseFeatures(string(runtime.FeaturePlayerTracking)+"=true")) 329 330 fixture := &agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "stuff"}} 331 332 e := &alpha.Empty{} 333 path, err := gsToTmpFile(fixture) 334 assert.NoError(t, err) 335 l, err := NewLocalSDKServer(path, "") 336 assert.Nil(t, err) 337 338 stream := newGameServerMockStream() 339 go func() { 340 err := l.WatchGameServer(&sdk.Empty{}, stream) 341 assert.Nil(t, err) 342 }() 343 assertInitialWatchUpdate(t, stream) 344 345 // wait for watching to begin 346 err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { 347 found := false 348 l.updateObservers.Range(func(_, _ interface{}) bool { 349 found = true 350 return false 351 }) 352 return found, nil 353 }) 354 assert.NoError(t, err) 355 356 c, err := l.GetPlayerCapacity(context.Background(), e) 357 assert.NoError(t, err) 358 assert.Equal(t, int64(0), c.Count) 359 360 _, err = l.SetPlayerCapacity(context.Background(), &alpha.Count{Count: 10}) 361 assert.NoError(t, err) 362 363 select { 364 case msg := <-stream.msgs: 365 assert.Equal(t, int64(10), msg.Status.Players.Capacity) 366 case <-time.After(10 * time.Second): 367 assert.Fail(t, "timeout getting watch") 368 } 369 370 c, err = l.GetPlayerCapacity(context.Background(), e) 371 assert.NoError(t, err) 372 assert.Equal(t, int64(10), c.Count) 373 374 gs, err := l.GetGameServer(context.Background(), &sdk.Empty{}) 375 assert.NoError(t, err) 376 assert.Equal(t, int64(10), gs.Status.Players.Capacity) 377 } 378 379 func TestLocalSDKServerPlayerConnectAndDisconnectWithoutPlayerTracking(t *testing.T) { 380 t.Parallel() 381 runtime.FeatureTestMutex.Lock() 382 defer runtime.FeatureTestMutex.Unlock() 383 384 assert.NoError(t, runtime.ParseFeatures(string(runtime.FeaturePlayerTracking)+"=false")) 385 386 l, err := NewLocalSDKServer("", "") 387 assert.Nil(t, err) 388 389 e := &alpha.Empty{} 390 capacity, err := l.GetPlayerCapacity(context.Background(), e) 391 assert.Nil(t, capacity) 392 assert.Error(t, err) 393 394 count, err := l.GetPlayerCount(context.Background(), e) 395 assert.Error(t, err) 396 assert.Nil(t, count) 397 398 list, err := l.GetConnectedPlayers(context.Background(), e) 399 assert.Error(t, err) 400 assert.Nil(t, list) 401 402 id := &alpha.PlayerID{PlayerID: "test-player"} 403 404 ok, err := l.PlayerConnect(context.Background(), id) 405 assert.Error(t, err) 406 assert.False(t, ok.Bool) 407 408 ok, err = l.IsPlayerConnected(context.Background(), id) 409 assert.Error(t, err) 410 assert.False(t, ok.Bool) 411 412 ok, err = l.PlayerDisconnect(context.Background(), id) 413 assert.Error(t, err) 414 assert.False(t, ok.Bool) 415 } 416 417 func TestLocalSDKServerPlayerConnectAndDisconnect(t *testing.T) { 418 t.Parallel() 419 420 runtime.FeatureTestMutex.Lock() 421 defer runtime.FeatureTestMutex.Unlock() 422 assert.NoError(t, runtime.ParseFeatures(string(runtime.FeaturePlayerTracking)+"=true")) 423 424 gs := func() *agonesv1.GameServer { 425 return &agonesv1.GameServer{ 426 ObjectMeta: metav1.ObjectMeta{Name: "stuff"}, 427 Status: agonesv1.GameServerStatus{ 428 Players: &agonesv1.PlayerStatus{ 429 Capacity: 1, 430 }, 431 }} 432 } 433 434 e := &alpha.Empty{} 435 436 fixtures := map[string]struct { 437 testMode bool 438 gs *agonesv1.GameServer 439 useFile bool 440 }{ 441 "test mode on, gs with Status.Players": { 442 testMode: true, 443 gs: gs(), 444 useFile: true, 445 }, 446 "test mode off, gs with Status.Players": { 447 testMode: false, 448 gs: gs(), 449 useFile: true, 450 }, 451 "test mode on, gs without Status.Players": { 452 testMode: true, 453 useFile: true, 454 }, 455 "test mode off, gs without Status.Players": { 456 testMode: false, 457 useFile: true, 458 }, 459 "test mode on, no filePath": { 460 testMode: true, 461 useFile: false, 462 }, 463 "test mode off, no filePath": { 464 testMode: false, 465 useFile: false, 466 }, 467 } 468 469 for k, v := range fixtures { 470 t.Run(k, func(t *testing.T) { 471 var l *LocalSDKServer 472 var err error 473 if v.useFile { 474 path, pathErr := gsToTmpFile(v.gs) 475 assert.NoError(t, pathErr) 476 l, err = NewLocalSDKServer(path, "") 477 } else { 478 l, err = NewLocalSDKServer("", "") 479 } 480 assert.Nil(t, err) 481 l.SetTestMode(v.testMode) 482 483 stream := newGameServerMockStream() 484 go func() { 485 err := l.WatchGameServer(&sdk.Empty{}, stream) 486 assert.Nil(t, err) 487 }() 488 assertInitialWatchUpdate(t, stream) 489 490 // wait for watching to begin 491 err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { 492 found := false 493 l.updateObservers.Range(func(_, _ interface{}) bool { 494 found = true 495 return false 496 }) 497 return found, nil 498 }) 499 assert.NoError(t, err) 500 501 if !v.useFile || v.gs == nil { 502 _, err := l.SetPlayerCapacity(context.Background(), &alpha.Count{ 503 Count: 1, 504 }) 505 assert.NoError(t, err) 506 expected := &sdk.GameServer_Status_PlayerStatus{ 507 Capacity: 1, 508 } 509 assertWatchUpdate(t, stream, expected, func(gs *sdk.GameServer) interface{} { 510 return gs.Status.Players 511 }) 512 } 513 514 id := &alpha.PlayerID{PlayerID: "one"} 515 ok, err := l.IsPlayerConnected(context.Background(), id) 516 assert.NoError(t, err) 517 if assert.NotNil(t, ok) { 518 assert.False(t, ok.Bool, "player should not be connected") 519 } 520 521 count, err := l.GetPlayerCount(context.Background(), e) 522 assert.NoError(t, err) 523 assert.Equal(t, int64(0), count.Count) 524 525 list, err := l.GetConnectedPlayers(context.Background(), e) 526 assert.NoError(t, err) 527 assert.Empty(t, list.List) 528 529 // connect a player 530 ok, err = l.PlayerConnect(context.Background(), id) 531 assert.NoError(t, err) 532 assert.True(t, ok.Bool, "Player should not exist yet") 533 534 count, err = l.GetPlayerCount(context.Background(), e) 535 assert.NoError(t, err) 536 assert.Equal(t, int64(1), count.Count) 537 538 expected := &sdk.GameServer_Status_PlayerStatus{ 539 Count: 1, 540 Capacity: 1, 541 Ids: []string{id.PlayerID}, 542 } 543 assertWatchUpdate(t, stream, expected, func(gs *sdk.GameServer) interface{} { 544 return gs.Status.Players 545 }) 546 547 ok, err = l.IsPlayerConnected(context.Background(), id) 548 assert.NoError(t, err) 549 assert.True(t, ok.Bool, "player should be connected") 550 551 list, err = l.GetConnectedPlayers(context.Background(), e) 552 assert.NoError(t, err) 553 assert.Equal(t, []string{id.PlayerID}, list.List) 554 555 // add same player 556 ok, err = l.PlayerConnect(context.Background(), id) 557 assert.NoError(t, err) 558 assert.False(t, ok.Bool, "Player already exists") 559 560 count, err = l.GetPlayerCount(context.Background(), e) 561 assert.NoError(t, err) 562 assert.Equal(t, int64(1), count.Count) 563 assertNoWatchUpdate(t, stream) 564 565 list, err = l.GetConnectedPlayers(context.Background(), e) 566 assert.NoError(t, err) 567 assert.Equal(t, []string{id.PlayerID}, list.List) 568 569 // should return an error if we try to add another, since we're at capacity 570 nopePlayer := &alpha.PlayerID{PlayerID: "nope"} 571 _, err = l.PlayerConnect(context.Background(), nopePlayer) 572 assert.EqualError(t, err, "Players are already at capacity") 573 574 ok, err = l.IsPlayerConnected(context.Background(), nopePlayer) 575 assert.NoError(t, err) 576 assert.False(t, ok.Bool) 577 578 // disconnect a player 579 ok, err = l.PlayerDisconnect(context.Background(), id) 580 assert.NoError(t, err) 581 assert.True(t, ok.Bool, "Player should be removed") 582 count, err = l.GetPlayerCount(context.Background(), e) 583 assert.NoError(t, err) 584 assert.Equal(t, int64(0), count.Count) 585 586 expected = &sdk.GameServer_Status_PlayerStatus{ 587 Count: 0, 588 Capacity: 1, 589 Ids: []string{}, 590 } 591 assertWatchUpdate(t, stream, expected, func(gs *sdk.GameServer) interface{} { 592 return gs.Status.Players 593 }) 594 595 list, err = l.GetConnectedPlayers(context.Background(), e) 596 assert.NoError(t, err) 597 assert.Empty(t, list.List) 598 599 // remove same player 600 ok, err = l.PlayerDisconnect(context.Background(), id) 601 assert.NoError(t, err) 602 assert.False(t, ok.Bool, "Player already be gone") 603 count, err = l.GetPlayerCount(context.Background(), e) 604 assert.NoError(t, err) 605 assert.Equal(t, int64(0), count.Count) 606 assertNoWatchUpdate(t, stream) 607 }) 608 } 609 } 610 611 func TestLocalSDKServerGetCounter(t *testing.T) { 612 t.Parallel() 613 614 runtime.FeatureTestMutex.Lock() 615 defer runtime.FeatureTestMutex.Unlock() 616 assert.NoError(t, runtime.ParseFeatures(string(runtime.FeatureCountsAndLists)+"=true")) 617 618 counters := map[string]agonesv1.CounterStatus{ 619 "sessions": {Count: int64(1), Capacity: int64(100)}, 620 } 621 fixture := &agonesv1.GameServer{ 622 ObjectMeta: metav1.ObjectMeta{Name: "stuff"}, 623 Status: agonesv1.GameServerStatus{ 624 Counters: counters, 625 }, 626 } 627 628 path, err := gsToTmpFile(fixture) 629 assert.NoError(t, err) 630 l, err := NewLocalSDKServer(path, "") 631 assert.Nil(t, err) 632 633 stream := newGameServerMockStream() 634 go func() { 635 err := l.WatchGameServer(&sdk.Empty{}, stream) 636 assert.Nil(t, err) 637 }() 638 assertInitialWatchUpdate(t, stream) 639 640 // wait for watching to begin 641 err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { 642 found := false 643 l.updateObservers.Range(func(_, _ interface{}) bool { 644 found = true 645 return false 646 }) 647 return found, nil 648 }) 649 assert.NoError(t, err) 650 651 testScenarios := map[string]struct { 652 name string 653 want *beta.Counter 654 wantErr error 655 }{ 656 "Counter exists": { 657 name: "sessions", 658 want: &beta.Counter{Name: "sessions", Count: int64(1), Capacity: int64(100)}, 659 }, 660 "Counter does not exist": { 661 name: "noName", 662 wantErr: errors.Errorf("not found. %s Counter not found", "noName"), 663 }, 664 } 665 666 for testName, testScenario := range testScenarios { 667 t.Run(testName, func(t *testing.T) { 668 got, err := l.GetCounter(context.Background(), &beta.GetCounterRequest{Name: testScenario.name}) 669 // Check tests expecting non-errors 670 if testScenario.want != nil { 671 assert.NoError(t, err) 672 if diff := cmp.Diff(testScenario.want, got, protocmp.Transform()); diff != "" { 673 t.Errorf("Unexpected difference:\n%v", diff) 674 } 675 } else { 676 // Check tests expecting errors 677 assert.EqualError(t, err, testScenario.wantErr.Error()) 678 } 679 }) 680 } 681 } 682 683 func TestLocalSDKServerUpdateCounter(t *testing.T) { 684 t.Parallel() 685 686 runtime.FeatureTestMutex.Lock() 687 defer runtime.FeatureTestMutex.Unlock() 688 assert.NoError(t, runtime.ParseFeatures(string(runtime.FeatureCountsAndLists)+"=true")) 689 690 counters := map[string]agonesv1.CounterStatus{ 691 "sessions": {Count: 1, Capacity: 100}, 692 "players": {Count: 100, Capacity: 100}, 693 "lobbies": {Count: 0, Capacity: 0}, 694 "games": {Count: 5, Capacity: 10}, 695 "npcs": {Count: 6, Capacity: 10}, 696 } 697 fixture := &agonesv1.GameServer{ 698 ObjectMeta: metav1.ObjectMeta{Name: "stuff"}, 699 Status: agonesv1.GameServerStatus{ 700 Counters: counters, 701 }, 702 } 703 704 path, err := gsToTmpFile(fixture) 705 assert.NoError(t, err) 706 l, err := NewLocalSDKServer(path, "") 707 assert.Nil(t, err) 708 709 stream := newGameServerMockStream() 710 go func() { 711 err := l.WatchGameServer(&sdk.Empty{}, stream) 712 assert.Nil(t, err) 713 }() 714 assertInitialWatchUpdate(t, stream) 715 716 // wait for watching to begin 717 err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { 718 found := false 719 l.updateObservers.Range(func(_, _ interface{}) bool { 720 found = true 721 return false 722 }) 723 return found, nil 724 }) 725 assert.NoError(t, err) 726 727 testScenarios := map[string]struct { 728 updateRequest *beta.UpdateCounterRequest 729 want *beta.Counter 730 wantErr error 731 }{ 732 "Set Counter Capacity": { 733 updateRequest: &beta.UpdateCounterRequest{ 734 CounterUpdateRequest: &beta.CounterUpdateRequest{ 735 Name: "lobbies", 736 Capacity: wrapperspb.Int64(10), 737 }}, 738 want: &beta.Counter{ 739 Name: "lobbies", Count: 0, Capacity: 10, 740 }, 741 }, 742 "Set Counter Count": { 743 updateRequest: &beta.UpdateCounterRequest{ 744 CounterUpdateRequest: &beta.CounterUpdateRequest{ 745 Name: "npcs", 746 Count: wrapperspb.Int64(10), 747 }}, 748 want: &beta.Counter{ 749 Name: "npcs", Count: 10, Capacity: 10, 750 }, 751 }, 752 "Decrement Counter Count": { 753 updateRequest: &beta.UpdateCounterRequest{ 754 CounterUpdateRequest: &beta.CounterUpdateRequest{ 755 Name: "games", 756 CountDiff: -5, 757 }}, 758 want: &beta.Counter{ 759 Name: "games", Count: 0, Capacity: 10, 760 }, 761 }, 762 "Cannot Decrement Counter": { 763 updateRequest: &beta.UpdateCounterRequest{ 764 CounterUpdateRequest: &beta.CounterUpdateRequest{ 765 Name: "sessions", 766 CountDiff: -2, 767 }}, 768 wantErr: errors.Errorf("out of range. Count must be within range [0,Capacity]. Found Count: %d, Capacity: %d", -1, 100), 769 }, 770 "Cannot Increment Counter": { 771 updateRequest: &beta.UpdateCounterRequest{ 772 CounterUpdateRequest: &beta.CounterUpdateRequest{ 773 Name: "players", 774 CountDiff: 1, 775 }}, 776 wantErr: errors.Errorf("out of range. Count must be within range [0,Capacity]. Found Count: %d, Capacity: %d", 101, 100), 777 }, 778 "Counter does not exist": { 779 updateRequest: &beta.UpdateCounterRequest{ 780 CounterUpdateRequest: &beta.CounterUpdateRequest{ 781 Name: "dragons", 782 CountDiff: 1, 783 }}, 784 wantErr: errors.Errorf("not found. %s Counter not found", "dragons"), 785 }, 786 "request Counter is nil": { 787 updateRequest: &beta.UpdateCounterRequest{ 788 CounterUpdateRequest: nil, 789 }, 790 wantErr: errors.Errorf("invalid argument. CounterUpdateRequest cannot be nil"), 791 }, 792 "capacity is less than zero": { 793 updateRequest: &beta.UpdateCounterRequest{ 794 CounterUpdateRequest: &beta.CounterUpdateRequest{ 795 Name: "lobbies", 796 Capacity: wrapperspb.Int64(-1), 797 }}, 798 wantErr: errors.Errorf("out of range. Capacity must be greater than or equal to 0. Found Capacity: %d", -1), 799 }, 800 "count is less than zero": { 801 updateRequest: &beta.UpdateCounterRequest{ 802 CounterUpdateRequest: &beta.CounterUpdateRequest{ 803 Name: "players", 804 Count: wrapperspb.Int64(-1), 805 }}, 806 wantErr: errors.Errorf("out of range. Count must be within range [0,Capacity]. Found Count: %d, Capacity: %d", -1, 100), 807 }, 808 "count is greater than capacity": { 809 updateRequest: &beta.UpdateCounterRequest{ 810 CounterUpdateRequest: &beta.CounterUpdateRequest{ 811 Name: "players", 812 Count: wrapperspb.Int64(101), 813 }}, 814 wantErr: errors.Errorf("out of range. Count must be within range [0,Capacity]. Found Count: %d, Capacity: %d", 101, 100), 815 }, 816 } 817 818 for testName, testScenario := range testScenarios { 819 t.Run(testName, func(t *testing.T) { 820 got, err := l.UpdateCounter(context.Background(), testScenario.updateRequest) 821 // Check tests expecting non-errors 822 if testScenario.want != nil { 823 assert.NoError(t, err) 824 if diff := cmp.Diff(testScenario.want, got, protocmp.Transform()); diff != "" { 825 t.Errorf("Unexpected difference:\n%v", diff) 826 } 827 } else { 828 // Check tests expecting errors 829 assert.ErrorContains(t, err, testScenario.wantErr.Error()) 830 } 831 }) 832 } 833 } 834 835 func TestLocalSDKServerGetList(t *testing.T) { 836 t.Parallel() 837 838 runtime.FeatureTestMutex.Lock() 839 defer runtime.FeatureTestMutex.Unlock() 840 assert.NoError(t, runtime.ParseFeatures(string(runtime.FeatureCountsAndLists)+"=true")) 841 842 lists := map[string]agonesv1.ListStatus{ 843 "games": {Capacity: int64(100), Values: []string{"game1", "game2"}}, 844 } 845 fixture := &agonesv1.GameServer{ 846 ObjectMeta: metav1.ObjectMeta{Name: "stuff"}, 847 Status: agonesv1.GameServerStatus{ 848 Lists: lists, 849 }, 850 } 851 852 path, err := gsToTmpFile(fixture) 853 assert.NoError(t, err) 854 l, err := NewLocalSDKServer(path, "") 855 assert.Nil(t, err) 856 857 stream := newGameServerMockStream() 858 go func() { 859 err := l.WatchGameServer(&sdk.Empty{}, stream) 860 assert.Nil(t, err) 861 }() 862 assertInitialWatchUpdate(t, stream) 863 864 // wait for watching to begin 865 err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { 866 found := false 867 l.updateObservers.Range(func(_, _ interface{}) bool { 868 found = true 869 return false 870 }) 871 return found, nil 872 }) 873 assert.NoError(t, err) 874 875 testScenarios := map[string]struct { 876 name string 877 want *beta.List 878 wantErr error 879 }{ 880 "List exists": { 881 name: "games", 882 want: &beta.List{Name: "games", Capacity: int64(100), Values: []string{"game1", "game2"}}, 883 }, 884 "List does not exist": { 885 name: "noName", 886 wantErr: errors.Errorf("not found. %s List not found", "noName"), 887 }, 888 } 889 890 for testName, testScenario := range testScenarios { 891 t.Run(testName, func(t *testing.T) { 892 got, err := l.GetList(context.Background(), &beta.GetListRequest{Name: testScenario.name}) 893 // Check tests expecting non-errors 894 if testScenario.want != nil { 895 assert.NoError(t, err) 896 if diff := cmp.Diff(testScenario.want, got, protocmp.Transform()); diff != "" { 897 t.Errorf("Unexpected difference:\n%v", diff) 898 } 899 } else { 900 // Check tests expecting errors 901 assert.EqualError(t, err, testScenario.wantErr.Error()) 902 } 903 }) 904 } 905 } 906 907 func TestLocalSDKServerUpdateList(t *testing.T) { 908 t.Parallel() 909 910 runtime.FeatureTestMutex.Lock() 911 defer runtime.FeatureTestMutex.Unlock() 912 assert.NoError(t, runtime.ParseFeatures(string(runtime.FeatureCountsAndLists)+"=true")) 913 914 lists := map[string]agonesv1.ListStatus{ 915 "games": {Capacity: 100, Values: []string{"game1", "game2"}}, 916 "unicorns": {Capacity: 1000, Values: []string{"unicorn1", "unicorn2"}}, 917 "clients": {Capacity: 10, Values: []string{}}, 918 "assets": {Capacity: 1, Values: []string{"asset1"}}, 919 "models": {Capacity: 11, Values: []string{"model1", "model2"}}, 920 } 921 fixture := &agonesv1.GameServer{ 922 ObjectMeta: metav1.ObjectMeta{Name: "stuff"}, 923 Status: agonesv1.GameServerStatus{ 924 Lists: lists, 925 }, 926 } 927 928 path, err := gsToTmpFile(fixture) 929 assert.NoError(t, err) 930 l, err := NewLocalSDKServer(path, "") 931 assert.Nil(t, err) 932 933 stream := newGameServerMockStream() 934 go func() { 935 err := l.WatchGameServer(&sdk.Empty{}, stream) 936 assert.Nil(t, err) 937 }() 938 assertInitialWatchUpdate(t, stream) 939 940 // wait for watching to begin 941 err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { 942 found := false 943 l.updateObservers.Range(func(_, _ interface{}) bool { 944 found = true 945 return false 946 }) 947 return found, nil 948 }) 949 assert.NoError(t, err) 950 951 testScenarios := map[string]struct { 952 updateRequest *beta.UpdateListRequest 953 want *beta.List 954 wantErr error 955 }{ 956 "only updates fields in the FieldMask": { 957 updateRequest: &beta.UpdateListRequest{ 958 List: &beta.List{ 959 Name: "games", 960 Capacity: int64(999), 961 Values: []string{"game3"}, 962 }, 963 UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"capacity"}}, 964 }, 965 want: &beta.List{ 966 Name: "games", 967 Capacity: int64(999), 968 Values: []string{"game1", "game2"}, 969 }, 970 }, 971 "updates both fields in the FieldMask": { 972 updateRequest: &beta.UpdateListRequest{ 973 List: &beta.List{ 974 Name: "unicorns", 975 Capacity: int64(42), 976 Values: []string{"unicorn0"}, 977 }, 978 UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"values", "capacity"}}, 979 }, 980 want: &beta.List{ 981 Name: "unicorns", 982 Capacity: int64(42), 983 Values: []string{"unicorn0"}, 984 }, 985 }, 986 "default value for Capacity applied": { 987 updateRequest: &beta.UpdateListRequest{ 988 List: &beta.List{ 989 Name: "clients", 990 }, 991 UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"capacity"}}, 992 }, 993 want: &beta.List{ 994 Name: "clients", 995 Capacity: int64(0), 996 Values: []string{}, 997 }, 998 }, 999 "default value for Values applied": { 1000 updateRequest: &beta.UpdateListRequest{ 1001 List: &beta.List{ 1002 Name: "assets", 1003 }, 1004 UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"values"}}, 1005 }, 1006 want: &beta.List{ 1007 Name: "assets", 1008 Capacity: int64(1), 1009 Values: []string{}, 1010 }, 1011 }, 1012 "List does not exist": { 1013 updateRequest: &beta.UpdateListRequest{ 1014 List: &beta.List{ 1015 Name: "dragons", 1016 }, 1017 UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"capacity"}}, 1018 }, 1019 wantErr: errors.Errorf("not found. %s List not found", "dragons"), 1020 }, 1021 "request List is nil": { 1022 updateRequest: &beta.UpdateListRequest{ 1023 List: nil, 1024 UpdateMask: &fieldmaskpb.FieldMask{}, 1025 }, 1026 wantErr: errors.Errorf("invalid argument. List: %v and UpdateMask %v cannot be nil", nil, &fieldmaskpb.FieldMask{}), 1027 }, 1028 "request UpdateMask is nil": { 1029 updateRequest: &beta.UpdateListRequest{ 1030 List: &beta.List{}, 1031 UpdateMask: nil, 1032 }, 1033 wantErr: errors.Errorf("invalid argument. List: %v and UpdateMask %v cannot be nil", &beta.List{}, nil), 1034 }, 1035 "updateMask contains invalid path": { 1036 updateRequest: &beta.UpdateListRequest{ 1037 List: &beta.List{ 1038 Name: "assets", 1039 }, 1040 UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"foo"}}, 1041 }, 1042 wantErr: errors.Errorf("invalid argument. Field Mask Path(s): [foo] are invalid for List. Use valid field name(s): "), 1043 }, 1044 "updateMask is empty": { 1045 updateRequest: &beta.UpdateListRequest{ 1046 List: &beta.List{ 1047 Name: "unicorns", 1048 }, 1049 UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{""}}, 1050 }, 1051 wantErr: errors.Errorf("invalid argument. Field Mask Path(s): [] are invalid for List. Use valid field name(s): "), 1052 }, 1053 "capacity is less than zero": { 1054 updateRequest: &beta.UpdateListRequest{ 1055 List: &beta.List{ 1056 Name: "clients", 1057 Capacity: -1, 1058 }, 1059 UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"capacity"}}, 1060 }, 1061 wantErr: errors.Errorf("out of range. Capacity must be within range [0,1000]. Found Capacity: %d", -1), 1062 }, 1063 "capacity greater than max capacity (1000)": { 1064 updateRequest: &beta.UpdateListRequest{ 1065 List: &beta.List{ 1066 Name: "clients", 1067 Capacity: 1001, 1068 }, 1069 UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"capacity"}}, 1070 }, 1071 wantErr: errors.Errorf("out of range. Capacity must be within range [0,1000]. Found Capacity: %d", 1001), 1072 }, 1073 "capacity is less than List length": { 1074 updateRequest: &beta.UpdateListRequest{ 1075 List: &beta.List{ 1076 Name: "models", 1077 Capacity: 1, 1078 }, 1079 UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"capacity"}}, 1080 }, 1081 want: &beta.List{ 1082 Name: "models", 1083 Capacity: int64(1), 1084 Values: []string{"model1"}, 1085 }, 1086 }, 1087 } 1088 1089 for testName, testScenario := range testScenarios { 1090 t.Run(testName, func(t *testing.T) { 1091 got, err := l.UpdateList(context.Background(), testScenario.updateRequest) 1092 // Check tests expecting non-errors 1093 if testScenario.want != nil { 1094 assert.NoError(t, err) 1095 if diff := cmp.Diff(testScenario.want, got, protocmp.Transform()); diff != "" { 1096 t.Errorf("Unexpected difference:\n%v", diff) 1097 } 1098 } else { 1099 // Check tests expecting errors 1100 assert.ErrorContains(t, err, testScenario.wantErr.Error()) 1101 } 1102 }) 1103 } 1104 } 1105 1106 func TestLocalSDKServerAddListValue(t *testing.T) { 1107 t.Parallel() 1108 1109 runtime.FeatureTestMutex.Lock() 1110 defer runtime.FeatureTestMutex.Unlock() 1111 assert.NoError(t, runtime.ParseFeatures(string(runtime.FeatureCountsAndLists)+"=true")) 1112 1113 lists := map[string]agonesv1.ListStatus{ 1114 "lemmings": {Capacity: int64(100), Values: []string{"lemming1", "lemming2"}}, 1115 "hacks": {Capacity: int64(2), Values: []string{"hack1", "hack2"}}, 1116 } 1117 fixture := &agonesv1.GameServer{ 1118 ObjectMeta: metav1.ObjectMeta{Name: "stuff"}, 1119 Status: agonesv1.GameServerStatus{ 1120 Lists: lists, 1121 }, 1122 } 1123 1124 path, err := gsToTmpFile(fixture) 1125 assert.NoError(t, err) 1126 l, err := NewLocalSDKServer(path, "") 1127 assert.Nil(t, err) 1128 1129 stream := newGameServerMockStream() 1130 go func() { 1131 err := l.WatchGameServer(&sdk.Empty{}, stream) 1132 assert.Nil(t, err) 1133 }() 1134 assertInitialWatchUpdate(t, stream) 1135 1136 // wait for watching to begin 1137 err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { 1138 found := false 1139 l.updateObservers.Range(func(_, _ interface{}) bool { 1140 found = true 1141 return false 1142 }) 1143 return found, nil 1144 }) 1145 assert.NoError(t, err) 1146 1147 testScenarios := map[string]struct { 1148 addRequest *beta.AddListValueRequest 1149 want *beta.List 1150 wantErr error 1151 }{ 1152 "add List value": { 1153 addRequest: &beta.AddListValueRequest{ 1154 Name: "lemmings", 1155 Value: "lemming3", 1156 }, 1157 want: &beta.List{Name: "lemmings", Capacity: int64(100), Values: []string{"lemming1", "lemming2", "lemming3"}}, 1158 }, 1159 "List does not exist": { 1160 addRequest: &beta.AddListValueRequest{ 1161 Name: "dragons", 1162 }, 1163 wantErr: errors.Errorf("not found. %s List not found", "dragons"), 1164 }, 1165 "add more values than capacity": { 1166 addRequest: &beta.AddListValueRequest{ 1167 Name: "hacks", 1168 Value: "hack3", 1169 }, 1170 wantErr: errors.Errorf("out of range. No available capacity. Current Capacity: %d, List Size: %d", int64(2), int64(2)), 1171 }, 1172 "add existing value": { 1173 addRequest: &beta.AddListValueRequest{ 1174 Name: "lemmings", 1175 Value: "lemming1", 1176 }, 1177 wantErr: errors.Errorf("already exists. Value: %s already in List: %s", "lemming1", "lemmings"), 1178 }, 1179 } 1180 1181 for testName, testScenario := range testScenarios { 1182 t.Run(testName, func(t *testing.T) { 1183 got, err := l.AddListValue(context.Background(), testScenario.addRequest) 1184 // Check tests expecting non-errors 1185 if testScenario.want != nil { 1186 assert.NoError(t, err) 1187 if diff := cmp.Diff(testScenario.want, got, protocmp.Transform()); diff != "" { 1188 t.Errorf("Unexpected difference:\n%v", diff) 1189 } 1190 } else { 1191 // Check tests expecting errors 1192 assert.ErrorContains(t, err, testScenario.wantErr.Error()) 1193 } 1194 }) 1195 } 1196 } 1197 1198 func TestLocalSDKServerRemoveListValue(t *testing.T) { 1199 t.Parallel() 1200 1201 runtime.FeatureTestMutex.Lock() 1202 defer runtime.FeatureTestMutex.Unlock() 1203 assert.NoError(t, runtime.ParseFeatures(string(runtime.FeatureCountsAndLists)+"=true")) 1204 1205 lists := map[string]agonesv1.ListStatus{ 1206 "players": {Capacity: int64(100), Values: []string{"player1", "player2"}}, 1207 "items": {Capacity: int64(1000), Values: []string{"item1", "item2"}}, 1208 } 1209 fixture := &agonesv1.GameServer{ 1210 ObjectMeta: metav1.ObjectMeta{Name: "stuff"}, 1211 Status: agonesv1.GameServerStatus{ 1212 Lists: lists, 1213 }, 1214 } 1215 1216 path, err := gsToTmpFile(fixture) 1217 assert.NoError(t, err) 1218 l, err := NewLocalSDKServer(path, "") 1219 assert.Nil(t, err) 1220 1221 stream := newGameServerMockStream() 1222 go func() { 1223 err := l.WatchGameServer(&sdk.Empty{}, stream) 1224 assert.Nil(t, err) 1225 }() 1226 assertInitialWatchUpdate(t, stream) 1227 1228 // wait for watching to begin 1229 err = wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (bool, error) { 1230 found := false 1231 l.updateObservers.Range(func(_, _ interface{}) bool { 1232 found = true 1233 return false 1234 }) 1235 return found, nil 1236 }) 1237 assert.NoError(t, err) 1238 1239 testScenarios := map[string]struct { 1240 removeRequest *beta.RemoveListValueRequest 1241 want *beta.List 1242 wantErr error 1243 }{ 1244 "remove List value": { 1245 removeRequest: &beta.RemoveListValueRequest{ 1246 Name: "players", 1247 Value: "player1", 1248 }, 1249 want: &beta.List{Name: "players", Capacity: int64(100), Values: []string{"player2"}}, 1250 }, 1251 "List does not exist": { 1252 removeRequest: &beta.RemoveListValueRequest{ 1253 Name: "dragons", 1254 }, 1255 wantErr: errors.Errorf("not found. %s List not found", "dragons"), 1256 }, 1257 "value does not exist": { 1258 removeRequest: &beta.RemoveListValueRequest{ 1259 Name: "items", 1260 Value: "item3", 1261 }, 1262 wantErr: errors.Errorf("not found. Value: %s not found in List: %s", "item3", "items"), 1263 }, 1264 } 1265 1266 for testName, testScenario := range testScenarios { 1267 t.Run(testName, func(t *testing.T) { 1268 got, err := l.RemoveListValue(context.Background(), testScenario.removeRequest) 1269 // Check tests expecting non-errors 1270 if testScenario.want != nil { 1271 assert.NoError(t, err) 1272 if diff := cmp.Diff(testScenario.want, got, protocmp.Transform()); diff != "" { 1273 t.Errorf("Unexpected difference:\n%v", diff) 1274 } 1275 } else { 1276 // Check tests expecting errors 1277 assert.ErrorContains(t, err, testScenario.wantErr.Error()) 1278 } 1279 }) 1280 } 1281 } 1282 1283 // TestLocalSDKServerStateUpdates verify that SDK functions changes the state of the 1284 // GameServer object 1285 func TestLocalSDKServerStateUpdates(t *testing.T) { 1286 t.Parallel() 1287 l, err := NewLocalSDKServer("", "") 1288 assert.Nil(t, err) 1289 1290 ctx := context.Background() 1291 e := &sdk.Empty{} 1292 _, err = l.Ready(ctx, e) 1293 assert.Nil(t, err) 1294 1295 gs, err := l.GetGameServer(ctx, e) 1296 assert.Nil(t, err) 1297 assert.Equal(t, gs.Status.State, string(agonesv1.GameServerStateReady)) 1298 1299 seconds := &sdk.Duration{Seconds: 2} 1300 _, err = l.Reserve(ctx, seconds) 1301 assert.Nil(t, err) 1302 1303 gs, err = l.GetGameServer(ctx, e) 1304 assert.Nil(t, err) 1305 assert.Equal(t, gs.Status.State, string(agonesv1.GameServerStateReserved)) 1306 1307 _, err = l.Allocate(ctx, e) 1308 assert.Nil(t, err) 1309 1310 gs, err = l.GetGameServer(ctx, e) 1311 assert.Nil(t, err) 1312 assert.Equal(t, gs.Status.State, string(agonesv1.GameServerStateAllocated)) 1313 1314 _, err = l.Shutdown(ctx, e) 1315 assert.Nil(t, err) 1316 1317 gs, err = l.GetGameServer(ctx, e) 1318 assert.Nil(t, err) 1319 assert.Equal(t, gs.Status.State, string(agonesv1.GameServerStateShutdown)) 1320 } 1321 1322 // TestSDKConformanceFunctionality - run a number of record requests in parallel 1323 func TestSDKConformanceFunctionality(t *testing.T) { 1324 t.Parallel() 1325 1326 l, err := NewLocalSDKServer("", "") 1327 assert.Nil(t, err) 1328 l.testMode = true 1329 l.recordRequest("") 1330 l.gs = &sdk.GameServer{ObjectMeta: &sdk.GameServer_ObjectMeta{Name: "empty"}} 1331 exampleUID := "052fb0f4-3d50-11e5-b066-42010af0d7b6" 1332 // field which is tested 1333 setAnnotation := "setannotation" 1334 l.gs.ObjectMeta.Uid = exampleUID 1335 1336 var expected []string 1337 expected = append(expected, "", setAnnotation) 1338 1339 wg := sync.WaitGroup{} 1340 for i := 0; i < 20; i++ { 1341 wg.Add(1) 1342 str := fmt.Sprintf("%d", i) 1343 expected = append(expected, str) 1344 1345 go func() { 1346 l.recordRequest(str) 1347 l.recordRequestWithValue(setAnnotation, exampleUID, "UID") 1348 wg.Done() 1349 }() 1350 } 1351 wg.Wait() 1352 1353 l.SetExpectedSequence(expected) 1354 b := l.EqualSets(l.expectedSequence, l.requestSequence) 1355 assert.True(t, b, "we should receive strings from all go routines %v %v", l.expectedSequence, l.requestSequence) 1356 } 1357 1358 func TestAlphaSDKConformanceFunctionality(t *testing.T) { 1359 t.Parallel() 1360 lStable, err := NewLocalSDKServer("", "") 1361 assert.Nil(t, err) 1362 v := int64(0) 1363 lStable.recordRequestWithValue("setplayercapacity", strconv.FormatInt(v, 10), "PlayerCapacity") 1364 lStable.recordRequestWithValue("isplayerconnected", "", "PlayerIDs") 1365 1366 runtime.FeatureTestMutex.Lock() 1367 defer runtime.FeatureTestMutex.Unlock() 1368 1369 assert.NoError(t, runtime.ParseFeatures(string(runtime.FeaturePlayerTracking)+"=true")) 1370 l, err := NewLocalSDKServer("", "") 1371 assert.Nil(t, err) 1372 l.testMode = true 1373 l.recordRequestWithValue("setplayercapacity", strconv.FormatInt(v, 10), "PlayerCapacity") 1374 l.recordRequestWithValue("isplayerconnected", "", "PlayerIDs") 1375 1376 } 1377 1378 func gsToTmpFile(gs *agonesv1.GameServer) (string, error) { 1379 file, err := os.CreateTemp(os.TempDir(), "gameserver-") 1380 if err != nil { 1381 return file.Name(), err 1382 } 1383 1384 err = json.NewEncoder(file).Encode(gs) 1385 return file.Name(), err 1386 } 1387 1388 // assertWatchUpdate checks the values of an update message when a GameServer value has been changed 1389 func assertWatchUpdate(t *testing.T, stream *gameServerMockStream, expected interface{}, actual func(gs *sdk.GameServer) interface{}) { 1390 select { 1391 case msg := <-stream.msgs: 1392 assert.Equal(t, expected, actual(msg)) 1393 case <-time.After(20 * time.Second): 1394 assert.Fail(t, "timeout on receiving messages") 1395 } 1396 } 1397 1398 // assertNoWatchUpdate checks that no update message has been sent for changes to the GameServer 1399 func assertNoWatchUpdate(t *testing.T, stream *gameServerMockStream) { 1400 select { 1401 case <-stream.msgs: 1402 assert.Fail(t, "should not get a message") 1403 case <-time.After(time.Second): 1404 } 1405 } 1406 1407 // assertInitialWatchUpdate checks that the initial GameServer state is sent immediately after WatchGameServer 1408 func assertInitialWatchUpdate(t *testing.T, stream *gameServerMockStream) { 1409 select { 1410 case <-stream.msgs: 1411 case <-time.After(time.Second): 1412 assert.Fail(t, "timeout on receiving initial message") 1413 } 1414 }