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 }