github.com/oam-dev/kubevela@v1.9.11/pkg/cue/script/template_test.go (about)

     1  /*
     2  Copyright 2022 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 script
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  )
    27  
    28  var templateScript = `
    29  metadata: {
    30  	name:      "helm-repository"
    31  	alias:     "Helm Repository"
    32  	scope:     "system"
    33  	sensitive: false
    34  }
    35  template: {
    36  	output: {
    37  		url: parameter.url
    38  	}
    39  	parameter: {
    40  		// +usage=The public url of the helm chart repository.
    41  		url: string
    42  		// +usage=The username of basic auth repo.
    43  		username: string
    44  		// +usage=The password of basic auth repo.
    45  		password?: string
    46  		// +usage=The ca certificate of helm repository. Please encode this data with base64.
    47  		caFile?: string
    48  
    49  		options: "o1" | "o2"
    50  	}
    51  }
    52  `
    53  
    54  var templateWithContextScript = `
    55  import (
    56  	"strconv"
    57    )
    58  metadata: {
    59  	name:      "helm-repository"
    60  	alias:     "Helm Repository"
    61  	scope:     "system"
    62  	sensitive: false
    63  }
    64  template: {
    65  	output: {
    66  		url: parameter.url
    67  		name: context.name
    68  		namespace: context.namespace
    69  		sensitive: strconv.FormatBool(metadata.sensitive)
    70  	}
    71  	parameter: {
    72  		// +usage=The public url of the helm chart repository.
    73  		url: string
    74  		// +usage=The username of basic auth repo.
    75  		username: string
    76  		// +usage=The password of basic auth repo.
    77  		password?: string
    78  		// +usage=The ca certificate of helm repository. Please encode this data with base64.
    79  		caFile?: string
    80  	}
    81  }
    82  `
    83  
    84  var withPackage = `
    85  
    86  package main
    87  
    88  const: {
    89  	// +usage=The name of the addon application
    90  	name: "addon-loki"
    91  }
    92  
    93  outputs: {
    94  	a: context.appName
    95  }
    96  
    97  parameter: {
    98  
    99  	// global parameters
   100  
   101  	// +usage=The namespace of the loki to be installed
   102  	namespace: *"o11y-system" | string
   103  	// +usage=The clusters to install
   104  	clusters?: [...string]
   105  
   106  	// loki parameters
   107  
   108  	// +usage=Specify the image of loki
   109  	image: *"grafana/loki" | string
   110  	// +usage=Specify the imagePullPolicy of the image
   111  	imagePullPolicy: *"IfNotPresent" | "Never" | "Always"
   112  	// +usage=Specify the service type for expose loki. If empty, it will be not exposed.
   113  	serviceType: *"ClusterIP" | "NodePort" | "LoadBalancer" | ""
   114  	// +usage=Specify the storage size to use. If empty, emptyDir will be used. Otherwise pvc will be used.
   115  	storage?: =~"^([1-9][0-9]{0,63})(E|P|T|G|M|K|Ei|Pi|Ti|Gi|Mi|Ki)$"
   116  	// +usage=Specify the storage class to use.
   117  	storageClassName?: string
   118  
   119  	// agent parameters
   120  
   121  	// +usage=Specify the type of log agents, if empty, no agent will be installed
   122  	agent: *"" | "vector" | "promtail"
   123  	// +usage=Specify the image of promtail
   124  	promtailImage: *"grafana/promtail" | string
   125  	// +usage=Specify the image of vector
   126  	vectorImage: *"timberio/vector:0.24.0-distroless-libc" | string
   127  }
   128  `
   129  
   130  var withImport = `
   131  import (
   132  	"vela/op"
   133  )
   134  
   135  apply: op.#Apply & {
   136  	value:   parameter.value
   137  	cluster: parameter.cluster
   138  }
   139  parameter: {
   140  	// +usage=Specify the value of the object
   141  	value: {...}
   142  	// +usage=Specify the cluster of the object
   143  	cluster: *"" | string
   144  }`
   145  
   146  var withTemplate = `
   147  metadata: {
   148  	name: "xxx"
   149  }
   150  template: {
   151  	output: {
   152  		name:    metadata.name
   153  		appName: context.appName
   154  		value:   parameter.value
   155  	}
   156  	parameter: {
   157  		// +usage=Specify the value of the object
   158  		value: {...}
   159  		// +usage=Specify the cluster of the object
   160  		cluster: *"" | string
   161  	}
   162  }
   163  `
   164  
   165  func TestMergeValues(t *testing.T) {
   166  	var cueScript = CUE(templateScript)
   167  	value, err := cueScript.MergeValues(nil, map[string]interface{}{
   168  		"url":      "hub.docker.com",
   169  		"username": "name",
   170  	})
   171  	assert.Equal(t, err, nil)
   172  	output, err := value.LookupValue("template", "output")
   173  	assert.Equal(t, err, nil)
   174  	var data = map[string]interface{}{}
   175  	err = output.UnmarshalTo(&data)
   176  	assert.Equal(t, err, nil)
   177  	assert.Equal(t, data["url"], "hub.docker.com")
   178  }
   179  
   180  func TestRunAndOutput(t *testing.T) {
   181  	var cueScript = BuildCUEScriptWithDefaultContext([]byte("context:{namespace:string \n name:string}"), []byte(templateWithContextScript))
   182  	output, err := cueScript.RunAndOutput(map[string]interface{}{
   183  		"name":      "nnn",
   184  		"namespace": "ns",
   185  	}, map[string]interface{}{
   186  		"url":      "hub.docker.com",
   187  		"username": "test",
   188  		"password": "test",
   189  		"caFile":   "test ca",
   190  	})
   191  	assert.Equal(t, err, nil)
   192  	var data = map[string]interface{}{}
   193  	err = output.UnmarshalTo(&data)
   194  	assert.Equal(t, err, nil)
   195  	assert.Equal(t, data["name"], "nnn")
   196  	assert.Equal(t, data["namespace"], "ns")
   197  	assert.Equal(t, data["url"], "hub.docker.com")
   198  }
   199  
   200  func TestRunAndOutputWithCueX(t *testing.T) {
   201  	var cueScript = BuildCUEScriptWithDefaultContext([]byte("context:{namespace:string \n name:string}"), []byte(templateWithContextScript))
   202  	output, err := cueScript.RunAndOutputWithCueX(context.Background(), map[string]interface{}{
   203  		"name":      "nnn",
   204  		"namespace": "ns",
   205  	}, map[string]interface{}{
   206  		"url":      "hub.docker.com",
   207  		"username": "test",
   208  		"password": "test",
   209  		"caFile":   "test ca",
   210  	}, "template", "output")
   211  	assert.Equal(t, err, nil)
   212  	var data = map[string]interface{}{}
   213  	err = output.Decode(&data)
   214  	assert.Equal(t, err, nil)
   215  	assert.Equal(t, data["name"], "nnn")
   216  	assert.Equal(t, data["namespace"], "ns")
   217  	assert.Equal(t, data["url"], "hub.docker.com")
   218  }
   219  
   220  func TestValidateProperties(t *testing.T) {
   221  	var cueScript = CUE(templateScript)
   222  	// miss the required parameter
   223  	err := cueScript.ValidateProperties(map[string]interface{}{
   224  		"url": "hub.docker.com",
   225  	})
   226  	assert.Equal(t, err.(*ParameterError).Message, "This parameter is required")
   227  
   228  	// wrong the parameter value type
   229  	err = cueScript.ValidateProperties(map[string]interface{}{
   230  		"url":      1,
   231  		"username": "ddd",
   232  	})
   233  	assert.Equal(t, strings.Contains(err.(*ParameterError).Message, "conflicting values"), true)
   234  	assert.Equal(t, strings.Contains(err.(*ParameterError).Name, "url"), true)
   235  
   236  	// wrong the parameter value
   237  	err = cueScript.ValidateProperties(map[string]interface{}{
   238  		"url":      "ddd",
   239  		"username": "ddd",
   240  	})
   241  	assert.Equal(t, strings.Contains(err.(*ParameterError).Message, "This parameter is required"), true)
   242  	assert.Equal(t, strings.Contains(err.(*ParameterError).Name, "options"), true)
   243  
   244  	// wrong the parameter value and no required value
   245  	err = cueScript.ValidateProperties(map[string]interface{}{
   246  		"url":      "ddd",
   247  		"username": "ddd",
   248  		"options":  "o3",
   249  	})
   250  	fmt.Println(err.(*ParameterError).Message)
   251  	assert.Equal(t, strings.Contains(err.(*ParameterError).Name, "options"), true)
   252  	assert.Equal(t, strings.Contains(err.(*ParameterError).Message, "2 errors in empty disjunction"), true)
   253  }
   254  
   255  func TestValidatePropertiesWithCueX(t *testing.T) {
   256  	var cueScript = CUE(templateScript)
   257  	// miss the required parameter
   258  	err := cueScript.ValidatePropertiesWithCueX(map[string]interface{}{
   259  		"url": "hub.docker.com",
   260  	})
   261  	assert.Equal(t, err.(*ParameterError).Message, "This parameter is required")
   262  
   263  	// wrong the parameter value type
   264  	err = cueScript.ValidatePropertiesWithCueX(map[string]interface{}{
   265  		"url":      1,
   266  		"username": "ddd",
   267  	})
   268  	assert.Equal(t, strings.Contains(err.(*ParameterError).Message, "conflicting values"), true)
   269  	assert.Equal(t, strings.Contains(err.(*ParameterError).Name, "url"), true)
   270  
   271  	// wrong the parameter value
   272  	err = cueScript.ValidatePropertiesWithCueX(map[string]interface{}{
   273  		"url":      "ddd",
   274  		"username": "ddd",
   275  	})
   276  	assert.Equal(t, strings.Contains(err.(*ParameterError).Message, "This parameter is required"), true)
   277  	assert.Equal(t, strings.Contains(err.(*ParameterError).Name, "options"), true)
   278  
   279  	// wrong the parameter value and no required value
   280  	err = cueScript.ValidatePropertiesWithCueX(map[string]interface{}{
   281  		"url":      "ddd",
   282  		"username": "ddd",
   283  		"options":  "o3",
   284  	})
   285  	fmt.Println(err.(*ParameterError).Message)
   286  	assert.Equal(t, strings.Contains(err.(*ParameterError).Name, "options"), true)
   287  	assert.Equal(t, strings.Contains(err.(*ParameterError).Message, "2 errors in empty disjunction"), true)
   288  }
   289  
   290  func TestParsePropertiesToSchema(t *testing.T) {
   291  	cue := CUE(withPackage)
   292  	schema, err := cue.ParsePropertiesToSchema()
   293  	assert.Equal(t, err, nil)
   294  	assert.Equal(t, len(schema.Properties), 10)
   295  
   296  	cue = CUE(withImport)
   297  	schema, err = cue.ParsePropertiesToSchema()
   298  	assert.Equal(t, err, nil)
   299  	assert.Equal(t, len(schema.Properties), 2)
   300  
   301  	cue = CUE([]byte(withTemplate))
   302  	schema, err = cue.ParsePropertiesToSchema("template")
   303  	assert.Equal(t, err, nil)
   304  	assert.Equal(t, len(schema.Properties), 2)
   305  }
   306  
   307  func TestParsePropertiesToSchemaWithCueX(t *testing.T) {
   308  	cue := CUE([]byte(withPackage))
   309  	schema, err := cue.ParsePropertiesToSchemaWithCueX("")
   310  	assert.Equal(t, err, nil)
   311  	assert.Equal(t, len(schema.Properties), 10)
   312  
   313  	cue = CUE([]byte(withTemplate))
   314  	schema, err = cue.ParsePropertiesToSchemaWithCueX("template")
   315  	assert.Equal(t, err, nil)
   316  	assert.Equal(t, len(schema.Properties), 2)
   317  }