github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/cd-service/pkg/service/release_train_prognosis_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  
    22  	"testing"
    23  
    24  	"github.com/freiheit-com/kuberpult/services/cd-service/pkg/config"
    25  	"github.com/freiheit-com/kuberpult/services/cd-service/pkg/repository/testutil"
    26  	"google.golang.org/grpc/codes"
    27  	"google.golang.org/grpc/status"
    28  	"google.golang.org/protobuf/proto"
    29  
    30  	api "github.com/freiheit-com/kuberpult/pkg/api/v1"
    31  	"github.com/freiheit-com/kuberpult/pkg/ptr"
    32  	rp "github.com/freiheit-com/kuberpult/services/cd-service/pkg/repository"
    33  )
    34  
    35  func TestReleaseTrainPrognosis(t *testing.T) {
    36  	environmentSetup := []rp.Transformer{
    37  		&rp.CreateEnvironment{
    38  			Environment: "development-1",
    39  			Config: config.EnvironmentConfig{
    40  				Upstream: &config.EnvironmentConfigUpstream{
    41  					Environment: "",
    42  					Latest:      true,
    43  				},
    44  				EnvironmentGroup: ptr.FromString("development"),
    45  			},
    46  		},
    47  		&rp.CreateEnvironment{
    48  			Environment: "development-2",
    49  			Config: config.EnvironmentConfig{
    50  				Upstream: &config.EnvironmentConfigUpstream{
    51  					Environment: "",
    52  					Latest:      true,
    53  				},
    54  				EnvironmentGroup: ptr.FromString("development"),
    55  			},
    56  		},
    57  		&rp.CreateEnvironment{
    58  			Environment: "development-3",
    59  			Config: config.EnvironmentConfig{
    60  				Upstream: &config.EnvironmentConfigUpstream{
    61  					Environment: "",
    62  					Latest:      true,
    63  				},
    64  				EnvironmentGroup: ptr.FromString("development"),
    65  			},
    66  		},
    67  		&rp.CreateEnvironment{
    68  			Environment: "staging-1",
    69  			Config: config.EnvironmentConfig{
    70  				Upstream: &config.EnvironmentConfigUpstream{
    71  					Environment: "development-1",
    72  					Latest:      false,
    73  				},
    74  				EnvironmentGroup: ptr.FromString("staging"),
    75  			},
    76  		},
    77  		&rp.CreateEnvironment{
    78  			Environment: "staging-2",
    79  			Config: config.EnvironmentConfig{
    80  				Upstream: &config.EnvironmentConfigUpstream{
    81  					Environment: "development-1", // CAREFUL, downstream from development-1, not development-2
    82  					Latest:      false,
    83  				},
    84  				EnvironmentGroup: ptr.FromString("staging"),
    85  			},
    86  		},
    87  		&rp.CreateEnvironment{
    88  			Environment: "staging-3",
    89  			Config: config.EnvironmentConfig{
    90  				Upstream: &config.EnvironmentConfigUpstream{
    91  					Environment: "development-3",
    92  					Latest:      false,
    93  				},
    94  				EnvironmentGroup: ptr.FromString("staging"),
    95  			},
    96  		},
    97  	}
    98  	type TestCase struct {
    99  		Name             string
   100  		Setup            []rp.Transformer
   101  		Request          *api.ReleaseTrainRequest
   102  		ExpectedResponse *api.GetReleaseTrainPrognosisResponse
   103  		ExpectedError    codes.Code
   104  	}
   105  
   106  	tcs := []TestCase{
   107  		{
   108  			Name:  "error with release train missing environment",
   109  			Setup: []rp.Transformer{},
   110  			Request: &api.ReleaseTrainRequest{
   111  				Target: "non-existent environment",
   112  			},
   113  			ExpectedResponse: nil,
   114  			ExpectedError:    codes.InvalidArgument,
   115  		},
   116  		{
   117  			Name: "some environment is skipped",
   118  			Setup: []rp.Transformer{
   119  				&rp.CreateEnvironmentLock{
   120  					Environment: "staging-1",
   121  					LockId:      "staging-1-lock",
   122  				},
   123  			},
   124  			Request: &api.ReleaseTrainRequest{
   125  				Target: "staging",
   126  			},
   127  			ExpectedResponse: &api.GetReleaseTrainPrognosisResponse{
   128  				EnvsPrognoses: map[string]*api.ReleaseTrainEnvPrognosis{
   129  					"staging-1": &api.ReleaseTrainEnvPrognosis{
   130  						Outcome: &api.ReleaseTrainEnvPrognosis_SkipCause{
   131  							SkipCause: api.ReleaseTrainEnvSkipCause_ENV_IS_LOCKED,
   132  						},
   133  					},
   134  					"staging-2": &api.ReleaseTrainEnvPrognosis{
   135  						Outcome: &api.ReleaseTrainEnvPrognosis_AppsPrognoses{
   136  							AppsPrognoses: &api.ReleaseTrainEnvPrognosis_AppsPrognosesWrapper{
   137  								Prognoses: map[string]*api.ReleaseTrainAppPrognosis{},
   138  							},
   139  						},
   140  					},
   141  					"staging-3": &api.ReleaseTrainEnvPrognosis{
   142  						Outcome: &api.ReleaseTrainEnvPrognosis_AppsPrognoses{
   143  							AppsPrognoses: &api.ReleaseTrainEnvPrognosis_AppsPrognosesWrapper{
   144  								Prognoses: map[string]*api.ReleaseTrainAppPrognosis{},
   145  							},
   146  						},
   147  					},
   148  				},
   149  			},
   150  			ExpectedError: codes.OK,
   151  		},
   152  		{
   153  			Name: "some application is skipped",
   154  			Setup: []rp.Transformer{
   155  				&rp.CreateApplicationVersion{
   156  					Application: "potato-app",
   157  					Manifests: map[string]string{
   158  						"development-1": "",
   159  						"staging-1":     "",
   160  					},
   161  				},
   162  				&rp.CreateApplicationVersion{
   163  					Application: "potato-app",
   164  					Manifests: map[string]string{
   165  						"development-1": "",
   166  						"staging-1":     "",
   167  					},
   168  				},
   169  				&rp.DeployApplicationVersion{
   170  					Environment: "development-1",
   171  					Application: "potato-app",
   172  					Version:     2,
   173  				},
   174  				&rp.DeployApplicationVersion{
   175  					Environment: "staging-1",
   176  					Application: "potato-app",
   177  					Version:     1,
   178  				},
   179  				&rp.CreateEnvironmentApplicationLock{
   180  					Environment: "staging-1",
   181  					Application: "potato-app",
   182  					LockId:      "staging-1-potato-app-lock",
   183  				},
   184  			},
   185  			Request: &api.ReleaseTrainRequest{
   186  				Target: "staging",
   187  			},
   188  			ExpectedResponse: &api.GetReleaseTrainPrognosisResponse{
   189  				EnvsPrognoses: map[string]*api.ReleaseTrainEnvPrognosis{
   190  					"staging-1": &api.ReleaseTrainEnvPrognosis{
   191  						Outcome: &api.ReleaseTrainEnvPrognosis_AppsPrognoses{
   192  							AppsPrognoses: &api.ReleaseTrainEnvPrognosis_AppsPrognosesWrapper{
   193  								Prognoses: map[string]*api.ReleaseTrainAppPrognosis{
   194  									"potato-app": &api.ReleaseTrainAppPrognosis{
   195  										Outcome: &api.ReleaseTrainAppPrognosis_SkipCause{
   196  											SkipCause: api.ReleaseTrainAppSkipCause_APP_IS_LOCKED,
   197  										},
   198  									},
   199  								},
   200  							},
   201  						},
   202  					},
   203  					"staging-2": &api.ReleaseTrainEnvPrognosis{
   204  						Outcome: &api.ReleaseTrainEnvPrognosis_AppsPrognoses{
   205  							AppsPrognoses: &api.ReleaseTrainEnvPrognosis_AppsPrognosesWrapper{
   206  								Prognoses: map[string]*api.ReleaseTrainAppPrognosis{
   207  									"potato-app": &api.ReleaseTrainAppPrognosis{
   208  										Outcome: &api.ReleaseTrainAppPrognosis_SkipCause{
   209  											SkipCause: api.ReleaseTrainAppSkipCause_APP_DOES_NOT_EXIST_IN_ENV,
   210  										},
   211  									},
   212  								},
   213  							},
   214  						},
   215  					},
   216  					"staging-3": &api.ReleaseTrainEnvPrognosis{
   217  						Outcome: &api.ReleaseTrainEnvPrognosis_AppsPrognoses{
   218  							AppsPrognoses: &api.ReleaseTrainEnvPrognosis_AppsPrognosesWrapper{
   219  								Prognoses: map[string]*api.ReleaseTrainAppPrognosis{},
   220  							},
   221  						},
   222  					},
   223  				},
   224  			},
   225  			ExpectedError: codes.OK,
   226  		},
   227  
   228  		{
   229  			Name: "proper release train",
   230  			Setup: []rp.Transformer{
   231  				&rp.CreateApplicationVersion{
   232  					Application: "potato-app",
   233  					Manifests: map[string]string{
   234  						"development-1": "",
   235  						"staging-1":     "",
   236  					},
   237  				},
   238  				&rp.CreateApplicationVersion{
   239  					Application: "potato-app",
   240  					Manifests: map[string]string{
   241  						"development-1": "",
   242  						"staging-1":     "",
   243  					},
   244  				},
   245  				&rp.DeployApplicationVersion{
   246  					Environment: "development-1",
   247  					Application: "potato-app",
   248  					Version:     2,
   249  				},
   250  				&rp.DeployApplicationVersion{
   251  					Environment: "staging-1",
   252  					Application: "potato-app",
   253  					Version:     1,
   254  				},
   255  			},
   256  			Request: &api.ReleaseTrainRequest{
   257  				Target: "staging",
   258  			},
   259  			ExpectedResponse: &api.GetReleaseTrainPrognosisResponse{
   260  				EnvsPrognoses: map[string]*api.ReleaseTrainEnvPrognosis{
   261  					"staging-1": &api.ReleaseTrainEnvPrognosis{
   262  						Outcome: &api.ReleaseTrainEnvPrognosis_AppsPrognoses{
   263  							AppsPrognoses: &api.ReleaseTrainEnvPrognosis_AppsPrognosesWrapper{
   264  								Prognoses: map[string]*api.ReleaseTrainAppPrognosis{
   265  									"potato-app": &api.ReleaseTrainAppPrognosis{
   266  										Outcome: &api.ReleaseTrainAppPrognosis_DeployedVersion{
   267  											DeployedVersion: 2,
   268  										},
   269  									},
   270  								},
   271  							},
   272  						},
   273  					},
   274  					"staging-2": &api.ReleaseTrainEnvPrognosis{
   275  						Outcome: &api.ReleaseTrainEnvPrognosis_AppsPrognoses{
   276  							AppsPrognoses: &api.ReleaseTrainEnvPrognosis_AppsPrognosesWrapper{
   277  								Prognoses: map[string]*api.ReleaseTrainAppPrognosis{
   278  									"potato-app": &api.ReleaseTrainAppPrognosis{
   279  										Outcome: &api.ReleaseTrainAppPrognosis_SkipCause{
   280  											SkipCause: api.ReleaseTrainAppSkipCause_APP_DOES_NOT_EXIST_IN_ENV,
   281  										},
   282  									},
   283  								},
   284  							},
   285  						},
   286  					},
   287  					"staging-3": &api.ReleaseTrainEnvPrognosis{
   288  						Outcome: &api.ReleaseTrainEnvPrognosis_AppsPrognoses{
   289  							AppsPrognoses: &api.ReleaseTrainEnvPrognosis_AppsPrognosesWrapper{
   290  								Prognoses: map[string]*api.ReleaseTrainAppPrognosis{},
   291  							},
   292  						},
   293  					},
   294  				},
   295  			},
   296  			ExpectedError: codes.OK,
   297  		},
   298  	}
   299  
   300  	for _, tc := range tcs {
   301  		tc := tc
   302  		t.Run(tc.Name, func(t *testing.T) {
   303  			repo, err := setupRepositoryTest(t)
   304  			if err != nil {
   305  				t.Fatalf("error setting up repository test: %v", err)
   306  			}
   307  
   308  			err = repo.Apply(testutil.MakeTestContext(), environmentSetup...)
   309  			if err != nil {
   310  				t.Fatalf("error during setup, error: %v", err)
   311  			}
   312  			err = repo.Apply(testutil.MakeTestContext(), tc.Setup...)
   313  			if err != nil {
   314  				t.Fatalf("error during setup, error: %v", err)
   315  			}
   316  
   317  			sv := &ReleaseTrainPrognosisServer{Repository: repo}
   318  			resp, err := sv.GetReleaseTrainPrognosis(context.Background(), tc.Request)
   319  
   320  			if status.Code(err) != tc.ExpectedError {
   321  				t.Fatalf("expected error doesn't match actual error, expected %v, got code: %v, error: %v", tc.ExpectedError, status.Code(err), err)
   322  			}
   323  			if !proto.Equal(tc.ExpectedResponse, resp) {
   324  				t.Fatalf("expected respones doesn't match actualy response, expected %v, got %v", tc.ExpectedResponse, resp)
   325  			}
   326  		})
   327  	}
   328  }