github.com/GoogleCloudPlatform/deploystack@v1.12.8/tui/post_processors_test.go (about) 1 // Copyright 2023 Google LLC 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 tui 16 17 import ( 18 "fmt" 19 "reflect" 20 "testing" 21 22 "cloud.google.com/go/domains/apiv1beta1/domainspb" 23 "github.com/GoogleCloudPlatform/deploystack/gcloud" 24 tea "github.com/charmbracelet/bubbletea" 25 "github.com/stretchr/testify/assert" 26 ) 27 28 func TestProcessProjectSelection(t *testing.T) { 29 tests := map[string]struct { 30 in string 31 want tea.Msg 32 setError bool 33 err error 34 }{ 35 "basic": { 36 in: "testproject", 37 want: successMsg{}, 38 }, 39 "fail": { 40 in: "testproject", 41 want: errMsg{err: errForced}, 42 setError: true, 43 }, 44 } 45 46 for name, tc := range tests { 47 t.Run(name, func(t *testing.T) { 48 49 q := getTestQueue(appTitle, "test") 50 51 if tc.setError { 52 errMock := GetMock(0) 53 errMock.forceErr = true 54 q.client = errMock 55 } 56 57 cmd := processProjectSelection(tc.in, &q) 58 got := cmd() 59 if !reflect.DeepEqual(tc.want, got) { 60 t.Fatalf("expected: %+v, got: %+v", tc.want, got) 61 } 62 }) 63 } 64 } 65 66 func TestCheckYesOrNo(t *testing.T) { 67 tests := map[string]struct { 68 in string 69 want bool 70 }{ 71 "y": {in: "y", want: true}, 72 "yes": {in: "yes", want: true}, 73 "n": {in: "n", want: true}, 74 "no": {in: "no", want: true}, 75 "Y": {in: "Y", want: true}, 76 "YES": {in: "YES", want: true}, 77 "N": {in: "N", want: true}, 78 "NO": {in: "NO", want: true}, 79 "yEs": {in: "yEs", want: true}, 80 "noT": {in: "noT", want: false}, 81 "sadasdasf": {in: "sadasdasf", want: false}, 82 "ye": {in: "ye", want: true}, 83 "4567": {in: "4567", want: false}, 84 } 85 86 for name, tc := range tests { 87 t.Run(name, func(t *testing.T) { 88 got := checkYesOrNo(tc.in) 89 90 if tc.want != got { 91 t.Fatalf("%s - want '%t' got '%t'", tc.in, tc.want, got) 92 } 93 }) 94 } 95 } 96 97 func TestValidateYesOrNo(t *testing.T) { 98 tests := map[string]struct { 99 in string 100 msg tea.Msg 101 }{ 102 "y": {in: "y", msg: successMsg{}}, 103 "yes": {in: "yes", msg: successMsg{}}, 104 "n": {in: "n", msg: successMsg{}}, 105 "no": {in: "no", msg: successMsg{}}, 106 "Y": {in: "Y", msg: successMsg{}}, 107 "YES": {in: "YES", msg: successMsg{}}, 108 "N": {in: "N", msg: successMsg{}}, 109 "NO": {in: "NO", msg: successMsg{}}, 110 "yEs": {in: "yEs", msg: successMsg{}}, 111 "noT": {in: "noT", msg: errMsg{err: fmt.Errorf("Your answer '%s' is neither 'yes' nor 'no'", "noT")}}, 112 "sadasdasf": {in: "sadasdasf", msg: errMsg{err: fmt.Errorf("Your answer '%s' is neither 'yes' nor 'no'", "sadasdasf")}}, 113 "ye": {in: "ye", msg: successMsg{}}, 114 "4567": {in: "4567", msg: errMsg{err: fmt.Errorf("Your answer '%s' is neither 'yes' nor 'no'", "4567")}}, 115 } 116 117 for name, tc := range tests { 118 t.Run(name, func(t *testing.T) { 119 q := getTestQueue(appTitle, "test") 120 cmd := validateYesOrNo(tc.in, &q) 121 122 got := cmd() 123 124 switch tc.msg.(type) { 125 case successMsg: 126 if tc.msg != got { 127 t.Fatalf("%s - want: \n'%+v' \ngot: \n'%+v'", tc.in, tc.msg, got) 128 } 129 case errMsg: 130 gotE := got.(errMsg) 131 tcmsgE := tc.msg.(errMsg) 132 133 if tcmsgE.err.Error() != gotE.err.Error() { 134 t.Fatalf("want: \n'%+v' \ngot: \n'%+v'", tcmsgE.err.Error(), gotE.err.Error()) 135 } 136 137 } 138 }) 139 } 140 } 141 142 func TestValidatePhoneNumber(t *testing.T) { 143 tests := map[string]struct { 144 in string 145 msg tea.Msg 146 }{ 147 "Good": {"800 555 1234", successMsg{}}, 148 "Weird": {"d746fd83843", successMsg{}}, 149 "BAD": {"dghdhdfuejfhfhfhrghfhfhdhgreh", errMsg{err: fmt.Errorf("Your answer '%s' is not a valid phone number. Please try again", "dghdhdfuejfhfhfhrghfhfhdhgreh")}}, 150 } 151 152 for name, tc := range tests { 153 t.Run(name, func(t *testing.T) { 154 q := getTestQueue(appTitle, "test") 155 cmd := validatePhoneNumber(tc.in, &q) 156 157 got := cmd() 158 159 switch tc.msg.(type) { 160 case successMsg: 161 if tc.msg != got { 162 t.Fatalf("%s - want: \n'%+v' \ngot: \n'%+v'", tc.in, tc.msg, got) 163 } 164 case errMsg: 165 gotE := got.(errMsg) 166 tcmsgE := tc.msg.(errMsg) 167 168 if tcmsgE.err.Error() != gotE.err.Error() { 169 t.Fatalf("want: \n'%+v' \ngot: \n'%+v'", tcmsgE.err.Error(), gotE.err.Error()) 170 } 171 172 } 173 }) 174 } 175 } 176 177 func TestMassagePhoneNumber(t *testing.T) { 178 tests := map[string]struct { 179 input string 180 want string 181 err error 182 }{ 183 "Good": {"800 555 1234", "+1.8005551234", nil}, 184 "Weird": {"d746fd83843", "+1.74683843", nil}, 185 "BAD": {"dghdhdfuejfhfhfhrghfhfhdhgreh", "", ErrorCustomNotValidPhoneNumber}, 186 } 187 188 for name, tc := range tests { 189 t.Run(name, func(t *testing.T) { 190 got, err := massagePhoneNumber(tc.input) 191 if err != tc.err { 192 t.Fatalf("expected: %v, got: %v", tc.err, err) 193 } 194 if !reflect.DeepEqual(tc.want, got) { 195 t.Fatalf("expected: %v, got: %v", tc.want, got) 196 } 197 }) 198 } 199 } 200 201 func TestValidateInteger(t *testing.T) { 202 tests := map[string]struct { 203 in string 204 msg tea.Msg 205 }{ 206 "1": {in: "1", msg: successMsg{}}, 207 "12": {in: "12", msg: successMsg{}}, 208 "1.4": {in: "1.4", msg: errMsg{err: fmt.Errorf("Your answer '%s' not a valid integer", "1.4")}}, 209 "12n": {in: "12n", msg: errMsg{err: fmt.Errorf("Your answer '%s' not a valid integer", "12n")}}, 210 "dsds": {in: "dsds", msg: errMsg{err: fmt.Errorf("Your answer '%s' not a valid integer", "dsds")}}, 211 } 212 213 for name, tc := range tests { 214 t.Run(name, func(t *testing.T) { 215 q := getTestQueue(appTitle, "test") 216 cmd := validateInteger(tc.in, &q) 217 218 got := cmd() 219 220 switch tc.msg.(type) { 221 case successMsg: 222 if tc.msg != got { 223 t.Fatalf("%s - want: \n'%+v' \ngot: \n'%+v'", tc.in, tc.msg, got) 224 } 225 case errMsg: 226 gotE := got.(errMsg) 227 tcmsgE := tc.msg.(errMsg) 228 229 if tcmsgE.err.Error() != gotE.err.Error() { 230 t.Fatalf("want: \n'%+v' \ngot: \n'%+v'", tcmsgE.err.Error(), gotE.err.Error()) 231 } 232 233 } 234 }) 235 } 236 } 237 238 func TestValidateDomain(t *testing.T) { 239 tests := map[string]struct { 240 in string 241 msg tea.Msg 242 }{ 243 "example.com": {in: "example.com", msg: errMsg{err: fmt.Errorf("validateDomain: error verifying domain: domain is not verified")}}, 244 "example2.com": {in: "example2.com", msg: errMsg{err: fmt.Errorf("validateDomain: not owned by requestor: %%!w(<nil>)")}}, 245 "example3.com": {in: "example3.com", msg: successMsg{}}, 246 "example4.com": {in: "example4.com", msg: successMsg{}}, 247 } 248 for name, tc := range tests { 249 t.Run(name, func(t *testing.T) { 250 q := getTestQueue(appTitle, "test") 251 cmd := validateDomain(tc.in, &q) 252 253 got := cmd() 254 255 switch tc.msg.(type) { 256 case successMsg: 257 if tc.msg != got { 258 t.Fatalf("%s - want: \n'%+v' \ngot: \n'%+v'", tc.in, tc.msg, got) 259 } 260 case errMsg: 261 gotE := got.(errMsg) 262 tcmsgE := tc.msg.(errMsg) 263 264 if tcmsgE.err.Error() != gotE.err.Error() { 265 t.Fatalf("want: \n'%+v' \ngot: \n'%+v'", tcmsgE.err.Error(), gotE.err.Error()) 266 } 267 268 } 269 }) 270 } 271 } 272 273 func TestRegisterDomain(t *testing.T) { 274 tests := map[string]struct { 275 in string 276 msg tea.Msg 277 info *domainspb.RegisterParameters 278 }{ 279 "example.com": { 280 in: "y", 281 msg: errMsg{err: fmt.Errorf("registerDomain: error registering domain: domain is already owned. This should have been caught")}, 282 info: &domainspb.RegisterParameters{ 283 DomainName: "example.com", 284 }, 285 }, 286 "example2.com": { 287 in: "y", 288 msg: errMsg{err: fmt.Errorf("registerDomain: error registering domain: domain is already owned. This should have been caught")}, 289 info: &domainspb.RegisterParameters{ 290 DomainName: "example2.com", 291 }, 292 }, 293 "example3.com": { 294 in: "y", 295 msg: errMsg{err: fmt.Errorf("registerDomain: error registering domain: domain is cursed and cannot be obtained by mortals")}, 296 info: &domainspb.RegisterParameters{ 297 DomainName: "example3.com", 298 }, 299 }, 300 301 "example4.com": { 302 in: "y", 303 msg: successMsg{}, 304 info: &domainspb.RegisterParameters{ 305 DomainName: "example4.com", 306 }, 307 }, 308 "example4.com_no": { 309 in: "n", 310 msg: errMsg{err: fmt.Errorf("did not consent to being charged")}, 311 info: &domainspb.RegisterParameters{ 312 DomainName: "example4.com", 313 }, 314 }, 315 "example4.com_garbage": { 316 in: "dasdasdasdsdas", 317 msg: errMsg{err: fmt.Errorf("did not consent to being charged")}, 318 info: &domainspb.RegisterParameters{ 319 DomainName: "example4.com", 320 }, 321 }, 322 } 323 for name, tc := range tests { 324 t.Run(name, func(t *testing.T) { 325 q := getTestQueue(appTitle, "test") 326 q.Save("domainInfo", tc.info) 327 q.stack.AddSetting("domain_consent", "y") 328 cmd := registerDomain(tc.in, &q) 329 330 got := cmd() 331 332 switch tc.msg.(type) { 333 case successMsg: 334 if tc.msg != got { 335 t.Fatalf("%s - want: \n'%+v' \ngot: \n'%+v'", tc.in, tc.msg, got) 336 } 337 case errMsg: 338 gotE := got.(errMsg) 339 tcmsgE := tc.msg.(errMsg) 340 341 if tcmsgE.err.Error() != gotE.err.Error() { 342 t.Fatalf("want: \n'%+v' \ngot: \n'%+v'", tcmsgE.err.Error(), gotE.err.Error()) 343 } 344 345 } 346 }) 347 } 348 } 349 350 func TestAttachBilling(t *testing.T) { 351 tests := map[string]struct { 352 in string 353 want tea.Msg 354 setError bool 355 err error 356 }{ 357 "basic": { 358 in: "000000-000000-000000", 359 want: successMsg{}, 360 }, 361 "fail": { 362 in: "000000-000000-000000", 363 want: errMsg{err: fmt.Errorf("attachBilling: could not attach billing to project: %w", errForced)}, 364 setError: true, 365 }, 366 } 367 368 for name, tc := range tests { 369 t.Run(name, func(t *testing.T) { 370 q := getTestQueue(appTitle, "test") 371 372 if tc.setError { 373 errMock := GetMock(0) 374 errMock.forceErr = true 375 q.client = errMock 376 } 377 378 cmd := attachBilling(tc.in, &q) 379 got := cmd() 380 381 if !reflect.DeepEqual(tc.want, got) { 382 t.Fatalf("expected: %+v, got: %+v", tc.want, got) 383 } 384 }) 385 } 386 } 387 388 func TestCreateProject(t *testing.T) { 389 tests := map[string]struct { 390 in string 391 msg tea.Msg 392 }{ 393 "ds-tester-deploystack": {in: "ds-tester-deploystack", msg: errMsg{err: fmt.Errorf("createProject: could not create project: project_id already exists")}}, 394 "1234": {in: "1234", msg: errMsg{err: fmt.Errorf("createProject: could not create project: project_id contains too many characters, limit 30")}}, 395 "sa1234122132132143145315246754736573568765": {in: "1234", msg: errMsg{err: fmt.Errorf("createProject: could not create project: project_id contains too many characters, limit 30")}}, 396 "tp-never-used": {in: "tp-never-used", msg: successMsg{}}, 397 } 398 for name, tc := range tests { 399 t.Run(name, func(t *testing.T) { 400 q := getTestQueue(appTitle, "test") 401 cmd := createProject(tc.in, &q) 402 403 got := cmd() 404 405 switch tc.msg.(type) { 406 case successMsg: 407 if tc.msg != got { 408 t.Fatalf("%s - want: \n'%+v' \ngot: \n'%+v'", tc.in, tc.msg, got) 409 } 410 case errMsg: 411 gotE := got.(errMsg) 412 tcmsgE := tc.msg.(errMsg) 413 414 if tcmsgE.err.Error() != gotE.err.Error() { 415 t.Fatalf("want: \n'%+v' \ngot: \n'%+v'", tcmsgE.err.Error(), gotE.err.Error()) 416 } 417 418 } 419 }) 420 } 421 } 422 423 func TestValidateGCEDefault(t *testing.T) { 424 tests := map[string]struct { 425 in string 426 msg tea.Msg 427 lenItems int 428 }{ 429 "donotdefault": {in: "n", msg: successMsg{}, lenItems: 12}, 430 "default": {in: "y", msg: successMsg{}, lenItems: 1}, 431 } 432 for name, tc := range tests { 433 t.Run(name, func(t *testing.T) { 434 q := getTestQueue(appTitle, "test") 435 newGCEInstance(&q) 436 437 cmd := validateGCEDefault(tc.in, &q) 438 439 got := cmd() 440 441 switch tc.msg.(type) { 442 case successMsg: 443 if tc.msg != got { 444 t.Fatalf("%s - want: \n'%+v' \ngot: \n'%+v'", tc.in, tc.msg, got) 445 } 446 case errMsg: 447 gotE := got.(errMsg) 448 tcmsgE := tc.msg.(errMsg) 449 450 if tcmsgE.err.Error() != gotE.err.Error() { 451 t.Fatalf("want: \n'%+v' \ngot: \n'%+v'", tcmsgE.err.Error(), gotE.err.Error()) 452 } 453 } 454 455 if tc.lenItems != len(q.models) { 456 for _, v := range q.models { 457 t.Logf("%s %s", v.getKey(), "") 458 } 459 460 t.Fatalf("number of models want: '%d' got: '%d'", tc.lenItems, len(q.models)) 461 } 462 }) 463 } 464 } 465 466 func TestValidateGCEConfiguration(t *testing.T) { 467 tests := map[string]struct { 468 in string 469 msg tea.Msg 470 value string 471 }{ 472 // "nowebserver": {in: "n", msg: successMsg{unset: true}, value: ""}, 473 "yeswebserver": { 474 in: "y", 475 msg: successMsg{unset: true}, 476 value: gcloud.HTTPServerTags, 477 }, 478 } 479 for name, tc := range tests { 480 t.Run(name, func(t *testing.T) { 481 q := getTestQueue(appTitle, "test") 482 483 q.stack.AddSetting("instance-webserver", tc.in) 484 q.stack.AddSetting("gce-use-defaults", "n") 485 q.stack.AddSetting("instance-webserver", "n") 486 q.stack.AddSetting("instance-image-project", "n") 487 q.stack.AddSetting("instance-machine-type-family", "n") 488 q.stack.AddSetting("instance-image-family", "n") 489 490 cmd := validateGCEConfiguration(tc.in, &q) 491 492 got := cmd() 493 494 switch tc.msg.(type) { 495 case successMsg: 496 if tc.msg != got { 497 t.Fatalf("%s - want: \n'%+v' \ngot: \n'%+v'", tc.in, tc.msg, got) 498 } 499 case errMsg: 500 gotE := got.(errMsg) 501 tcmsgE := tc.msg.(errMsg) 502 503 if tcmsgE.err.Error() != gotE.err.Error() { 504 t.Fatalf("want: \n'%+v' \ngot: \n'%+v'", tcmsgE.err.Error(), gotE.err.Error()) 505 } 506 } 507 508 if tc.value != q.stack.GetSetting("instance-tags") { 509 t.Fatalf("tags want: '%s' got: '%s'", tc.value, q.stack.GetSetting("instance-tags")) 510 } 511 }) 512 } 513 } 514 515 func TestStackSelection(t *testing.T) { 516 tests := map[string]struct { 517 input string 518 want string 519 }{ 520 "Basic": {"examplePath", "examplePath"}, 521 } 522 523 for name, tc := range tests { 524 t.Run(name, func(t *testing.T) { 525 q := getTestQueue(appTitle, "test") 526 527 cmd := handleStackSelection(tc.input, &q) 528 cmd() 529 530 got := q.Get("stack").(string) 531 532 if !reflect.DeepEqual(tc.want, got) { 533 t.Fatalf("expected: %v, got: %v", tc.want, got) 534 } 535 }) 536 } 537 } 538 539 func TestHandleProjectNumbe(t *testing.T) { 540 tests := map[string]struct { 541 forceErr bool 542 want interface{} 543 }{ 544 "noerror": { 545 forceErr: false, 546 want: nil, 547 }, 548 "error": { 549 forceErr: true, 550 want: errMsg{err: errForced}, 551 }, 552 } 553 554 for name, tc := range tests { 555 t.Run(name, func(t *testing.T) { 556 q := getTestQueue(appTitle, "test") 557 q.stack.Config.ProjectNumber = true 558 m := GetMock(0) 559 m.forceErr = tc.forceErr 560 q.client = m 561 got := handleProjectNumber("test", &q) 562 563 assert.Equal(t, tc.want, got) 564 }) 565 } 566 }