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  }