github.com/GoogleCloudPlatform/deploystack@v1.12.8/deploystack_test.go (about) 1 // Copyright 2022 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 deploystack 16 17 import ( 18 "errors" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "os/exec" 23 "path/filepath" 24 "reflect" 25 "strings" 26 "testing" 27 28 "github.com/GoogleCloudPlatform/deploystack/config" 29 "github.com/GoogleCloudPlatform/deploystack/gcloud" 30 "github.com/GoogleCloudPlatform/deploystack/github" 31 "github.com/GoogleCloudPlatform/deploystack/terraform" 32 "github.com/go-test/deep" 33 "github.com/kylelemons/godebug/diff" 34 cp "github.com/otiai10/copy" 35 ) 36 37 var testFilesDir = filepath.Join(os.Getenv("DEPLOYSTACK_PATH"), "testdata") 38 39 var nosqltestdata = filepath.Join(testFilesDir, "reposformeta") 40 41 // Getting this right was such a huge pain in the ass, I'm using it a few times 42 // AND NEVER TOUCHING IT AGAIN 43 44 var nosqlMeta = Meta{ 45 DeployStack: config.Config{ 46 Title: "NoSQL Client Server", 47 Name: "nosql-client-server", 48 Duration: 5, 49 Project: true, 50 Region: true, 51 RegionType: "compute", 52 RegionDefault: "us-central1", 53 Zone: true, 54 PathTerraform: "terraform", 55 PathMessages: ".deploystack/messages", 56 PathScripts: ".deploystack/scripts", 57 58 DocumentationLink: "https://cloud.google.com/shell/docs/cloud-shell-tutorials/deploystack/nosql-client-server", 59 AuthorSettings: config.Settings{ 60 { 61 Name: "basename", 62 Value: "nosql-client-server", 63 Type: "string", 64 }, 65 }, 66 Description: "", 67 Products: []config.Product{ 68 {Product: "Compute Engine", Info: "Server - which will run mongodb"}, 69 {Product: "Compute Engine", Info: "Client - which will run a custom go application"}, 70 }, 71 }, 72 Terraform: terraform.Blocks{ 73 { 74 Name: "project", 75 Kind: "data", 76 Type: "google_project", 77 File: filepath.Join( 78 nosqltestdata, 79 "deploystack-nosql-client-server", 80 "terraform", 81 "main.tf", 82 ), 83 Start: 21, 84 Text: ` 85 data "google_project" "project" { 86 project_id = var.project_id 87 }`, 88 }, 89 { 90 Name: "all", 91 Kind: "managed", 92 Type: "google_project_service", 93 File: filepath.Join( 94 nosqltestdata, 95 "deploystack-nosql-client-server", 96 "terraform", 97 "main.tf", 98 ), 99 Start: 25, 100 Text: ` 101 resource "google_project_service" "all" { 102 for_each = toset(var.gcp_service_list) 103 project = data.google_project.project.number 104 service = each.key 105 disable_dependent_services = false 106 disable_on_destroy = false 107 }`, 108 }, 109 { 110 Name: "default", 111 Kind: "data", 112 Type: "google_compute_network", 113 File: filepath.Join( 114 nosqltestdata, 115 "deploystack-nosql-client-server", 116 "terraform", 117 "main.tf", 118 ), 119 Start: 34, 120 Text: ` 121 data "google_compute_network" "default" { 122 project = var.project_id 123 name = "default" 124 depends_on = [google_project_service.all] 125 }`, 126 }, 127 128 { 129 Name: "main", 130 Kind: "managed", 131 Type: "google_compute_network", 132 File: filepath.Join( 133 nosqltestdata, 134 "deploystack-nosql-client-server", 135 "terraform", 136 "main.tf", 137 ), 138 Start: 40, 139 Text: ` 140 resource "google_compute_network" "main" { 141 provider = google-beta 142 name = "${var.basename}-network" 143 auto_create_subnetworks = true 144 project = var.project_id 145 depends_on = [google_project_service.all] 146 }`, 147 }, 148 { 149 Name: "default-allow-http", 150 Kind: "managed", 151 Type: "google_compute_firewall", 152 File: filepath.Join( 153 nosqltestdata, 154 "deploystack-nosql-client-server", 155 "terraform", 156 "main.tf", 157 ), 158 Start: 48, 159 Text: ` 160 resource "google_compute_firewall" "default-allow-http" { 161 name = "deploystack-allow-http" 162 project = data.google_project.project.number 163 network = google_compute_network.main.name 164 165 allow { 166 protocol = "tcp" 167 ports = ["80"] 168 } 169 170 source_ranges = ["0.0.0.0/0"] 171 172 target_tags = ["http-server"] 173 depends_on = [google_project_service.all] 174 }`, 175 }, 176 177 { 178 Name: "default-allow-internal", 179 Kind: "managed", 180 Type: "google_compute_firewall", 181 File: filepath.Join( 182 nosqltestdata, 183 "deploystack-nosql-client-server", 184 "terraform", 185 "main.tf", 186 ), 187 Start: 64, 188 Text: ` 189 resource "google_compute_firewall" "default-allow-internal" { 190 name = "deploystack-allow-internal" 191 project = data.google_project.project.number 192 network = google_compute_network.main.name 193 194 allow { 195 protocol = "tcp" 196 ports = ["0-65535"] 197 } 198 199 allow { 200 protocol = "udp" 201 ports = ["0-65535"] 202 } 203 204 allow { 205 protocol = "icmp" 206 } 207 208 source_ranges = ["10.128.0.0/20"] 209 depends_on = [google_project_service.all] 210 211 }`, 212 }, 213 214 { 215 Name: "default-allow-ssh", 216 Kind: "managed", 217 Type: "google_compute_firewall", 218 File: filepath.Join( 219 nosqltestdata, 220 "deploystack-nosql-client-server", 221 "terraform", 222 "main.tf", 223 ), 224 Start: 88, 225 Text: ` 226 resource "google_compute_firewall" "default-allow-ssh" { 227 name = "deploystack-allow-ssh" 228 project = data.google_project.project.number 229 network = google_compute_network.main.name 230 231 allow { 232 protocol = "tcp" 233 ports = ["22"] 234 } 235 236 source_ranges = ["0.0.0.0/0"] 237 238 target_tags = ["ssh-server"] 239 depends_on = [google_project_service.all] 240 }`, 241 }, 242 243 { 244 Name: "server", 245 Kind: "managed", 246 Type: "google_compute_instance", 247 File: filepath.Join( 248 nosqltestdata, 249 "deploystack-nosql-client-server", 250 "terraform", 251 "main.tf", 252 ), 253 Start: 105, 254 Text: `# Create Instances 255 resource "google_compute_instance" "server" { 256 name = "server" 257 zone = var.zone 258 project = var.project_id 259 machine_type = "e2-standard-2" 260 tags = ["ssh-server", "http-server"] 261 allow_stopping_for_update = true 262 263 264 boot_disk { 265 auto_delete = true 266 device_name = "server" 267 initialize_params { 268 image = "family/ubuntu-1804-lts" 269 size = 10 270 type = "pd-standard" 271 } 272 } 273 274 network_interface { 275 network = google_compute_network.main.name 276 access_config { 277 // Ephemeral public IP 278 } 279 } 280 281 service_account { 282 scopes = ["https://www.googleapis.com/auth/logging.write"] 283 } 284 285 metadata_startup_script = <<SCRIPT 286 apt-get update 287 apt-get install -y mongodb 288 service mongodb stop 289 sed -i 's/bind_ip = 127.0.0.1/bind_ip = 0.0.0.0/' /etc/mongodb.conf 290 iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 27017 291 service mongodb start 292 SCRIPT 293 depends_on = [google_project_service.all] 294 }`, 295 }, 296 297 { 298 Name: "client", 299 Kind: "managed", 300 Type: "google_compute_instance", 301 File: filepath.Join( 302 nosqltestdata, 303 "deploystack-nosql-client-server", 304 "terraform", 305 "main.tf", 306 ), 307 Start: 146, 308 Text: ` 309 resource "google_compute_instance" "client" { 310 name = "client" 311 zone = var.zone 312 project = var.project_id 313 machine_type = "e2-standard-2" 314 tags = ["http-server", "https-server", "ssh-server"] 315 allow_stopping_for_update = true 316 317 boot_disk { 318 auto_delete = true 319 device_name = "client" 320 initialize_params { 321 image = "family/ubuntu-1804-lts" 322 size = 10 323 type = "pd-standard" 324 } 325 } 326 service_account { 327 scopes = ["https://www.googleapis.com/auth/logging.write"] 328 } 329 330 network_interface { 331 network = google_compute_network.main.name 332 333 access_config { 334 // Ephemeral public IP 335 } 336 } 337 338 metadata_startup_script = <<SCRIPT 339 add-apt-repository ppa:longsleep/golang-backports -y && \ 340 apt update -y && \ 341 apt install golang-go git -y 342 mkdir /modcache 343 mkdir /go 344 mkdir /app && cd /app 345 git clone https://github.com/GoogleCloudPlatform/deploystack-nosql-client-server.git 346 cd /app/deploystack-nosql-client-server/code/client 347 GOPATH=/go GOMODCACHE=/modcache GOCACHE=/modcache go build -o trainers main.go model.go 348 echo "GOPATH=/go GOMODCACHE=/modcache GOCACHE=/modcache DBHOST=${google_compute_instance.server.network_interface.0.network_ip} ./trainers" 349 GOPATH=/go GOMODCACHE=/modcache GOCACHE=/modcache DBHOST=${google_compute_instance.server.network_interface.0.network_ip} ./trainers & 350 SCRIPT 351 352 353 depends_on = [google_project_service.all] 354 }`, 355 }, 356 357 { 358 Name: "project_id", 359 Kind: "variable", 360 Type: "string", 361 File: filepath.Join(nosqltestdata, "deploystack-nosql-client-server", "terraform", "variables.tf"), 362 Start: 17, 363 Text: ` 364 variable "project_id" { 365 type = string 366 }`, 367 }, 368 369 { 370 Name: "zone", 371 Kind: "variable", 372 Type: "string", 373 File: filepath.Join(nosqltestdata, "deploystack-nosql-client-server", "terraform", "variables.tf"), 374 Start: 21, 375 Text: ` 376 variable "zone" { 377 type = string 378 }`, 379 }, 380 381 { 382 Name: "region", 383 Kind: "variable", 384 Type: "string", 385 File: filepath.Join(nosqltestdata, "deploystack-nosql-client-server", "terraform", "variables.tf"), 386 Start: 25, 387 Text: ` 388 variable "region" { 389 type = string 390 }`, 391 }, 392 393 { 394 Name: "basename", 395 Kind: "variable", 396 Type: "string", 397 File: filepath.Join(nosqltestdata, "deploystack-nosql-client-server", "terraform", "variables.tf"), 398 Start: 29, 399 Text: ` 400 variable "basename" { 401 type = string 402 }`, 403 }, 404 405 { 406 Name: "gcp_service_list", 407 Kind: "variable", 408 Type: "list(string)", 409 File: filepath.Join(nosqltestdata, "deploystack-nosql-client-server", "terraform", "variables.tf"), 410 Start: 33, 411 Text: ` 412 variable "gcp_service_list" { 413 description = "The list of apis necessary for the project" 414 type = list(string) 415 default = [ 416 "compute.googleapis.com", 417 ] 418 }`, 419 }, 420 }, 421 } 422 423 func compareValues(label string, want interface{}, got interface{}, t *testing.T) { 424 if !reflect.DeepEqual(want, got) { 425 t.Fatalf("%s: expected: \n|%v|\ngot: \n|%v|", label, want, got) 426 } 427 } 428 429 func TestPrecheck(t *testing.T) { 430 wd, err := filepath.Abs(".") 431 if err != nil { 432 t.Fatalf("error setting up environment for testing %v", err) 433 } 434 435 testdata := fmt.Sprintf("%s/testdata/configs", wd) 436 tests := map[string]struct { 437 wd string 438 want string 439 }{ 440 "single": { 441 wd: fmt.Sprintf("%s/preferred", testdata), 442 want: "", 443 }, 444 } 445 446 for name, tc := range tests { 447 t.Run(name, func(t *testing.T) { 448 449 oldWD, _ := os.Getwd() 450 err := os.Chdir(tc.wd) 451 if err != nil { 452 t.Fatalf("error changing wd: %s", err) 453 } 454 455 out := captureOutput(func() { 456 Precheck() 457 }) 458 459 if !strings.Contains(tc.want, string(out)) { 460 t.Fatalf("expected to contain: %+v, got: %+v", tc.want, string(out)) 461 } 462 463 os.Chdir(oldWD) 464 }) 465 } 466 } 467 func TestPrecheckMulti(t *testing.T) { 468 // Precheck exits if it is called in testing with multiple stacks 469 // so make throwing the exit the test 470 471 if os.Getenv("BE_CRASHER") == "1" { 472 wd, err := filepath.Abs(".") 473 if err != nil { 474 t.Fatalf("error setting up environment for testing %v", err) 475 } 476 477 testdata := fmt.Sprintf("%s/testdata/configs", wd) 478 path := fmt.Sprintf("%s/multi", testdata) 479 oldWD, _ := os.Getwd() 480 if err := os.Chdir(path); err != nil { 481 t.Fatalf("error changing wd: %s", err) 482 } 483 484 Precheck() 485 os.Chdir(oldWD) 486 return 487 } 488 // So this is what I got advice to do, but it caused panics 489 // Setting it to explicitly "go test" fixed it 490 // cmd := exec.Command(os.Args[0], "-test.run=TestPrecheckMulti") 491 cmd := exec.Command("go", "test", "-timeout", "2s", "-test.run=TestPrecheckMulti", "-coverage") 492 cmd.Env = append(os.Environ(), "BE_CRASHER=1") 493 err := cmd.Run() 494 if e, ok := err.(*exec.ExitError); ok && !e.Success() { 495 return 496 } 497 t.Fatalf("process ran with err %v, want exit status 1", err) 498 499 } 500 501 func captureOutput(f func()) string { 502 rescueStdout := os.Stdout 503 r, w, _ := os.Pipe() 504 os.Stdout = w 505 506 f() 507 508 w.Close() 509 out, _ := ioutil.ReadAll(r) 510 os.Stdout = rescueStdout 511 return string(out) 512 } 513 514 func TestCacheContact(t *testing.T) { 515 tests := map[string]struct { 516 in gcloud.ContactData 517 err error 518 }{ 519 "basic": { 520 in: gcloud.ContactData{ 521 AllContacts: gcloud.DomainRegistrarContact{ 522 Email: "test@example.com", 523 Phone: "+155555551212", 524 PostalAddress: gcloud.PostalAddress{ 525 RegionCode: "US", 526 PostalCode: "94502", 527 AdministrativeArea: "CA", 528 Locality: "San Francisco", 529 AddressLines: []string{"345 Spear Street"}, 530 Recipients: []string{"Googler"}, 531 }, 532 }, 533 }, 534 err: nil, 535 }, 536 "err": { 537 in: gcloud.ContactData{}, 538 err: fmt.Errorf("stat contact.yaml: no such file or directory"), 539 }, 540 } 541 542 for name, tc := range tests { 543 t.Run(name, func(t *testing.T) { 544 ContactSave(tc.in) 545 546 if tc.err == nil { 547 if _, err := os.Stat(contactfile); errors.Is(err, os.ErrNotExist) { 548 t.Fatalf("expected no error, got: %+v", err) 549 } 550 } else { 551 if _, err := os.Stat(contactfile); err.Error() != tc.err.Error() { 552 t.Fatalf("expected %+v, got: %+v", tc.err, err) 553 } 554 555 } 556 557 os.Remove(contactfile) 558 559 }) 560 } 561 } 562 563 func TestCheckForContact(t *testing.T) { 564 tests := map[string]struct { 565 in string 566 want gcloud.ContactData 567 }{ 568 "basic": { 569 in: "testdata/contact/contact.yaml", 570 want: gcloud.ContactData{ 571 AllContacts: gcloud.DomainRegistrarContact{ 572 Email: "test@example.com", 573 Phone: "+155555551212", 574 PostalAddress: gcloud.PostalAddress{ 575 RegionCode: "US", 576 PostalCode: "94502", 577 AdministrativeArea: "CA", 578 Locality: "San Francisco", 579 AddressLines: []string{"345 Spear Street"}, 580 Recipients: []string{"Googler"}, 581 }, 582 }, 583 }, 584 }, 585 586 "empty": { 587 in: contactfile, 588 want: gcloud.ContactData{}, 589 }, 590 } 591 592 for name, tc := range tests { 593 t.Run(name, func(t *testing.T) { 594 595 oldContactFile := contactfile 596 contactfile = tc.in 597 598 got := ContactCheck() 599 if !reflect.DeepEqual(tc.want, got) { 600 t.Fatalf("expected: %+v, got: %+v", tc.want, got) 601 } 602 603 contactfile = oldContactFile 604 }) 605 } 606 } 607 608 func TestInit(t *testing.T) { 609 errUnableToRead := errors.New("unable to read config file: ") 610 tests := map[string]struct { 611 path string 612 want config.Stack 613 err error 614 }{ 615 "error": { 616 path: "sadasd", 617 want: config.Stack{}, 618 err: errUnableToRead, 619 }, 620 "no_custom": { 621 path: "testdata/dsfolders/no_customs", 622 want: config.Stack{ 623 Config: config.Config{ 624 Title: "TESTCONFIG", 625 Name: "test", 626 Description: "A test string for usage with this stuff.", 627 Duration: 5, 628 Project: true, 629 Region: true, 630 RegionType: "functions", 631 RegionDefault: "us-central1", 632 }, 633 }, 634 err: nil, 635 }, 636 "no_name": { 637 path: "testdata/dsfolders/no_name", 638 want: config.Stack{ 639 Config: config.Config{ 640 Title: "NONAME", 641 Name: "", 642 Description: "A test string for usage with this stuff.", 643 Duration: 5, 644 Project: true, 645 Region: true, 646 RegionType: "functions", 647 RegionDefault: "us-central1", 648 }, 649 }, 650 err: fmt.Errorf("could retrieve name of stack: could not open local git directory: repository does not exist \nDeployStack author: fix this by adding a 'name' key and value to the deploystack config"), 651 }, 652 "custom": { 653 path: "testdata/dsfolders/customs", 654 want: config.Stack{ 655 Config: config.Config{ 656 Title: "TESTCONFIG", 657 Name: "test", 658 Description: "A test string for usage with this stuff.", 659 Duration: 5, 660 Project: false, 661 Region: false, 662 RegionType: "", 663 RegionDefault: "", 664 CustomSettings: []config.Custom{ 665 {Name: "nodes", Description: "Nodes", Default: "3"}, 666 }, 667 }, 668 }, 669 err: nil, 670 }, 671 "custom_options": { 672 path: "testdata/dsfolders/customs_options", 673 want: config.Stack{ 674 Config: config.Config{ 675 Title: "TESTCONFIG", 676 Name: "test", 677 Description: "A test string for usage with this stuff.", 678 Duration: 5, 679 Project: false, 680 Region: false, 681 RegionType: "", 682 RegionDefault: "", 683 684 CustomSettings: []config.Custom{ 685 { 686 Name: "nodes", 687 Description: "Nodes", 688 Default: "3", 689 Options: []string{"1", "2", "3"}, 690 }, 691 }, 692 }, 693 }, 694 err: nil, 695 }, 696 } 697 698 for name, tc := range tests { 699 t.Run(name, func(t *testing.T) { 700 701 s, err := Init(tc.path) 702 703 if tc.err == nil { 704 if err != nil { 705 t.Fatalf("expected: no error got: %+v", err) 706 } 707 } 708 709 if errors.Is(err, tc.err) { 710 if err != nil && tc.err != nil && err.Error() != tc.err.Error() { 711 t.Fatalf("expected: error(%s) got: error(%s)", tc.err, err) 712 } 713 } 714 715 compareValues("Name", tc.want.Config.Name, s.Config.Name, t) 716 compareValues("Title", tc.want.Config.Title, s.Config.Title, t) 717 compareValues("Description", tc.want.Config.Description, s.Config.Description, t) 718 compareValues("Duration", tc.want.Config.Duration, s.Config.Duration, t) 719 compareValues("Project", tc.want.Config.Project, s.Config.Project, t) 720 compareValues("Region", tc.want.Config.Region, s.Config.Region, t) 721 compareValues("RegionType", tc.want.Config.RegionType, s.Config.RegionType, t) 722 compareValues("RegionDefault", tc.want.Config.RegionDefault, s.Config.RegionDefault, t) 723 for i, v := range s.Config.CustomSettings { 724 compareValues(v.Name, tc.want.Config.CustomSettings[i], v, t) 725 } 726 }) 727 } 728 } 729 730 func TestShortName(t *testing.T) { 731 tests := map[string]struct { 732 in string 733 want string 734 }{ 735 "deploystack-repo": {in: "https://github.com/GoogleCloudPlatform/deploystack-cost-sentry", want: "cost-sentry"}, 736 "non-deploystack-repo": {in: "https://github.com/tpryan/microservices-demo", want: "microservices-demo"}, 737 } 738 739 for name, tc := range tests { 740 t.Run(name, func(t *testing.T) { 741 m := Meta{} 742 m.Github.Name = tc.in 743 744 got := m.ShortName() 745 if !reflect.DeepEqual(tc.want, got) { 746 t.Fatalf("expected: %v, got: %v", tc.want, got) 747 } 748 }) 749 } 750 } 751 752 func TestShortNameUnderscore(t *testing.T) { 753 tests := map[string]struct { 754 in string 755 want string 756 }{ 757 "deploystack-repo": {in: "https://github.com/GoogleCloudPlatform/deploystack-cost-sentry", want: "cost_sentry"}, 758 "non-deploystack-repo": {in: "https://github.com/tpryan/microservices-demo", want: "microservices_demo"}, 759 } 760 761 for name, tc := range tests { 762 t.Run(name, func(t *testing.T) { 763 m := Meta{} 764 m.Github.Name = tc.in 765 766 got := m.ShortNameUnderscore() 767 if !reflect.DeepEqual(tc.want, got) { 768 t.Fatalf("expected: %v, got: %v", tc.want, got) 769 } 770 }) 771 } 772 } 773 774 func TestGetRepo(t *testing.T) { 775 wd, err := filepath.Abs(".") 776 if err != nil { 777 t.Fatalf("error setting up environment for testing %v", err) 778 } 779 780 testdata := fmt.Sprintf("%s/testdata/repoforgithub", wd) 781 tests := map[string]struct { 782 repo github.Repo 783 path string 784 want string 785 err error 786 }{ 787 "deploystack-nosql-client-server": { 788 repo: github.Repo{ 789 Name: "deploystack-nosql-client-server", 790 Owner: "GoogleCloudPlatform", 791 Branch: "main", 792 }, 793 path: testdata, 794 want: fmt.Sprintf("%s/deploystack-nosql-client-server", testdata), 795 }, 796 797 "deploystack-cost-sentry": { 798 repo: github.Repo{ 799 Name: "deploystack-cost-sentry", 800 Owner: "GoogleCloudPlatform", 801 Branch: "main", 802 }, 803 path: testdata, 804 want: fmt.Sprintf("%s/deploystack-cost-sentry_1", testdata), 805 }, 806 } 807 808 for name, tc := range tests { 809 t.Run(name, func(t *testing.T) { 810 811 // Defered to make sure it runs even if things fatal 812 defer func() { 813 err = os.RemoveAll(tc.want) 814 if err != nil { 815 t.Logf(err.Error()) 816 } 817 }() 818 819 got, err := DownloadRepo(tc.repo, tc.path) 820 821 if tc.err == nil && err != nil { 822 t.Fatalf("expected: no error got: %+v", err) 823 } 824 825 if !reflect.DeepEqual(tc.want, got) { 826 t.Fatalf("expected: %+v, got: %+v", tc.want, got) 827 } 828 829 if _, err := os.Stat(tc.want); os.IsNotExist(err) { 830 t.Fatalf("expected: %s to exist it does not", err) 831 } 832 833 }) 834 } 835 } 836 837 func TestGetAcceptableDir(t *testing.T) { 838 wd, err := filepath.Abs(".") 839 if err != nil { 840 t.Fatalf("error setting up environment for testing %v", err) 841 } 842 testdata := fmt.Sprintf("%s/testdata/repoforgithub", wd) 843 844 tests := map[string]struct { 845 in string 846 want string 847 }{ 848 "doesnotexist": { 849 in: fmt.Sprintf("%s/testfolder", testdata), 850 want: fmt.Sprintf("%s/testfolder", testdata), 851 }, 852 "exists": { 853 in: fmt.Sprintf("%s/alreadyexists", testdata), 854 want: fmt.Sprintf("%s/alreadyexists_2", testdata), 855 }, 856 } 857 858 for name, tc := range tests { 859 t.Run(name, func(t *testing.T) { 860 got := UniquePath(tc.in) 861 if !reflect.DeepEqual(tc.want, got) { 862 t.Fatalf("expected: %+v, got: %+v", tc.want, got) 863 } 864 }) 865 } 866 } 867 868 func TestNewMeta(t *testing.T) { 869 testdata := filepath.Join(testFilesDir, "reposformeta") 870 871 tests := map[string]struct { 872 path string 873 want Meta 874 err error 875 }{ 876 "deploystack-nosql-client-server": { 877 path: filepath.Join(testdata, "deploystack-nosql-client-server"), 878 want: nosqlMeta, 879 }, 880 } 881 882 for name, tc := range tests { 883 t.Run(name, func(t *testing.T) { 884 got, err := NewMeta(tc.path) 885 886 got.Terraform.Sort() 887 tc.want.Terraform.Sort() 888 889 if tc.err == nil && err != nil { 890 t.Fatalf("expected no error, got %s", err) 891 } 892 893 if !reflect.DeepEqual(tc.want, got) { 894 895 for i := range tc.want.Terraform { 896 fmt.Println(diff.Diff( 897 tc.want.Terraform[i].Text, 898 got.Terraform[i].Text, 899 )) 900 } 901 902 fmt.Println(diff.Diff( 903 tc.want.DeployStack.Description, 904 got.DeployStack.Description, 905 )) 906 907 diff := deep.Equal(tc.want, got) 908 t.Log("a common reason for this error is that one of the outside repos changed") 909 t.Errorf("compare failed: %v", diff) 910 } 911 }) 912 } 913 } 914 915 func TestSuggest(t *testing.T) { 916 tests := map[string]struct { 917 in Meta 918 want config.Config 919 err error 920 }{ 921 "nosql-client-server": { 922 in: nosqlMeta, 923 want: config.Config{ 924 Title: "NoSQL Client Server", 925 Name: "nosql-client-server", 926 Duration: 5, 927 Project: true, 928 Region: true, 929 RegionType: "compute", 930 RegionDefault: "us-central1", 931 Zone: true, 932 PathTerraform: "terraform", 933 PathMessages: ".deploystack/messages", 934 PathScripts: ".deploystack/scripts", 935 936 DocumentationLink: "https://cloud.google.com/shell/docs/cloud-shell-tutorials/deploystack/nosql-client-server", 937 AuthorSettings: config.Settings{ 938 { 939 Name: "basename", 940 Value: "nosql-client-server", 941 Type: "string", 942 }, 943 }, 944 Description: "", 945 Products: []config.Product{ 946 {Product: "Compute Engine", Info: "Server - which will run mongodb"}, 947 {Product: "Compute Engine", Info: "Client - which will run a custom go application"}, 948 }, 949 }, 950 }, 951 } 952 953 for name, tc := range tests { 954 t.Run(name, func(t *testing.T) { 955 got, err := tc.in.Suggest() 956 957 if tc.err == nil && err != nil { 958 t.Fatalf("expected no error, got %s", err) 959 } 960 961 if !reflect.DeepEqual(tc.want, got) { 962 963 fmt.Println(diff.Diff( 964 tc.want.Description, 965 got.Description, 966 )) 967 968 diff := deep.Equal(tc.want, got) 969 t.Errorf("compare failed: %v", diff) 970 } 971 }) 972 } 973 } 974 975 func TestAttemptRepo(t *testing.T) { 976 tempName, err := os.MkdirTemp("", "testrepos") 977 defer os.RemoveAll(tempName) 978 if err != nil { 979 t.Fatalf("could not get a temp directory for test: %s", err) 980 } 981 982 err = os.Mkdir(filepath.Join(tempName, "deploystack-single-vm"), 0666) 983 if err != nil { 984 t.Fatalf("could not get a make a directory for test: %s", err) 985 } 986 987 tests := map[string]struct { 988 name string 989 wd string 990 wantdir string 991 wantrepo github.Repo 992 err error 993 }{ 994 "deploystack-nosql-client-server": { 995 name: "deploystack-nosql-client-server", 996 wd: tempName, 997 wantdir: filepath.Join(tempName, "deploystack-nosql-client-server"), 998 wantrepo: github.Repo{ 999 Name: "deploystack-nosql-client-server", 1000 Owner: "GoogleCloudPlatform", 1001 Branch: "main", 1002 }, 1003 }, 1004 "deploystack-single-vm": { 1005 name: "deploystack-single-vm", 1006 wd: tempName, 1007 wantdir: filepath.Join(tempName, "deploystack-single-vm_1"), 1008 wantrepo: github.Repo{ 1009 Name: "deploystack-single-vm", 1010 Owner: "GoogleCloudPlatform", 1011 Branch: "main", 1012 }, 1013 }, 1014 "cost-sentry": { 1015 name: "cost-sentry", 1016 wd: tempName, 1017 wantdir: filepath.Join(tempName, "deploystack-cost-sentry"), 1018 wantrepo: github.Repo{ 1019 Name: "deploystack-cost-sentry", 1020 Owner: "GoogleCloudPlatform", 1021 Branch: "main", 1022 }, 1023 }, 1024 1025 "notvalid": { 1026 name: "badreponame", 1027 wd: tempName, 1028 wantdir: filepath.Join(tempName, "deploystack-badreponame"), 1029 wantrepo: github.Repo{ 1030 Name: "deploystack-badreponame", 1031 Owner: "GoogleCloudPlatform", 1032 Branch: "main", 1033 }, 1034 err: fmt.Errorf("cannot clone repo"), 1035 }, 1036 } 1037 1038 for name, tc := range tests { 1039 t.Run(name, func(t *testing.T) { 1040 gotdir, gotrepo, err := AttemptRepo(tc.name, tc.wd) 1041 1042 if tc.err == nil && err != nil { 1043 t.Fatalf("expected no error, got: %s", err) 1044 } 1045 1046 if tc.err != nil && err != nil { 1047 if !strings.Contains(err.Error(), tc.err.Error()) { 1048 t.Fatalf("expected error %s, got: %s", tc.err, err) 1049 } 1050 } 1051 1052 if !reflect.DeepEqual(tc.wantdir, gotdir) { 1053 diff := deep.Equal(tc.wantdir, gotdir) 1054 t.Errorf("compare failed: %v", diff) 1055 } 1056 1057 if !reflect.DeepEqual(tc.wantrepo, gotrepo) { 1058 diff := deep.Equal(tc.wantrepo, gotrepo) 1059 t.Errorf("compare failed: %v", diff) 1060 } 1061 }) 1062 } 1063 } 1064 1065 func TestWriteConfig(t *testing.T) { 1066 tempName, err := os.MkdirTemp("", "testreposwc") 1067 defer os.RemoveAll(tempName) 1068 if err != nil { 1069 t.Fatalf("could not get a temp directory for test: %s", err) 1070 } 1071 1072 src := filepath.Join(testFilesDir, "reposformeta", "terraform-google-load-balanced-vms") 1073 dest := filepath.Join(tempName, "terraform-google-load-balanced-vms") 1074 1075 if err := cp.Copy(src, dest); err != nil { 1076 t.Fatalf("could create a test directory for test: %s", err) 1077 } 1078 1079 gh := github.Repo{ 1080 Name: "terraform-google-load-balanced-vms", 1081 Owner: "GoogleCloudPlatform", 1082 Branch: "main", 1083 } 1084 1085 if err := WriteConfig(dest, gh); err != nil { 1086 t.Fatalf("writeconfig: failed %s", err) 1087 } 1088 1089 target := filepath.Join(dest, ".deploystack", "deploystack.yaml") 1090 if _, err := os.Stat(target); err != nil { 1091 t.Fatalf("writeconfig: failed to write file %s", err) 1092 } 1093 }