github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/cd-service/pkg/service/overview_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 "github.com/freiheit-com/kuberpult/services/cd-service/pkg/repository/testutil" 22 "sync" 23 "testing" 24 25 api "github.com/freiheit-com/kuberpult/pkg/api/v1" 26 "github.com/freiheit-com/kuberpult/pkg/auth" 27 "github.com/freiheit-com/kuberpult/services/cd-service/pkg/config" 28 "github.com/freiheit-com/kuberpult/services/cd-service/pkg/repository" 29 "github.com/google/go-cmp/cmp" 30 "google.golang.org/grpc" 31 ) 32 33 type mockOverviewService_StreamOverviewServer struct { 34 grpc.ServerStream 35 Results chan *api.GetOverviewResponse 36 Ctx context.Context 37 } 38 39 func (m *mockOverviewService_StreamOverviewServer) Send(msg *api.GetOverviewResponse) error { 40 m.Results <- msg 41 return nil 42 } 43 44 func (m *mockOverviewService_StreamOverviewServer) Context() context.Context { 45 return m.Ctx 46 } 47 48 func makeApps(apps ...*api.Environment_Application) map[string]*api.Environment_Application { 49 var result map[string]*api.Environment_Application = map[string]*api.Environment_Application{} 50 for i := 0; i < len(apps); i++ { 51 app := apps[i] 52 result[app.Name] = app 53 } 54 return result 55 } 56 57 func makeEnv(envName string, groupName string, upstream *api.EnvironmentConfig_Upstream, apps map[string]*api.Environment_Application) *api.Environment { 58 return &api.Environment{ 59 Name: envName, 60 Config: &api.EnvironmentConfig{ 61 Upstream: upstream, 62 EnvironmentGroup: &groupName, 63 }, 64 Locks: map[string]*api.Lock{}, 65 Applications: apps, 66 DistanceToUpstream: 0, 67 Priority: api.Priority_UPSTREAM, // we are 1 away from prod, hence pre-prod 68 } 69 } 70 71 func makeApp(appName string, version uint64) *api.Environment_Application { 72 return &api.Environment_Application{ 73 Name: appName, 74 Version: version, 75 Locks: nil, 76 QueuedVersion: 0, 77 UndeployVersion: false, 78 ArgoCd: nil, 79 } 80 } 81 func makeEnvGroup(envGroupName string, environments []*api.Environment) *api.EnvironmentGroup { 82 return &api.EnvironmentGroup{ 83 EnvironmentGroupName: envGroupName, 84 Environments: environments, 85 DistanceToUpstream: 0, 86 } 87 } 88 89 func makeUpstreamLatest() *api.EnvironmentConfig_Upstream { 90 f := true 91 return &api.EnvironmentConfig_Upstream{ 92 Latest: &f, 93 } 94 } 95 96 func makeUpstreamEnv(upstream string) *api.EnvironmentConfig_Upstream { 97 return &api.EnvironmentConfig_Upstream{ 98 Environment: &upstream, 99 } 100 } 101 102 func TestCalculateWarnings(t *testing.T) { 103 var dev = "dev" 104 tcs := []struct { 105 Name string 106 AppName string 107 Groups []*api.EnvironmentGroup 108 ExpectedWarnings []*api.Warning 109 }{ 110 { 111 Name: "no envs - no warning", 112 AppName: "foo", 113 Groups: []*api.EnvironmentGroup{ 114 makeEnvGroup(dev, []*api.Environment{ 115 makeEnv("dev-de", dev, makeUpstreamLatest(), nil), 116 })}, 117 ExpectedWarnings: []*api.Warning{}, 118 }, 119 { 120 Name: "app deployed in higher version on upstream should warn", 121 AppName: "foo", 122 Groups: []*api.EnvironmentGroup{ 123 makeEnvGroup(dev, []*api.Environment{ 124 makeEnv("prod", dev, makeUpstreamEnv("dev"), 125 makeApps(makeApp("foo", 2))), 126 }), 127 makeEnvGroup(dev, []*api.Environment{ 128 makeEnv("dev", dev, makeUpstreamLatest(), 129 makeApps(makeApp("foo", 1))), 130 }), 131 }, 132 ExpectedWarnings: []*api.Warning{ 133 { 134 WarningType: &api.Warning_UnusualDeploymentOrder{ 135 UnusualDeploymentOrder: &api.UnusualDeploymentOrder{ 136 UpstreamVersion: 1, 137 UpstreamEnvironment: "dev", 138 ThisVersion: 2, 139 ThisEnvironment: "prod", 140 }, 141 }, 142 }, 143 }, 144 }, 145 { 146 Name: "app deployed in same version on upstream should not warn", 147 AppName: "foo", 148 Groups: []*api.EnvironmentGroup{ 149 makeEnvGroup(dev, []*api.Environment{ 150 makeEnv("prod", dev, makeUpstreamEnv("dev"), 151 makeApps(makeApp("foo", 2))), 152 }), 153 makeEnvGroup(dev, []*api.Environment{ 154 makeEnv("dev", dev, makeUpstreamLatest(), 155 makeApps(makeApp("foo", 2))), 156 }), 157 }, 158 ExpectedWarnings: []*api.Warning{}, 159 }, 160 { 161 Name: "app deployed in no version on upstream should warn", 162 AppName: "foo", 163 Groups: []*api.EnvironmentGroup{ 164 makeEnvGroup(dev, []*api.Environment{ 165 makeEnv("prod", dev, makeUpstreamEnv("dev"), 166 makeApps(makeApp("foo", 1))), 167 }), 168 makeEnvGroup(dev, []*api.Environment{ 169 makeEnv("dev", dev, makeUpstreamLatest(), 170 makeApps()), 171 }), 172 }, 173 ExpectedWarnings: []*api.Warning{ 174 { 175 WarningType: &api.Warning_UpstreamNotDeployed{ 176 UpstreamNotDeployed: &api.UpstreamNotDeployed{ 177 UpstreamEnvironment: "dev", 178 ThisVersion: 1, 179 ThisEnvironment: "prod", 180 }, 181 }, 182 }, 183 }, 184 }, 185 } 186 for _, tc := range tcs { 187 tc := tc 188 t.Run(tc.Name, func(t *testing.T) { 189 actualWarnings := CalculateWarnings(testutil.MakeTestContext(), tc.AppName, tc.Groups) 190 if len(actualWarnings) != len(tc.ExpectedWarnings) { 191 t.Errorf("Different number of warnings. got: %s\nwant: %s", actualWarnings, tc.ExpectedWarnings) 192 } 193 for i := 0; i < len(actualWarnings); i++ { 194 actualWarning := actualWarnings[i] 195 expectedWarning := tc.ExpectedWarnings[i] 196 if diff := cmp.Diff(actualWarning.String(), expectedWarning.String()); diff != "" { 197 t.Errorf("Different warning at index [%d]:\ngot: %s\nwant: %s", i, actualWarning, expectedWarning) 198 } 199 } 200 }) 201 } 202 203 } 204 205 func TestOverviewService(t *testing.T) { 206 var dev = "dev" 207 tcs := []struct { 208 Name string 209 Setup []repository.Transformer 210 Test func(t *testing.T, svc *OverviewServiceServer) 211 }{ 212 { 213 Name: "A simple overview works", 214 Setup: []repository.Transformer{ 215 &repository.CreateEnvironment{ 216 Environment: "development", 217 Config: config.EnvironmentConfig{ 218 Upstream: &config.EnvironmentConfigUpstream{ 219 Latest: true, 220 }, 221 ArgoCd: nil, 222 EnvironmentGroup: &dev, 223 }, 224 }, 225 &repository.CreateEnvironment{ 226 Environment: "staging", 227 Config: config.EnvironmentConfig{ 228 Upstream: &config.EnvironmentConfigUpstream{ 229 Environment: "development", 230 }, 231 }, 232 }, 233 &repository.CreateEnvironment{ 234 Environment: "production", 235 Config: config.EnvironmentConfig{ 236 Upstream: &config.EnvironmentConfigUpstream{ 237 Environment: "staging", 238 }, 239 }, 240 }, 241 &repository.CreateApplicationVersion{ 242 Application: "test", 243 Manifests: map[string]string{ 244 "development": "dev", 245 }, 246 SourceAuthor: "example <example@example.com>", 247 SourceCommitId: "deadbeef", 248 SourceMessage: "changed something (#678)", 249 SourceRepoUrl: "testing@testing.com/abc", 250 }, 251 &repository.CreateApplicationVersion{ 252 Application: "test-with-team", 253 Manifests: map[string]string{ 254 "development": "dev", 255 }, 256 Team: "test-team", 257 }, 258 &repository.CreateApplicationVersion{ 259 Application: "test-with-incorrect-pr-number", 260 Manifests: map[string]string{ 261 "development": "dev", 262 }, 263 SourceAuthor: "example <example@example.com>", 264 SourceCommitId: "deadbeef", 265 SourceMessage: "changed something (#678", 266 SourceRepoUrl: "testing@testing.com/abc", 267 }, 268 &repository.CreateApplicationVersion{ 269 Application: "test-with-only-pr-number", 270 Manifests: map[string]string{ 271 "development": "dev", 272 }, 273 SourceAuthor: "example <example@example.com>", 274 SourceCommitId: "deadbeef", 275 SourceMessage: "(#678)", 276 SourceRepoUrl: "testing@testing.com/abc", 277 }, 278 &repository.DeployApplicationVersion{ 279 Application: "test", 280 Environment: "development", 281 Version: 1, 282 }, 283 &repository.DeployApplicationVersion{ 284 Application: "test-with-team", 285 Environment: "development", 286 Version: 1, 287 }, 288 &repository.CreateEnvironmentLock{ 289 Environment: "development", 290 LockId: "manual", 291 Message: "please", 292 }, 293 &repository.CreateEnvironmentApplicationLock{ 294 Environment: "production", 295 Application: "test", 296 LockId: "manual", 297 Message: "no", 298 }, 299 }, 300 Test: func(t *testing.T, svc *OverviewServiceServer) { 301 var ctx = auth.WriteUserToContext(testutil.MakeTestContext(), auth.User{ 302 Email: "test-email@example.com", 303 Name: "overview tester", 304 }) 305 resp, err := svc.GetOverview(ctx, &api.GetOverviewRequest{}) 306 if err != nil { 307 t.Fatal(err) 308 } 309 if resp.GitRevision == "" { 310 t.Errorf("expected non-empty git revision but was empty") 311 } 312 313 const expectedEnvs = 3 314 if len(resp.EnvironmentGroups) != expectedEnvs { 315 t.Errorf("expected %d environmentGroups, got %q", expectedEnvs, resp.EnvironmentGroups) 316 } 317 testApp := resp.Applications["test"] 318 if testApp.SourceRepoUrl != "testing@testing.com/abc" { 319 t.Errorf("Expected \"testing@testing.com/abc\", but got %#q", resp.Applications["test"].SourceRepoUrl) 320 } 321 releases := testApp.Releases 322 if len(releases) != 1 { 323 t.Errorf("Expected one release, but got %#q", len(releases)) 324 } 325 if releases[0].PrNumber != "678" { 326 t.Errorf("Release should have PR number \"678\", but got %q", releases[0].PrNumber) 327 } 328 testApp = resp.Applications["test-with-team"] 329 if testApp.SourceRepoUrl != "" { 330 t.Errorf("Expected \"\", but got %#q", resp.Applications["test"].SourceRepoUrl) 331 } 332 releases = testApp.Releases 333 if len(releases) != 1 { 334 t.Errorf("Expected one release, but got %#q", len(releases)) 335 } 336 if releases[0].PrNumber != "" { 337 t.Errorf("Release should not have PR number") 338 } 339 testApp = resp.Applications["test-with-incorrect-pr-number"] 340 releases = testApp.Releases 341 if len(releases) != 1 { 342 t.Errorf("Expected one release, but got %#q", len(releases)) 343 } 344 if releases[0].PrNumber != "" { 345 t.Errorf("Release should not have PR number since is an invalid PR number") 346 } 347 testApp = resp.Applications["test-with-only-pr-number"] 348 releases = testApp.Releases 349 if len(releases) != 1 { 350 t.Errorf("Expected one release, but got %#q", len(releases)) 351 } 352 if releases[0].PrNumber == "" { 353 t.Errorf("Release should have PR number \"678\", but got %q", releases[0].PrNumber) 354 } 355 356 // Check Dev 357 // Note that EnvironmentGroups are sorted, so it's dev,staging,production (see MapEnvironmentsToGroups for details on sorting) 358 devGroup := resp.EnvironmentGroups[0] 359 if devGroup.EnvironmentGroupName != "dev" { 360 t.Errorf("dev environmentGroup has wrong name: %q", devGroup.EnvironmentGroupName) 361 } 362 dev := devGroup.Environments[0] 363 if dev.Name != "development" { 364 t.Errorf("development environment has wrong name: %q", dev.Name) 365 } 366 if dev.Config.Upstream == nil { 367 t.Errorf("development environment has wrong upstream: %#v", dev.Config.Upstream) 368 } else { 369 if !dev.Config.Upstream.GetLatest() { 370 t.Errorf("production environment has wrong upstream: %#v", dev.Config.Upstream) 371 } 372 } 373 374 if len(dev.Locks) != 1 { 375 t.Errorf("development environment has wrong locks: %#v", dev.Locks) 376 } 377 if lck, ok := dev.Locks["manual"]; !ok { 378 t.Errorf("development environment doesn't contain manual lock: %#v", dev.Locks) 379 } else { 380 if lck.Message != "please" { 381 t.Errorf("development environment manual lock has wrong message: %q", lck.Message) 382 } 383 } 384 if len(dev.Applications) != 4 { 385 t.Errorf("development environment has wrong applications: %#v", dev.Applications) 386 } 387 if app, ok := dev.Applications["test"]; !ok { 388 t.Errorf("development environment has wrong applications: %#v", dev.Applications) 389 } else { 390 if app.Version != 1 { 391 t.Errorf("test application has not version 1 but %d", app.Version) 392 } 393 if len(app.Locks) != 0 { 394 t.Errorf("test application has locks in development: %#v", app.Locks) 395 } 396 } 397 398 got := dev.Applications["test"].GetDeploymentMetaData().DeployAuthor 399 if got != "test tester" { 400 t.Errorf("development environment deployment did not create deploymentMetaData, got %s", got) 401 } 402 403 // Check staging 404 stageGroup := resp.EnvironmentGroups[1] 405 if stageGroup.EnvironmentGroupName != "staging" { 406 t.Errorf("staging environmentGroup has wrong name: %q", stageGroup.EnvironmentGroupName) 407 } 408 stage := stageGroup.Environments[0] 409 if stage.Name != "staging" { 410 t.Errorf("staging environment has wrong name: %q", stage.Name) 411 } 412 if stage.Config.Upstream == nil { 413 t.Errorf("staging environment has wrong upstream: %#v", stage.Config.Upstream) 414 } else { 415 if stage.Config.Upstream.GetEnvironment() != "development" { 416 t.Errorf("staging environment has wrong upstream: %#v", stage.Config.Upstream) 417 } 418 if stage.Config.Upstream.GetLatest() { 419 t.Errorf("staging environment has wrong upstream: %#v", stage.Config.Upstream) 420 } 421 } 422 if len(stage.Locks) != 0 { 423 t.Errorf("staging environment has wrong locks: %#v", stage.Locks) 424 } 425 if len(stage.Applications) != 0 { 426 t.Errorf("staging environment has wrong applications: %#v", stage.Applications) 427 } 428 429 // Check production 430 prodGroup := resp.EnvironmentGroups[2] 431 if prodGroup.EnvironmentGroupName != "production" { 432 t.Errorf("prod environmentGroup has wrong name: %q", prodGroup.EnvironmentGroupName) 433 } 434 prod := prodGroup.Environments[0] 435 if prod.Name != "production" { 436 t.Errorf("production environment has wrong name: %q", prod.Name) 437 } 438 if prod.Config.Upstream == nil { 439 t.Errorf("production environment has wrong upstream: %#v", prod.Config.Upstream) 440 } else { 441 if prod.Config.Upstream.GetEnvironment() != "staging" { 442 t.Errorf("production environment has wrong upstream: %#v", prod.Config.Upstream) 443 } 444 if prod.Config.Upstream.GetLatest() { 445 t.Errorf("production environment has wrong upstream: %#v", prod.Config.Upstream) 446 } 447 } 448 if len(prod.Locks) != 0 { 449 t.Errorf("production environment has wrong locks: %#v", prod.Locks) 450 } 451 if len(prod.Applications) != 1 { 452 t.Errorf("production environment has wrong applications: %#v", prod.Applications) 453 } 454 if app, ok := prod.Applications["test"]; !ok { 455 t.Errorf("production environment has wrong applications: %#v", prod.Applications) 456 } else { 457 if app.Version != 0 { 458 t.Errorf("test application has not version 0 but %d", app.Version) 459 } 460 if len(app.Locks) != 1 { 461 t.Errorf("test application has locks in production: %#v", app.Locks) 462 } 463 } 464 465 // Check applications 466 if len(resp.Applications) != 4 { 467 t.Errorf("expected two application, got %#v", resp.Applications) 468 } 469 if test, ok := resp.Applications["test"]; !ok { 470 t.Errorf("test application is missing in %#v", resp.Applications) 471 } else { 472 if test.Name != "test" { 473 t.Errorf("test applications name is not test but %q", test.Name) 474 } 475 if len(test.Releases) != 1 { 476 t.Errorf("expected one release, got %#v", test.Releases) 477 } 478 if test.Releases[0].Version != 1 { 479 t.Errorf("expected test release version to be 1, but got %d", test.Releases[0].Version) 480 } 481 if test.Releases[0].SourceAuthor != "example <example@example.com>" { 482 t.Errorf("expected test source author to be \"example <example@example.com>\", but got %q", test.Releases[0].SourceAuthor) 483 } 484 if test.Releases[0].SourceMessage != "changed something (#678)" { 485 t.Errorf("expected test source message to be \"changed something\", but got %q", test.Releases[0].SourceMessage) 486 } 487 if test.Releases[0].SourceCommitId != "deadbeef" { 488 t.Errorf("expected test source commit id to be \"deadbeef\", but got %q", test.Releases[0].SourceCommitId) 489 } 490 } 491 if testWithTeam, ok := resp.Applications["test-with-team"]; !ok { 492 t.Errorf("test-with-team application is missing in %#v", resp.Applications) 493 } else { 494 if testWithTeam.Team != "test-team" { 495 t.Errorf("application team is not test-team but %q", testWithTeam.Team) 496 } 497 } 498 }, 499 }, 500 { 501 Name: "A stream overview works", 502 Setup: []repository.Transformer{ 503 &repository.CreateEnvironment{ 504 Environment: "development", 505 Config: config.EnvironmentConfig{}, 506 }, 507 &repository.CreateApplicationVersion{ 508 Application: "test", 509 Manifests: map[string]string{ 510 "development": "v1", 511 }, 512 }, 513 &repository.CreateApplicationVersion{ 514 Application: "test", 515 Manifests: map[string]string{ 516 "development": "v2", 517 }, 518 }, 519 &repository.DeployApplicationVersion{ 520 Application: "test", 521 Environment: "development", 522 Version: 1, 523 }, 524 }, 525 Test: func(t *testing.T, svc *OverviewServiceServer) { 526 ctx, cancel := context.WithCancel(testutil.MakeTestContext()) 527 ch := make(chan *api.GetOverviewResponse) 528 stream := mockOverviewService_StreamOverviewServer{ 529 Results: ch, 530 Ctx: ctx, 531 } 532 wg := sync.WaitGroup{} 533 wg.Add(1) 534 go func() { 535 defer wg.Done() 536 err := svc.StreamOverview(&api.GetOverviewRequest{}, &stream) 537 if err != nil { 538 t.Fatal(err) 539 } 540 }() 541 542 // Check that we get a first overview 543 overview1 := <-ch 544 if overview1 == nil { 545 t.Fatal("overview is nil") 546 } 547 v1 := overview1.GetEnvironmentGroups()[0].GetEnvironments()[0].GetApplications()["test"].Version 548 549 // Update a version and see that the version changed 550 err := svc.Repository.Apply(ctx, &repository.DeployApplicationVersion{ 551 Application: "test", 552 Environment: "development", 553 Version: 2, 554 }) 555 if err != nil { 556 t.Fatal(err) 557 } 558 559 // Check that the second overview is different 560 overview2 := <-ch 561 if overview2 == nil { 562 t.Fatal("overview is nil") 563 } 564 v2 := overview2.EnvironmentGroups[0].Environments[0].Applications["test"].Version 565 if v1 == v2 { 566 t.Fatalf("Versions are not different: %q vs %q", v1, v2) 567 } 568 569 if overview1.GitRevision == overview2.GitRevision { 570 t.Errorf("Git Revisions are not different: %q", overview1.GitRevision) 571 } 572 573 cancel() 574 wg.Wait() 575 }, 576 }, 577 } 578 for _, tc := range tcs { 579 tc := tc 580 t.Run(tc.Name, func(t *testing.T) { 581 shutdown := make(chan struct{}, 1) 582 repo, err := setupRepositoryTest(t) 583 if err != nil { 584 t.Fatal(err) 585 } 586 for _, tr := range tc.Setup { 587 if err := repo.Apply(testutil.MakeTestContext(), tr); err != nil { 588 t.Fatal(err) 589 } 590 } 591 svc := &OverviewServiceServer{ 592 Repository: repo, 593 Shutdown: shutdown, 594 } 595 tc.Test(t, svc) 596 close(shutdown) 597 }) 598 } 599 } 600 601 func TestOverviewServiceFromCommit(t *testing.T) { 602 type step struct { 603 Transformer repository.Transformer 604 } 605 tcs := []struct { 606 Name string 607 Steps []step 608 }{ 609 { 610 Name: "A simple overview works", 611 Steps: []step{ 612 { 613 Transformer: &repository.CreateEnvironment{ 614 Environment: "development", 615 Config: config.EnvironmentConfig{}, 616 }, 617 }, 618 { 619 Transformer: &repository.CreateEnvironment{ 620 Environment: "staging", 621 Config: config.EnvironmentConfig{ 622 Upstream: &config.EnvironmentConfigUpstream{ 623 Latest: true, 624 }, 625 }, 626 }, 627 }, 628 { 629 Transformer: &repository.CreateEnvironment{ 630 Environment: "production", 631 Config: config.EnvironmentConfig{ 632 Upstream: &config.EnvironmentConfigUpstream{ 633 Environment: "staging", 634 }, 635 }, 636 }, 637 }, 638 { 639 Transformer: &repository.CreateApplicationVersion{ 640 Application: "test", 641 Manifests: map[string]string{ 642 "development": "dev", 643 }, 644 SourceAuthor: "example <example@example.com>", 645 SourceCommitId: "deadbeef", 646 SourceMessage: "changed something (#678)", 647 SourceRepoUrl: "testing@testing.com/abc", 648 }, 649 }, 650 { 651 Transformer: &repository.CreateApplicationVersion{ 652 Application: "test-with-team", 653 Manifests: map[string]string{ 654 "development": "dev", 655 }, 656 Team: "test-team", 657 }, 658 }, 659 { 660 Transformer: &repository.CreateApplicationVersion{ 661 Application: "test-with-incorrect-pr-number", 662 Manifests: map[string]string{ 663 "development": "dev", 664 }, 665 SourceAuthor: "example <example@example.com>", 666 SourceCommitId: "deadbeef", 667 SourceMessage: "changed something (#678", 668 SourceRepoUrl: "testing@testing.com/abc", 669 }, 670 }, 671 { 672 Transformer: &repository.CreateApplicationVersion{ 673 Application: "test-with-only-pr-number", 674 Manifests: map[string]string{ 675 "development": "dev", 676 }, 677 SourceAuthor: "example <example@example.com>", 678 SourceCommitId: "deadbeef", 679 SourceMessage: "(#678)", 680 SourceRepoUrl: "testing@testing.com/abc", 681 }, 682 }, 683 { 684 Transformer: &repository.DeployApplicationVersion{ 685 Application: "test", 686 Environment: "development", 687 Version: 1, 688 }, 689 }, 690 { 691 Transformer: &repository.DeployApplicationVersion{ 692 Application: "test-with-team", 693 Environment: "development", 694 Version: 1, 695 }, 696 }, 697 { 698 Transformer: &repository.CreateEnvironmentLock{ 699 Environment: "development", 700 LockId: "manual", 701 Message: "please", 702 }, 703 }, 704 { 705 Transformer: &repository.CreateEnvironmentApplicationLock{ 706 Environment: "production", 707 Application: "test", 708 LockId: "manual", 709 Message: "no", 710 }, 711 }, 712 }, 713 }, 714 } 715 for _, tc := range tcs { 716 tc := tc 717 t.Run(tc.Name, func(t *testing.T) { 718 shutdown := make(chan struct{}, 1) 719 repo, err := setupRepositoryTest(t) 720 if err != nil { 721 t.Fatal(err) 722 } 723 svc := &OverviewServiceServer{ 724 Repository: repo, 725 Shutdown: shutdown, 726 } 727 728 ov, err := svc.GetOverview(testutil.MakeTestContext(), &api.GetOverviewRequest{}) 729 if err != nil { 730 t.Errorf("expected no error, got %s", err) 731 } 732 if ov.GitRevision != "" { 733 t.Errorf("expected git revision to be empty, got %q", ov.GitRevision) 734 } 735 revisions := map[string]*api.GetOverviewResponse{} 736 for _, tr := range tc.Steps { 737 if err := repo.Apply(testutil.MakeTestContext(), tr.Transformer); err != nil { 738 t.Fatal(err) 739 } 740 ov, err = svc.GetOverview(testutil.MakeTestContext(), &api.GetOverviewRequest{}) 741 if err != nil { 742 t.Errorf("expected no error, got %s", err) 743 } 744 if ov.GitRevision == "" { 745 t.Errorf("expected git revision to be non-empty") 746 } 747 if revisions[ov.GitRevision] != nil { 748 t.Errorf("git revision was observed twice: %q", ov.GitRevision) 749 } 750 revisions[ov.GitRevision] = ov 751 } 752 for rev := range revisions { 753 ov, err = svc.GetOverview(testutil.MakeTestContext(), &api.GetOverviewRequest{GitRevision: rev}) 754 if err != nil { 755 t.Errorf("expected no error, got %s", err) 756 } 757 if ov.GitRevision != rev { 758 t.Errorf("expected git revision to be %q, but got %q", rev, ov.GitRevision) 759 } 760 } 761 close(shutdown) 762 }) 763 } 764 } 765 766 func groupFromEnvs(environments []*api.Environment) []*api.EnvironmentGroup { 767 return []*api.EnvironmentGroup{ 768 { 769 EnvironmentGroupName: "group1", 770 Environments: environments, 771 }, 772 } 773 } 774 775 func TestDeriveUndeploySummary(t *testing.T) { 776 var tcs = []struct { 777 Name string 778 AppName string 779 groups []*api.EnvironmentGroup 780 ExpectedResult api.UndeploySummary 781 }{ 782 { 783 Name: "No Environments", 784 AppName: "foo", 785 groups: []*api.EnvironmentGroup{}, 786 ExpectedResult: api.UndeploySummary_UNDEPLOY, 787 }, 788 { 789 Name: "one Environment but no Application", 790 AppName: "foo", 791 groups: groupFromEnvs([]*api.Environment{ 792 { 793 Applications: map[string]*api.Environment_Application{ 794 "bar": { // different app 795 UndeployVersion: true, 796 Version: 666, 797 }, 798 }, 799 }, 800 }), 801 ExpectedResult: api.UndeploySummary_UNDEPLOY, 802 }, 803 { 804 Name: "One Env with undeploy", 805 AppName: "foo", 806 groups: groupFromEnvs([]*api.Environment{ 807 { 808 Applications: map[string]*api.Environment_Application{ 809 "foo": { 810 UndeployVersion: true, 811 Version: 666, 812 }, 813 }, 814 }, 815 }), 816 ExpectedResult: api.UndeploySummary_UNDEPLOY, 817 }, 818 { 819 Name: "One Env with normal version", 820 AppName: "foo", 821 groups: groupFromEnvs([]*api.Environment{ 822 { 823 Applications: map[string]*api.Environment_Application{ 824 "foo": { 825 UndeployVersion: false, 826 Version: 666, 827 }, 828 }, 829 }, 830 }), 831 ExpectedResult: api.UndeploySummary_NORMAL, 832 }, 833 { 834 Name: "Two Envs all undeploy", 835 AppName: "foo", 836 groups: groupFromEnvs([]*api.Environment{ 837 { 838 Applications: map[string]*api.Environment_Application{ 839 "foo": { 840 UndeployVersion: true, 841 Version: 666, 842 }, 843 }, 844 }, 845 { 846 Applications: map[string]*api.Environment_Application{ 847 "foo": { 848 UndeployVersion: true, 849 Version: 666, 850 }, 851 }, 852 }, 853 }), 854 ExpectedResult: api.UndeploySummary_UNDEPLOY, 855 }, 856 { 857 Name: "Two Envs all normal", 858 AppName: "foo", 859 groups: groupFromEnvs([]*api.Environment{ 860 { 861 Applications: map[string]*api.Environment_Application{ 862 "foo": { 863 UndeployVersion: false, 864 Version: 666, 865 }, 866 }, 867 }, 868 { 869 Applications: map[string]*api.Environment_Application{ 870 "foo": { 871 UndeployVersion: false, 872 Version: 666, 873 }, 874 }, 875 }, 876 }), 877 ExpectedResult: api.UndeploySummary_NORMAL, 878 }, 879 { 880 Name: "Two Envs all different", 881 AppName: "foo", 882 groups: groupFromEnvs([]*api.Environment{ 883 { 884 Applications: map[string]*api.Environment_Application{ 885 "foo": { 886 UndeployVersion: true, 887 Version: 666, 888 }, 889 }, 890 }, 891 { 892 Applications: map[string]*api.Environment_Application{ 893 "foo": { 894 UndeployVersion: false, 895 Version: 666, 896 }, 897 }, 898 }, 899 }), 900 ExpectedResult: api.UndeploySummary_MIXED, 901 }, 902 } 903 for _, tc := range tcs { 904 t.Run(tc.Name, func(t *testing.T) { 905 actualResult := deriveUndeploySummary(tc.AppName, tc.groups) 906 if !cmp.Equal(tc.ExpectedResult, actualResult) { 907 t.Fatal("Output mismatch (-want +got):\n", cmp.Diff(tc.ExpectedResult, actualResult)) 908 } 909 }) 910 } 911 }