github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/rollout-service/pkg/service/broadcast_test.go (about) 1 /*This file is part of kuberpult. 2 3 Kuberpult is free software: you can redistribute it and/or modify 4 it under the terms of the Expat(MIT) License as published by 5 the Free Software Foundation. 6 7 Kuberpult is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 MIT License for more details. 11 12 You should have received a copy of the MIT License 13 along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>. 14 15 Copyright 2023 freiheit.com*/ 16 17 package service 18 19 import ( 20 "context" 21 "fmt" 22 "testing" 23 "time" 24 25 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 26 "github.com/argoproj/gitops-engine/pkg/health" 27 "github.com/argoproj/gitops-engine/pkg/sync/common" 28 api "github.com/freiheit-com/kuberpult/pkg/api/v1" 29 "github.com/freiheit-com/kuberpult/services/rollout-service/pkg/versions" 30 "github.com/google/go-cmp/cmp" 31 "google.golang.org/grpc" 32 "google.golang.org/protobuf/proto" 33 "google.golang.org/protobuf/testing/protocmp" 34 ) 35 36 type testSrv struct { 37 ch chan *api.StreamStatusResponse 38 ctx context.Context 39 grpc.ServerStream 40 } 41 42 func (t *testSrv) Send(resp *api.StreamStatusResponse) error { 43 t.ch <- resp 44 return nil 45 } 46 47 func (t *testSrv) Context() context.Context { 48 return t.ctx 49 } 50 51 type errSrv struct { 52 err error 53 ctx context.Context 54 grpc.ServerStream 55 } 56 57 func (t *errSrv) Send(_ *api.StreamStatusResponse) error { 58 return t.err 59 } 60 61 func (t *errSrv) Context() context.Context { 62 return t.ctx 63 } 64 65 func TestBroadcast(t *testing.T) { 66 t.Parallel() 67 var ( 68 RolloutStatusSuccesful = api.RolloutStatus_ROLLOUT_STATUS_SUCCESFUL 69 RolloutStatusProgressing = api.RolloutStatus_ROLLOUT_STATUS_PROGRESSING 70 RolloutStatusError = api.RolloutStatus_ROLLOUT_STATUS_ERROR 71 RolloutStatusUnknown = api.RolloutStatus_ROLLOUT_STATUS_UNKNOWN 72 RolloutStatusUnhealthy = api.RolloutStatus_ROLLOUT_STATUS_UNHEALTHY 73 RolloutStatusPending = api.RolloutStatus_ROLLOUT_STATUS_PENDING 74 ) 75 type step struct { 76 ArgoEvent *ArgoEvent 77 VersionEvent *versions.KuberpultEvent 78 79 ExpectStatus *api.RolloutStatus 80 } 81 82 application := func(s step) string { 83 if s.ArgoEvent != nil { 84 return s.ArgoEvent.Application 85 } 86 return s.VersionEvent.Application 87 } 88 environment := func(s step) string { 89 if s.ArgoEvent != nil { 90 return s.ArgoEvent.Environment 91 } 92 return s.VersionEvent.Environment 93 } 94 95 tcs := []struct { 96 Name string 97 Steps []step 98 }{ 99 { 100 Name: "simple case", 101 Steps: []step{ 102 { 103 ArgoEvent: &ArgoEvent{ 104 Application: "foo", 105 Environment: "bar", 106 Version: &versions.VersionInfo{Version: 1}, 107 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 108 HealthStatusCode: health.HealthStatusHealthy, 109 }, 110 }, 111 { 112 VersionEvent: &versions.KuberpultEvent{ 113 Application: "foo", 114 Environment: "bar", 115 Version: &versions.VersionInfo{Version: 1}, 116 }, 117 118 ExpectStatus: &RolloutStatusSuccesful, 119 }, 120 }, 121 }, 122 { 123 Name: "missing argo app", 124 Steps: []step{ 125 { 126 VersionEvent: &versions.KuberpultEvent{ 127 Application: "foo", 128 Environment: "bar", 129 Version: &versions.VersionInfo{Version: 2}, 130 }, 131 132 ExpectStatus: &RolloutStatusUnknown, 133 }, 134 }, 135 }, 136 { 137 Name: "missing version in argo event", 138 Steps: []step{ 139 { 140 ArgoEvent: &ArgoEvent{ 141 Application: "foo", 142 Environment: "bar", 143 Version: &versions.VersionInfo{Version: 1}, 144 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 145 HealthStatusCode: health.HealthStatusHealthy, 146 }, 147 148 ExpectStatus: &RolloutStatusUnknown, 149 }, 150 { 151 ArgoEvent: &ArgoEvent{ 152 Application: "foo", 153 Environment: "bar", 154 Version: nil, 155 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 156 HealthStatusCode: health.HealthStatusHealthy, 157 }, 158 159 ExpectStatus: &RolloutStatusUnknown, 160 }, 161 }, 162 }, 163 { 164 Name: "app syncing and becomming healthy", 165 Steps: []step{ 166 { 167 VersionEvent: &versions.KuberpultEvent{ 168 Application: "foo", 169 Environment: "bar", 170 Version: &versions.VersionInfo{Version: 1}, 171 }, 172 173 ExpectStatus: &RolloutStatusUnknown, 174 }, 175 { 176 ArgoEvent: &ArgoEvent{ 177 Application: "foo", 178 Environment: "bar", 179 Version: &versions.VersionInfo{Version: 1}, 180 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 181 HealthStatusCode: health.HealthStatusHealthy, 182 }, 183 184 ExpectStatus: &RolloutStatusSuccesful, 185 }, 186 { 187 VersionEvent: &versions.KuberpultEvent{ 188 Application: "foo", 189 Environment: "bar", 190 Version: &versions.VersionInfo{Version: 2}, 191 }, 192 193 ExpectStatus: &RolloutStatusPending, 194 }, 195 { 196 ArgoEvent: &ArgoEvent{ 197 Application: "foo", 198 Environment: "bar", 199 Version: &versions.VersionInfo{Version: 2}, 200 SyncStatusCode: v1alpha1.SyncStatusCodeOutOfSync, 201 HealthStatusCode: health.HealthStatusHealthy, 202 }, 203 204 ExpectStatus: &RolloutStatusProgressing, 205 }, 206 { 207 ArgoEvent: &ArgoEvent{ 208 Application: "foo", 209 Environment: "bar", 210 Version: &versions.VersionInfo{Version: 2}, 211 SyncStatusCode: v1alpha1.SyncStatusCodeOutOfSync, 212 HealthStatusCode: health.HealthStatusProgressing, 213 }, 214 215 ExpectStatus: nil, 216 }, 217 { 218 ArgoEvent: &ArgoEvent{ 219 Application: "foo", 220 Environment: "bar", 221 Version: &versions.VersionInfo{Version: 2}, 222 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 223 HealthStatusCode: health.HealthStatusHealthy, 224 }, 225 226 ExpectStatus: &RolloutStatusSuccesful, 227 }, 228 }, 229 }, 230 { 231 Name: "app becomming unhealthy and recovers", 232 Steps: []step{ 233 { 234 VersionEvent: &versions.KuberpultEvent{ 235 Application: "foo", 236 Environment: "bar", 237 Version: &versions.VersionInfo{Version: 1}, 238 }, 239 240 ExpectStatus: &RolloutStatusUnknown, 241 }, 242 { 243 ArgoEvent: &ArgoEvent{ 244 Application: "foo", 245 Environment: "bar", 246 Version: &versions.VersionInfo{Version: 1}, 247 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 248 HealthStatusCode: health.HealthStatusHealthy, 249 }, 250 251 ExpectStatus: &RolloutStatusSuccesful, 252 }, 253 { 254 ArgoEvent: &ArgoEvent{ 255 Application: "foo", 256 Environment: "bar", 257 Version: &versions.VersionInfo{Version: 1}, 258 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 259 HealthStatusCode: health.HealthStatusDegraded, 260 }, 261 262 ExpectStatus: &RolloutStatusUnhealthy, 263 }, 264 { 265 ArgoEvent: &ArgoEvent{ 266 Application: "foo", 267 Environment: "bar", 268 Version: &versions.VersionInfo{Version: 1}, 269 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 270 HealthStatusCode: health.HealthStatusHealthy, 271 }, 272 273 ExpectStatus: &RolloutStatusSuccesful, 274 }, 275 }, 276 }, 277 { 278 Name: "rollout fails", 279 Steps: []step{ 280 { 281 ArgoEvent: &ArgoEvent{ 282 Application: "foo", 283 Environment: "bar", 284 Version: &versions.VersionInfo{Version: 1}, 285 SyncStatusCode: v1alpha1.SyncStatusCodeOutOfSync, 286 HealthStatusCode: health.HealthStatusHealthy, 287 OperationState: &v1alpha1.OperationState{ 288 Phase: common.OperationFailed, 289 }, 290 }, 291 292 ExpectStatus: &RolloutStatusError, 293 }, 294 }, 295 }, 296 { 297 Name: "rollout errors", 298 Steps: []step{ 299 { 300 ArgoEvent: &ArgoEvent{ 301 Application: "foo", 302 Environment: "bar", 303 Version: &versions.VersionInfo{Version: 1}, 304 SyncStatusCode: v1alpha1.SyncStatusCodeOutOfSync, 305 HealthStatusCode: health.HealthStatusHealthy, 306 OperationState: &v1alpha1.OperationState{ 307 Phase: common.OperationError, 308 }, 309 }, 310 311 ExpectStatus: &RolloutStatusError, 312 }, 313 }, 314 }, 315 { 316 Name: "healthy app switches to pending when a new version in kuberpult is deployed", 317 Steps: []step{ 318 { 319 VersionEvent: &versions.KuberpultEvent{ 320 Application: "foo", 321 Environment: "bar", 322 Version: &versions.VersionInfo{Version: 1}, 323 }, 324 325 ExpectStatus: &RolloutStatusUnknown, 326 }, 327 { 328 ArgoEvent: &ArgoEvent{ 329 Application: "foo", 330 Environment: "bar", 331 Version: &versions.VersionInfo{Version: 1}, 332 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 333 HealthStatusCode: health.HealthStatusHealthy, 334 }, 335 336 ExpectStatus: &RolloutStatusSuccesful, 337 }, 338 { 339 VersionEvent: &versions.KuberpultEvent{ 340 Application: "foo", 341 Environment: "bar", 342 Version: &versions.VersionInfo{Version: 2}, 343 }, 344 345 ExpectStatus: &RolloutStatusPending, 346 }, 347 { 348 ArgoEvent: &ArgoEvent{ 349 Application: "foo", 350 Environment: "bar", 351 Version: &versions.VersionInfo{Version: 2}, 352 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 353 HealthStatusCode: health.HealthStatusHealthy, 354 }, 355 356 ExpectStatus: &RolloutStatusSuccesful, 357 }, 358 }, 359 }, 360 } 361 362 for _, tc := range tcs { 363 tc := tc 364 t.Run(tc.Name+" (streaming)", func(t *testing.T) { 365 bc := New() 366 ctx, cancel := context.WithCancel(context.Background()) 367 ch := make(chan *api.StreamStatusResponse) 368 srv := testSrv{ctx: ctx, ch: ch} 369 go bc.StreamStatus(&api.StreamStatusRequest{}, &srv) 370 for i, s := range tc.Steps { 371 if s.ArgoEvent != nil { 372 bc.ProcessArgoEvent(context.Background(), *s.ArgoEvent) 373 } else if s.VersionEvent != nil { 374 bc.ProcessKuberpultEvent(context.Background(), *s.VersionEvent) 375 } 376 if s.ExpectStatus != nil { 377 resp := <-ch 378 if resp.Application != application(s) { 379 t.Errorf("wrong application received in step %d: expected %q, got %q", i, application(s), resp.Application) 380 } 381 if resp.Environment != environment(s) { 382 t.Errorf("wrong environment received in step %d: expected %q, got %q", i, environment(s), resp.Environment) 383 } 384 if resp.RolloutStatus != *s.ExpectStatus { 385 t.Errorf("wrong status received in step %d: expected %q, got %q", i, s.ExpectStatus, resp.RolloutStatus) 386 } 387 } else { 388 select { 389 case resp := <-ch: 390 t.Errorf("didn't expect status update but got %#v", resp) 391 default: 392 } 393 } 394 } 395 cancel() 396 }) 397 t.Run(tc.Name+" (once)", func(t *testing.T) { 398 bc := New() 399 for i, s := range tc.Steps { 400 if s.ArgoEvent != nil { 401 bc.ProcessArgoEvent(context.Background(), *s.ArgoEvent) 402 } else if s.VersionEvent != nil { 403 bc.ProcessKuberpultEvent(context.Background(), *s.VersionEvent) 404 } 405 if s.ExpectStatus != nil { 406 ctx, cancel := context.WithCancel(context.Background()) 407 ch := make(chan *api.StreamStatusResponse, 1) 408 srv := testSrv{ctx: ctx, ch: ch} 409 go bc.StreamStatus(&api.StreamStatusRequest{}, &srv) 410 resp := <-ch 411 cancel() 412 if resp.Application != application(s) { 413 t.Errorf("wrong application received in step %d: expected %q, got %q", i, application(s), resp.Application) 414 } 415 if resp.Environment != environment(s) { 416 t.Errorf("wrong environment received in step %d: expected %q, got %q", i, environment(s), resp.Environment) 417 } 418 if resp.RolloutStatus != *s.ExpectStatus { 419 t.Errorf("wrong status received in step %d: expected %q, got %q", i, s.ExpectStatus, resp.RolloutStatus) 420 } 421 } 422 } 423 }) 424 t.Run(tc.Name+" (get)", func(t *testing.T) { 425 bc := New() 426 lastStatus := RolloutStatusUnknown 427 for i, s := range tc.Steps { 428 if s.ArgoEvent != nil { 429 bc.ProcessArgoEvent(context.Background(), *s.ArgoEvent) 430 } else if s.VersionEvent != nil { 431 bc.ProcessKuberpultEvent(context.Background(), *s.VersionEvent) 432 } 433 434 ctx, cancel := context.WithCancel(context.Background()) 435 resp, err := bc.GetStatus(ctx, &api.GetStatusRequest{}) 436 cancel() 437 if err != nil { 438 t.Errorf("didn't expect an error but got %q", err) 439 } 440 441 if s.ExpectStatus != nil { 442 lastStatus = *s.ExpectStatus 443 } 444 if resp.Status != lastStatus { 445 t.Errorf("wrong status received in step %d: expected %q, got %q", i, lastStatus, resp.Status) 446 } 447 448 if lastStatus == RolloutStatusSuccesful { 449 // Apps with successful state are excluded 450 if len(resp.Applications) != 0 { 451 t.Errorf("expected no applications but got %d", len(resp.Applications)) 452 } 453 continue 454 } 455 app := resp.Applications[0] 456 if app.Application != application(s) { 457 t.Errorf("wrong application received in step %d: expected %q, got %q", i, application(s), app.Application) 458 } 459 if app.Environment != environment(s) { 460 t.Errorf("wrong environment received in step %d: expected %q, got %q", i, environment(s), app.Environment) 461 } 462 if app.RolloutStatus != lastStatus { 463 t.Errorf("wrong status received in step %d: expected %q, got %q", i, lastStatus, app.RolloutStatus) 464 } 465 } 466 }) 467 468 } 469 } 470 471 func TestBroadcastDoesntGetStuck(t *testing.T) { 472 t.Parallel() 473 tcs := []struct { 474 Name string 475 Events uint 476 }{ 477 { 478 Name: "200 events", 479 Events: 200, 480 }, 481 } 482 for _, tc := range tcs { 483 tc := tc 484 t.Run(tc.Name, func(t *testing.T) { 485 bc := New() 486 // srv1 will just be blocked 487 ctx1, cancel1 := context.WithCancel(context.Background()) 488 ch1 := make(chan *api.StreamStatusResponse, 200) 489 ech1 := make(chan error, 1) 490 srv1 := testSrv{ctx: ctx1, ch: ch1} 491 go func() { 492 ech1 <- bc.StreamStatus(&api.StreamStatusRequest{}, &srv1) 493 }() 494 defer cancel1() 495 // srv2 will actually get consumed 496 ctx2, cancel2 := context.WithCancel(context.Background()) 497 ch2 := make(chan *api.StreamStatusResponse) 498 ech2 := make(chan error, 1) 499 srv2 := testSrv{ctx: ctx2, ch: ch2} 500 go func() { 501 ech2 <- bc.StreamStatus(&api.StreamStatusRequest{}, &srv2) 502 }() 503 defer cancel2() 504 // srv3 will just return an error 505 ctx3, cancel3 := context.WithCancel(context.Background()) 506 ech3 := make(chan error, 1) 507 testErr := fmt.Errorf("some error") 508 srv3 := errSrv{ctx: ctx3, err: testErr} 509 go func() { 510 ech3 <- bc.StreamStatus(&api.StreamStatusRequest{}, &srv3) 511 }() 512 defer cancel3() 513 514 for i := uint(0); i < tc.Events; i += 1 { 515 app := fmt.Sprintf("app-%d", i) 516 bc.ProcessArgoEvent(context.Background(), ArgoEvent{ 517 Application: app, 518 Environment: "doesntmatter", 519 HealthStatusCode: health.HealthStatusHealthy, 520 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 521 Version: &versions.VersionInfo{Version: 1}, 522 }) 523 select { 524 case resp := <-ch2: 525 if resp.Application != app { 526 t.Errorf("didn't receive correct application in for event %d, expected %q, got %q", i, app, resp.Application) 527 } 528 case <-time.After(1 * time.Second): 529 t.Fatalf("didn't receive event %d", i) 530 } 531 } 532 // Shutdown all consumers 533 cancel1() 534 cancel2() 535 cancel3() 536 // Unblock ch1 537 go func() { 538 for range ch1 { 539 } 540 }() 541 e1 := <-ech1 542 if e1 != nil { 543 t.Errorf("first subscription failed with unexpected error: %q", e1) 544 } 545 e2 := <-ech2 546 if e2 != nil { 547 t.Errorf("second subscription failed with unexpected error: %q", e2) 548 } 549 e3 := <-ech3 550 if e3 != testErr { 551 t.Errorf("third subscription failed with unexpected error: %q, exepcted: %q", e3, testErr) 552 } 553 }) 554 555 } 556 } 557 558 func TestGetStatus(t *testing.T) { 559 t.Parallel() 560 561 tcs := []struct { 562 Name string 563 ArgoEvents []ArgoEvent 564 KuberpultEvents []versions.KuberpultEvent 565 Request *api.GetStatusRequest 566 DelayedArgoEvents []ArgoEvent 567 568 ExpectedResponse *api.GetStatusResponse 569 }{ 570 { 571 Name: "simple case", 572 Request: &api.GetStatusRequest{}, 573 ExpectedResponse: &api.GetStatusResponse{ 574 Status: api.RolloutStatus_ROLLOUT_STATUS_SUCCESFUL, 575 }, 576 }, 577 { 578 Name: "filters for environmentGroup", 579 ArgoEvents: []ArgoEvent{ 580 { 581 Application: "foo", 582 Environment: "dev", 583 Version: &versions.VersionInfo{Version: 2}, 584 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 585 HealthStatusCode: health.HealthStatusHealthy, 586 }, 587 { 588 Application: "foo", 589 Environment: "prd", 590 Version: &versions.VersionInfo{Version: 1}, 591 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 592 HealthStatusCode: health.HealthStatusHealthy, 593 }, 594 }, 595 KuberpultEvents: []versions.KuberpultEvent{ 596 { 597 Application: "foo", 598 Environment: "dev", 599 Version: &versions.VersionInfo{Version: 3}, 600 EnvironmentGroup: "dev-group", 601 }, 602 { 603 Application: "foo", 604 Environment: "prd", 605 Version: &versions.VersionInfo{Version: 3}, 606 EnvironmentGroup: "prd-group", 607 }, 608 }, 609 Request: &api.GetStatusRequest{ 610 EnvironmentGroup: "dev-group", 611 }, 612 ExpectedResponse: &api.GetStatusResponse{ 613 Status: api.RolloutStatus_ROLLOUT_STATUS_PENDING, 614 Applications: []*api.GetStatusResponse_ApplicationStatus{ 615 { 616 Environment: "dev", 617 Application: "foo", 618 RolloutStatus: api.RolloutStatus_ROLLOUT_STATUS_PENDING, 619 }, 620 }, 621 }, 622 }, 623 { 624 Name: "processes late health events", 625 ArgoEvents: []ArgoEvent{ 626 { 627 Application: "foo", 628 Environment: "dev", 629 Version: &versions.VersionInfo{Version: 2}, 630 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 631 HealthStatusCode: health.HealthStatusHealthy, 632 }, 633 }, 634 KuberpultEvents: []versions.KuberpultEvent{ 635 { 636 Application: "foo", 637 Environment: "dev", 638 Version: &versions.VersionInfo{Version: 3}, 639 EnvironmentGroup: "dev-group", 640 }, 641 }, 642 // This signals that the application is now healthy 643 DelayedArgoEvents: []ArgoEvent{ 644 { 645 Application: "foo", 646 Environment: "dev", 647 Version: &versions.VersionInfo{Version: 3}, 648 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 649 HealthStatusCode: health.HealthStatusHealthy, 650 }, 651 }, 652 Request: &api.GetStatusRequest{ 653 EnvironmentGroup: "dev-group", 654 WaitSeconds: 1, 655 }, 656 ExpectedResponse: &api.GetStatusResponse{ 657 Status: api.RolloutStatus_ROLLOUT_STATUS_SUCCESFUL, 658 Applications: []*api.GetStatusResponse_ApplicationStatus{}, 659 }, 660 }, 661 { 662 Name: "processes late error events", 663 ArgoEvents: []ArgoEvent{ 664 { 665 Application: "foo", 666 Environment: "dev", 667 Version: &versions.VersionInfo{Version: 2}, 668 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 669 HealthStatusCode: health.HealthStatusHealthy, 670 }, 671 }, 672 KuberpultEvents: []versions.KuberpultEvent{ 673 { 674 Application: "foo", 675 Environment: "dev", 676 Version: &versions.VersionInfo{Version: 3}, 677 EnvironmentGroup: "dev-group", 678 }, 679 }, 680 // This signals that the application is now broken 681 DelayedArgoEvents: []ArgoEvent{ 682 { 683 Application: "foo", 684 Environment: "dev", 685 Version: &versions.VersionInfo{Version: 3}, 686 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 687 HealthStatusCode: health.HealthStatusDegraded, 688 OperationState: &v1alpha1.OperationState{ 689 Phase: common.OperationFailed, 690 }, 691 }, 692 }, 693 Request: &api.GetStatusRequest{ 694 EnvironmentGroup: "dev-group", 695 WaitSeconds: 1, 696 }, 697 ExpectedResponse: &api.GetStatusResponse{ 698 Status: api.RolloutStatus_ROLLOUT_STATUS_ERROR, 699 Applications: []*api.GetStatusResponse_ApplicationStatus{ 700 { 701 Environment: "dev", 702 Application: "foo", 703 RolloutStatus: api.RolloutStatus_ROLLOUT_STATUS_ERROR, 704 }, 705 }, 706 }, 707 }, 708 { 709 Name: "excludes succesful applications", 710 ArgoEvents: []ArgoEvent{ 711 { 712 Application: "foo", 713 Environment: "dev", 714 Version: &versions.VersionInfo{Version: 2}, 715 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 716 HealthStatusCode: health.HealthStatusHealthy, 717 }, 718 { 719 Application: "bar", 720 Environment: "dev", 721 Version: &versions.VersionInfo{Version: 1}, 722 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 723 HealthStatusCode: health.HealthStatusHealthy, 724 }, 725 }, 726 KuberpultEvents: []versions.KuberpultEvent{ 727 { 728 Application: "foo", 729 Environment: "dev", 730 Version: &versions.VersionInfo{Version: 3}, 731 EnvironmentGroup: "dev-group", 732 }, 733 { 734 Application: "bar", 735 Environment: "dev", 736 Version: &versions.VersionInfo{Version: 1}, 737 EnvironmentGroup: "dev-group", 738 }, 739 }, 740 Request: &api.GetStatusRequest{ 741 EnvironmentGroup: "dev-group", 742 }, 743 ExpectedResponse: &api.GetStatusResponse{ 744 Status: api.RolloutStatus_ROLLOUT_STATUS_PENDING, 745 Applications: []*api.GetStatusResponse_ApplicationStatus{ 746 { 747 Environment: "dev", 748 Application: "foo", 749 RolloutStatus: api.RolloutStatus_ROLLOUT_STATUS_PENDING, 750 }, 751 }, 752 }, 753 }, 754 { 755 Name: "filters for environmentGroup and team", 756 ArgoEvents: []ArgoEvent{ 757 { 758 Application: "foo", 759 Environment: "dev", 760 Version: &versions.VersionInfo{Version: 2}, 761 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 762 HealthStatusCode: health.HealthStatusHealthy, 763 }, 764 { 765 Application: "foo", 766 Environment: "prd", 767 Version: &versions.VersionInfo{Version: 1}, 768 SyncStatusCode: v1alpha1.SyncStatusCodeSynced, 769 HealthStatusCode: health.HealthStatusHealthy, 770 }, 771 }, 772 KuberpultEvents: []versions.KuberpultEvent{ 773 { 774 Application: "foo", 775 Environment: "dev", 776 Version: &versions.VersionInfo{Version: 3}, 777 EnvironmentGroup: "dev-group", 778 Team: "a", 779 }, 780 { 781 Application: "foo", 782 Environment: "bar", 783 Version: &versions.VersionInfo{Version: 3}, 784 EnvironmentGroup: "dev-group", 785 Team: "b", 786 }, 787 { 788 Application: "foo", 789 Environment: "prd", 790 Version: &versions.VersionInfo{Version: 3}, 791 EnvironmentGroup: "prd-group", 792 }, 793 }, 794 Request: &api.GetStatusRequest{ 795 EnvironmentGroup: "dev-group", 796 Team: "b", 797 }, 798 ExpectedResponse: &api.GetStatusResponse{ 799 Status: api.RolloutStatus_ROLLOUT_STATUS_UNKNOWN, 800 Applications: []*api.GetStatusResponse_ApplicationStatus{ 801 { 802 Environment: "bar", 803 Application: "foo", 804 RolloutStatus: api.RolloutStatus_ROLLOUT_STATUS_UNKNOWN, 805 }, 806 }, 807 }, 808 }, 809 } 810 811 for _, tc := range tcs { 812 tc := tc 813 t.Run(tc.Name, func(t *testing.T) { 814 t.Parallel() 815 bc := New() 816 for _, s := range tc.ArgoEvents { 817 bc.ProcessArgoEvent(context.Background(), s) 818 } 819 for _, s := range tc.KuberpultEvents { 820 bc.ProcessKuberpultEvent(context.Background(), s) 821 } 822 823 bc.waiting = func() { 824 for _, s := range tc.DelayedArgoEvents { 825 bc.ProcessArgoEvent(context.Background(), s) 826 } 827 } 828 829 resp, err := bc.GetStatus(context.Background(), tc.Request) 830 if err != nil { 831 t.Errorf("didn't expect an error but got %q", err) 832 } 833 if d := cmp.Diff(tc.ExpectedResponse, resp, protocmp.Transform()); d != "" { 834 t.Errorf("response mismatch:\ndiff:%s", d) 835 } 836 }) 837 838 // This runs all test-cases again but delays all argoevents. 839 // The effect is that all apps will start as "unknown" and then will eventually converge. 840 t.Run(tc.Name+" (delay all argo events)", func(t *testing.T) { 841 bc := New() 842 for _, s := range tc.KuberpultEvents { 843 bc.ProcessKuberpultEvent(context.Background(), s) 844 } 845 bc.waiting = func() { 846 for _, s := range tc.ArgoEvents { 847 bc.ProcessArgoEvent(context.Background(), s) 848 } 849 for _, s := range tc.DelayedArgoEvents { 850 bc.ProcessArgoEvent(context.Background(), s) 851 } 852 bc.DisconnectAll() 853 } 854 var req api.GetStatusRequest 855 proto.Merge(&req, tc.Request) 856 req.WaitSeconds = 1 857 858 resp, err := bc.GetStatus(context.Background(), &req) 859 if err != nil { 860 t.Errorf("didn't expect an error but got %q", err) 861 } 862 if d := cmp.Diff(tc.ExpectedResponse, resp, protocmp.Transform()); d != "" { 863 t.Errorf("response mismatch:\ndiff:%s", d) 864 } 865 }) 866 } 867 }