github.com/oam-dev/kubevela@v1.9.11/pkg/utils/common/common_test.go (about) 1 /* 2 Copyright 2021 The KubeVela Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package common 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "net/http" 24 "net/http/httptest" 25 "os" 26 "os/exec" 27 "path/filepath" 28 "testing" 29 "time" 30 31 "cuelang.org/go/cue/cuecontext" 32 33 "cuelang.org/go/cue/load" 34 "github.com/crossplane/crossplane-runtime/pkg/test" 35 "github.com/google/go-cmp/cmp" 36 "github.com/kubevela/workflow/pkg/cue/model/value" 37 "github.com/stretchr/testify/assert" 38 ) 39 40 var ResponseString = "Hello HTTP Get." 41 42 func TestHTTPGet(t *testing.T) { 43 type want struct { 44 data string 45 errStr string 46 } 47 var ctx = context.Background() 48 49 testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 50 fmt.Fprintln(w, ResponseString) 51 })) 52 defer testServer.Close() 53 54 cases := map[string]struct { 55 reason string 56 url string 57 want want 58 }{ 59 "normal case": { 60 reason: "url is valid\n", 61 url: testServer.URL, 62 want: want{ 63 data: fmt.Sprintf("%s\n", ResponseString), 64 errStr: "", 65 }, 66 }, 67 } 68 69 for name, tc := range cases { 70 t.Run(name, func(t *testing.T) { 71 got, err := HTTPGetWithOption(ctx, tc.url, nil) 72 if tc.want.errStr != "" { 73 if diff := cmp.Diff(tc.want.errStr, err.Error(), test.EquateErrors()); diff != "" { 74 t.Errorf("\n%s\nHTTPGet(...): -want error, +got error:\n%s", tc.reason, diff) 75 } 76 } 77 78 if diff := cmp.Diff(tc.want.data, string(got)); diff != "" { 79 t.Errorf("\n%s\nHTTPGet(...): -want, +got:\n%s", tc.reason, diff) 80 } 81 }) 82 } 83 84 } 85 86 func TestHTTPGetWithOption(t *testing.T) { 87 type want struct { 88 data string 89 } 90 var ctx = context.Background() 91 92 testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 93 u, p, ok := r.BasicAuth() 94 if !ok { 95 w.Write([]byte("Error parsing basic auth")) 96 w.WriteHeader(401) 97 return 98 } 99 if u != "test-user" { 100 w.Write([]byte(fmt.Sprintf("Username provided is incorrect: %s", u))) 101 w.WriteHeader(401) 102 return 103 } 104 if p != "test-pass" { 105 w.Write([]byte(fmt.Sprintf("Password provided is incorrect: %s", p))) 106 w.WriteHeader(401) 107 return 108 } 109 w.Write([]byte("correct password")) 110 w.WriteHeader(200) 111 })) 112 defer testServer.Close() 113 114 cases := map[string]struct { 115 opts *HTTPOption 116 url string 117 want want 118 }{ 119 "without auth case": { 120 opts: nil, 121 url: testServer.URL, 122 want: want{ 123 data: "Error parsing basic auth", 124 }, 125 }, 126 "error user name case": { 127 opts: &HTTPOption{ 128 Username: "no-user", 129 Password: "test-pass", 130 }, 131 url: testServer.URL, 132 want: want{ 133 data: "Username provided is incorrect: no-user", 134 }, 135 }, 136 "error password case": { 137 opts: &HTTPOption{ 138 Username: "test-user", 139 Password: "error-pass", 140 }, 141 url: testServer.URL, 142 want: want{ 143 data: "Password provided is incorrect: error-pass", 144 }, 145 }, 146 "correct password case": { 147 opts: &HTTPOption{ 148 Username: "test-user", 149 Password: "test-pass", 150 }, 151 url: testServer.URL, 152 want: want{ 153 data: "correct password", 154 }, 155 }, 156 } 157 158 for name, tc := range cases { 159 t.Run(name, func(t *testing.T) { 160 got, err := HTTPGetWithOption(ctx, tc.url, tc.opts) 161 assert.NoError(t, err) 162 163 if diff := cmp.Diff(tc.want.data, string(got)); diff != "" { 164 t.Errorf("\n%s\nHTTPGet(...): -want, +got:\n%s", tc.want.data, diff) 165 } 166 }) 167 } 168 169 } 170 171 func TestHttpGetCaFile(t *testing.T) { 172 type want struct { 173 data string 174 } 175 var ctx = context.Background() 176 testServer := &http.Server{Addr: ":10443"} 177 178 http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { 179 writer.Write([]byte("this is https server")) 180 writer.WriteHeader(200) 181 }) 182 183 go func() { 184 err := testServer.ListenAndServeTLS("./testdata/server.crt", "./testdata/server.key") 185 assert.NoError(t, err) 186 }() 187 time.Sleep(time.Millisecond) 188 caFile, err := os.ReadFile("./testdata/server.crt") 189 assert.NoError(t, err) 190 191 cases := map[string]struct { 192 opts *HTTPOption 193 url string 194 want want 195 }{ 196 "with caFile": { 197 opts: &HTTPOption{CaFile: string(caFile)}, 198 url: "https://127.0.0.1:10443", 199 want: want{ 200 data: "this is https server", 201 }, 202 }, 203 } 204 205 for name, tc := range cases { 206 t.Run(name, func(t *testing.T) { 207 got, err := HTTPGetWithOption(ctx, tc.url, tc.opts) 208 assert.NoError(t, err) 209 210 if diff := cmp.Diff(tc.want.data, string(got)); diff != "" { 211 t.Errorf("\n%s\nHTTPGet(...): -want, +got:\n%s", tc.want.data, diff) 212 } 213 }) 214 } 215 } 216 217 func TestGetCUEParameterValue(t *testing.T) { 218 type want struct { 219 err error 220 } 221 var validCueStr = ` 222 parameter: { 223 min: int 224 } 225 ` 226 227 var CueStrNotContainParameter = ` 228 output: { 229 min: int 230 } 231 ` 232 cases := map[string]struct { 233 reason string 234 cueStr string 235 want want 236 }{ 237 "GetCUEParameterValue": { 238 reason: "cue string is valid", 239 cueStr: validCueStr, 240 want: want{ 241 err: nil, 242 }, 243 }, 244 "CUEStringNotContainParameter": { 245 reason: "cue string doesn't contain Parameter", 246 cueStr: CueStrNotContainParameter, 247 want: want{ 248 err: fmt.Errorf("parameter not exist"), 249 }, 250 }, 251 } 252 253 for name, tc := range cases { 254 t.Run(name, func(t *testing.T) { 255 _, err := GetCUEParameterValue(tc.cueStr, nil) 256 if tc.want.err != nil { 257 if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { 258 t.Errorf("\n%s\nGenOpenAPIFromFile(...): -want error, +got error:\n%s", tc.reason, diff) 259 } 260 } 261 262 }) 263 } 264 } 265 266 func TestGetCUEParameterValue4RareCases(t *testing.T) { 267 type want struct { 268 errMsg string 269 } 270 271 cases := map[string]struct { 272 reason string 273 cueStr string 274 want want 275 }{ 276 "CUEParameterNotFound": { 277 reason: "cue parameter not found", 278 cueStr: `name: string`, 279 want: want{ 280 errMsg: "parameter not exist", 281 }, 282 }, 283 "CUEStringInvalid": { 284 reason: "cue string is invalid", 285 cueStr: `name`, 286 want: want{ 287 errMsg: "parameter not exist", 288 }, 289 }, 290 } 291 292 for name, tc := range cases { 293 t.Run(name, func(t *testing.T) { 294 _, err := GetCUEParameterValue(tc.cueStr, nil) 295 if diff := cmp.Diff(tc.want.errMsg, err.Error(), test.EquateConditions()); diff != "" { 296 t.Errorf("\n%s\nGenOpenAPIFromFile(...): -want error, +got error:\n%s", tc.reason, diff) 297 } 298 299 }) 300 } 301 } 302 303 func TestGenOpenAPI(t *testing.T) { 304 type want struct { 305 targetSchemaFile string 306 err error 307 } 308 cases := map[string]struct { 309 reason string 310 fileName string 311 targetSchema string 312 want want 313 }{ 314 "GenOpenAPI": { 315 reason: "generate valid OpenAPI schema with context", 316 fileName: "workload1.cue", 317 want: want{ 318 targetSchemaFile: "workload1.json", 319 err: nil, 320 }, 321 }, 322 "EmptyOpenAPI": { 323 reason: "generate empty OpenAPI schema", 324 fileName: "emptyParameter.cue", 325 want: want{ 326 targetSchemaFile: "emptyParameter.json", 327 err: nil, 328 }, 329 }, 330 } 331 332 for name, tc := range cases { 333 t.Run(name, func(t *testing.T) { 334 instances := load.Instances([]string{filepath.FromSlash(tc.fileName)}, &load.Config{ 335 Dir: "testdata", 336 }) 337 val, err := value.NewValueWithInstance(instances[0], nil, "") 338 assert.NoError(t, err) 339 got, err := GenOpenAPI(val) 340 if tc.want.err != nil { 341 if diff := cmp.Diff(tc.want.err, errors.New(err.Error()), test.EquateErrors()); diff != "" { 342 t.Errorf("\n%s\nGenOpenAPIFromFile(...): -want error, +got error:\n%s", tc.reason, diff) 343 } 344 } 345 if tc.want.targetSchemaFile == "" { 346 return 347 } 348 wantSchema, _ := os.ReadFile(filepath.Join("testdata", tc.want.targetSchemaFile)) 349 if diff := cmp.Diff(wantSchema, got); diff != "" { 350 t.Errorf("\n%s\nGenOpenAPIFromFile(...): -want, +got:\n%s", tc.reason, diff) 351 } 352 }) 353 } 354 } 355 356 func TestGenOpenAPIWithCueX(t *testing.T) { 357 type want struct { 358 targetSchemaFile string 359 err error 360 } 361 cases := map[string]struct { 362 reason string 363 fileName string 364 targetSchema string 365 want want 366 }{ 367 "GenOpenAPI": { 368 reason: "generate valid OpenAPI schema with context", 369 fileName: "workload1.cue", 370 want: want{ 371 targetSchemaFile: "workload1.json", 372 err: nil, 373 }, 374 }, 375 "EmptyOpenAPI": { 376 reason: "generate empty OpenAPI schema", 377 fileName: "emptyParameter.cue", 378 want: want{ 379 targetSchemaFile: "emptyParameter.json", 380 err: nil, 381 }, 382 }, 383 } 384 385 for name, tc := range cases { 386 t.Run(name, func(t *testing.T) { 387 instances := load.Instances([]string{filepath.FromSlash(tc.fileName)}, &load.Config{ 388 Dir: "testdata", 389 }) 390 val := cuecontext.New().BuildInstance(instances[0]) 391 assert.NoError(t, val.Err()) 392 got, err := GenOpenAPIWithCueX(val) 393 if tc.want.err != nil { 394 if diff := cmp.Diff(tc.want.err, errors.New(err.Error()), test.EquateErrors()); diff != "" { 395 t.Errorf("\n%s\nGenOpenAPIFromFile(...): -want error, +got error:\n%s", tc.reason, diff) 396 } 397 } 398 if tc.want.targetSchemaFile == "" { 399 return 400 } 401 wantSchema, _ := os.ReadFile(filepath.Join("testdata", tc.want.targetSchemaFile)) 402 if diff := cmp.Diff(wantSchema, got); diff != "" { 403 t.Errorf("\n%s\nGenOpenAPIFromFile(...): -want, +got:\n%s", tc.reason, diff) 404 } 405 }) 406 } 407 } 408 409 func TestRealtimePrintCommandOutput(t *testing.T) { 410 cmd := exec.Command("bash", "-c", "date") 411 err := RealtimePrintCommandOutput(cmd, "") 412 assert.NoError(t, err) 413 cmd.Process.Kill() 414 415 var logFile = "terraform.log" 416 var hello = "Hello" 417 cmd = exec.Command("bash", "-c", fmt.Sprintf("echo \"%s\"", hello)) 418 err = RealtimePrintCommandOutput(cmd, logFile) 419 assert.NoError(t, err) 420 421 data, _ := os.ReadFile(logFile) 422 assert.Contains(t, string(data), hello) 423 os.Remove(logFile) 424 } 425 426 func TestParseTerraformVariables(t *testing.T) { 427 configuration := ` 428 module "rds" { 429 source = "terraform-alicloud-modules/rds/alicloud" 430 engine = "MySQL" 431 engine_version = "8.0" 432 instance_type = "rds.mysql.c1.large" 433 instance_storage = "20" 434 instance_name = var.instance_name 435 account_name = var.account_name 436 password = var.password 437 } 438 439 output "DB_NAME" { 440 value = module.rds.this_db_instance_name 441 } 442 output "DB_USER" { 443 value = module.rds.this_db_database_account 444 } 445 output "DB_PORT" { 446 value = module.rds.this_db_instance_port 447 } 448 output "DB_HOST" { 449 value = module.rds.this_db_instance_connection_string 450 } 451 output "DB_PASSWORD" { 452 value = module.rds.this_db_instance_port 453 } 454 455 variable "instance_name" { 456 description = "RDS instance name" 457 type = string 458 default = "poc" 459 } 460 461 variable "account_name" { 462 description = "RDS instance user account name" 463 type = "string" 464 default = "oam" 465 } 466 467 variable "password" { 468 description = "RDS instance account password" 469 type = "string" 470 default = "xxx" 471 } 472 473 variable "intVar" { 474 type = "number" 475 } 476 477 variable "boolVar" { 478 type = "bool" 479 } 480 481 variable "listVar" { 482 type = "list" 483 } 484 485 variable "mapVar" { 486 type = "map" 487 }` 488 489 variables, _, err := ParseTerraformVariables(configuration) 490 assert.NoError(t, err) 491 _, passwordExisted := variables["password"] 492 assert.True(t, passwordExisted) 493 494 _, intVarExisted := variables["password"] 495 assert.True(t, intVarExisted) 496 } 497 498 func TestRefineParameterInstance(t *testing.T) { 499 // test #parameter exists: mock issues in #1939 & #2062 500 s := `parameter: #parameter 501 #parameter: { 502 x?: string 503 if x != "" { 504 y: string 505 } 506 } 507 patch: { 508 if parameter.x != "" { 509 label: parameter.x 510 } 511 }` 512 val, err := value.NewValue(s, nil, "") 513 assert.NoError(t, err) 514 assert.NoError(t, val.CueValue().Err()) 515 _, err = RefineParameterValue(val) 516 assert.NoError(t, err) 517 // test #parameter not exist but parameter exists 518 s = `parameter: { 519 x?: string 520 if x != "" { 521 y: string 522 } 523 }` 524 val, err = value.NewValue(s, nil, "") 525 assert.NoError(t, err) 526 assert.NoError(t, val.CueValue().Err()) 527 assert.NoError(t, err) 528 _, err = RefineParameterValue(val) 529 assert.NoError(t, err) 530 // test #parameter as int 531 s = `parameter: #parameter 532 #parameter: int` 533 val, err = value.NewValue(s, nil, "") 534 assert.NoError(t, err) 535 assert.NoError(t, val.CueValue().Err()) 536 _, err = RefineParameterValue(val) 537 assert.NoError(t, err) 538 } 539 540 func TestFillParameterDefinitionFieldIfNotExist(t *testing.T) { 541 // test #parameter exists: mock issues in #1939 & #2062 542 s := `parameter: #parameter 543 #parameter: { 544 x?: string 545 if x != "" { 546 y: string 547 } 548 } 549 patch: { 550 if parameter.x != "" { 551 label: parameter.x 552 } 553 }` 554 val := cuecontext.New().CompileString(s) 555 assert.NoError(t, val.Err()) 556 filledVal := FillParameterDefinitionFieldIfNotExist(val) 557 assert.NoError(t, filledVal.Err()) 558 559 // test #parameter not exist but parameter exists 560 s = `parameter: { 561 x?: string 562 if x != "" { 563 y: string 564 } 565 }` 566 val = cuecontext.New().CompileString(s) 567 assert.NoError(t, val.Err()) 568 filledVal = FillParameterDefinitionFieldIfNotExist(val) 569 assert.NoError(t, filledVal.Err()) 570 571 // test #parameter as int 572 s = `parameter: #parameter 573 #parameter: int` 574 val = cuecontext.New().CompileString(s) 575 assert.NoError(t, val.Err()) 576 filledVal = FillParameterDefinitionFieldIfNotExist(val) 577 assert.NoError(t, filledVal.Err()) 578 } 579 580 func TestHTTPGetKubernetesObjects(t *testing.T) { 581 _, err := HTTPGetKubernetesObjects(context.Background(), "invalid-url") 582 assert.NotNil(t, err) 583 uns, err := HTTPGetKubernetesObjects(context.Background(), "https://gist.githubusercontent.com/Somefive/b189219a9222eaa70b8908cf4379402b/raw/920e83b1a2d56b584f9d8c7a97810a505a0bbaad/example-busybox-resources.yaml") 584 assert.NoError(t, err) 585 assert.Equal(t, 2, len(uns)) 586 assert.Equal(t, "busybox", uns[0].GetName()) 587 assert.Equal(t, "Deployment", uns[0].GetKind()) 588 assert.Equal(t, "busybox", uns[1].GetName()) 589 assert.Equal(t, "ConfigMap", uns[1].GetKind()) 590 } 591 592 func TestGetRawConfig(t *testing.T) { 593 assert.NoError(t, os.Setenv("KUBECONFIG", filepath.Join("testdata", "testkube.conf"))) 594 ag := Args{} 595 ns := ag.GetNamespaceFromConfig() 596 assert.Equal(t, "prod", ns) 597 }