github.com/oam-dev/kubevela@v1.9.11/pkg/definition/gen_sdk/gen_sdk_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 gen_sdk
    18  
    19  import (
    20  	"os"
    21  	"path/filepath"
    22  
    23  	"github.com/getkin/kin-openapi/openapi3"
    24  	. "github.com/onsi/ginkgo/v2"
    25  	. "github.com/onsi/gomega"
    26  
    27  	"github.com/oam-dev/kubevela/pkg/utils/common"
    28  )
    29  
    30  var _outputDir string
    31  
    32  var _ = Describe("Test Generating SDK", func() {
    33  	var err error
    34  	outputDir := filepath.Join("testdata", "output")
    35  	_outputDir = outputDir
    36  	lang := "go"
    37  	meta := GenMeta{
    38  		Output:       outputDir,
    39  		Lang:         lang,
    40  		Package:      "github.com/kubevela-contrib/kubevela-go-sdk",
    41  		APIDirectory: defaultAPIDir[lang],
    42  		Verbose:      true,
    43  	}
    44  	var langArgs []string
    45  
    46  	BeforeEach(func() {
    47  		meta.InitSDK = false
    48  		meta.File = []string{filepath.Join("testdata", "cron-task.cue")}
    49  		meta.cuePaths = []string{}
    50  	})
    51  
    52  	checkDirNotEmpty := func(dir string) {
    53  		_, err = os.Stat(dir)
    54  		Expect(err).Should(BeNil())
    55  	}
    56  	genWithMeta := func() {
    57  		err = meta.Init(common.Args{}, langArgs)
    58  		Expect(err).Should(BeNil())
    59  		err = meta.CreateScaffold()
    60  		Expect(err).Should(BeNil())
    61  		err = meta.PrepareGeneratorAndTemplate()
    62  		Expect(err).Should(BeNil())
    63  		err = meta.Run()
    64  		Expect(err).Should(BeNil())
    65  	}
    66  	It("Test generating SDK and init the scaffold", func() {
    67  		meta.InitSDK = true
    68  		genWithMeta()
    69  		checkDirNotEmpty(filepath.Join(outputDir, "pkg", "apis"))
    70  		checkDirNotEmpty(filepath.Join(outputDir, "pkg", "apis", "component", "cron-task"))
    71  	})
    72  
    73  	It("Test generating SDK, append apis", func() {
    74  		meta.File = append(meta.File, "testdata/shared-resource.cue")
    75  
    76  		genWithMeta()
    77  		checkDirNotEmpty(filepath.Join(outputDir, "pkg", "apis", "policy", "shared-resource"))
    78  	})
    79  
    80  	It("Test free form parameter {...}", func() {
    81  		meta.File = []string{"testdata/json-merge-patch.cue"}
    82  		meta.Verbose = true
    83  
    84  		genWithMeta()
    85  		checkDirNotEmpty(filepath.Join(outputDir, "pkg", "apis", "trait", "json-merge-patch"))
    86  	})
    87  
    88  	It("Test workflow step", func() {
    89  		meta.File = []string{"testdata/deploy.cue"}
    90  		meta.Verbose = true
    91  
    92  		genWithMeta()
    93  		checkDirNotEmpty(filepath.Join(outputDir, "pkg", "apis", "workflow-step", "deploy"))
    94  	})
    95  
    96  	It("Test step-group", func() {
    97  		meta.File = []string{"testdata/step-group.cue"}
    98  		meta.Verbose = true
    99  
   100  		genWithMeta()
   101  		checkDirNotEmpty(filepath.Join(outputDir, "pkg", "apis", "workflow-step", "step-group"))
   102  		By("check if AddSubStep is generated")
   103  		content, err := os.ReadFile(filepath.Join(outputDir, "pkg", "apis", "workflow-step", "step-group", "step_group.go"))
   104  		Expect(err).Should(BeNil())
   105  		Expect(string(content)).Should(ContainSubstring("AddSubStep"))
   106  	})
   107  
   108  	It("Test oneOf", func() {
   109  		meta.File = []string{"testdata/one_of.cue"}
   110  		meta.Verbose = true
   111  
   112  		genWithMeta()
   113  		checkDirNotEmpty(filepath.Join(outputDir, "pkg", "apis", "workflow-step", "one_of"))
   114  	})
   115  
   116  	It("Test known issue: apply-terraform-provider", func() {
   117  		meta.Verbose = true
   118  		meta.File = []string{"testdata/apply-terraform-provider.cue"}
   119  		genWithMeta()
   120  	})
   121  
   122  	It("Test generate sub-module", func() {
   123  		meta.APIDirectory = "pkg/apis/addons/test_addon"
   124  		langArgs = []string{
   125  			string(mainModuleVersionKey) + "=" + mainModuleVersion.Default,
   126  		}
   127  		meta.IsSubModule = true
   128  		genWithMeta()
   129  		checkDirNotEmpty(filepath.Join(outputDir, "pkg", "apis", "addons", "test_addon", "component", "cron-task"))
   130  	})
   131  
   132  })
   133  
   134  var _ = AfterSuite(func() {
   135  	By("Cleaning up generated files")
   136  	_ = os.RemoveAll(_outputDir)
   137  })
   138  
   139  var _ = Describe("FixSchemaWithOneAnyAllOf", func() {
   140  	var (
   141  		schema *openapi3.SchemaRef
   142  	)
   143  
   144  	It("should set default value to right sub-schema", func() {
   145  		By(`cpu?: *1 | number | string`)
   146  		schema = &openapi3.SchemaRef{
   147  			Ref: "",
   148  			Value: &openapi3.Schema{
   149  				Default: 1,
   150  				OneOf: openapi3.SchemaRefs{
   151  					{
   152  						Value: &openapi3.Schema{
   153  							Type: "number",
   154  						},
   155  					},
   156  					{
   157  						Value: &openapi3.Schema{
   158  							Type: "string",
   159  						},
   160  					},
   161  				},
   162  			},
   163  		}
   164  		fixSchemaWithOneOf(schema)
   165  
   166  		Expect(schema.Value.OneOf[0].Value.Default).To(Equal(1))
   167  		Expect(schema.Value.OneOf[1].Value.Default).To(BeNil())
   168  		Expect(schema.Value.Default).To(BeNil())
   169  	})
   170  
   171  	It("should remove duplicated type in oneOf", func() {
   172  		By(`language: "go" | "java" | "python" | "node" | "ruby" | string`)
   173  		By(`image: language | string`)
   174  		schema = &openapi3.SchemaRef{
   175  			Value: &openapi3.Schema{
   176  				Type:  "string",
   177  				Title: "image",
   178  				OneOf: openapi3.SchemaRefs{
   179  					{
   180  						Value: &openapi3.Schema{
   181  							Type: "string",
   182  							Enum: []interface{}{"go", "java", "python", "node", "ruby"},
   183  						},
   184  					},
   185  					{
   186  						Value: &openapi3.Schema{
   187  							Type: "string",
   188  						},
   189  					},
   190  				},
   191  			},
   192  		}
   193  		fixSchemaWithOneOf(schema)
   194  
   195  		Expect(schema.Value.OneOf).To(HaveLen(1))
   196  		Expect(schema.Value.OneOf[0].Value.Type).To(Equal("string"))
   197  		Expect(schema.Value.OneOf[0].Value.Enum).To(Equal([]interface{}{"go", "java", "python", "node", "ruby"}))
   198  	})
   199  
   200  	It("should both move type and remove duplicated type in oneOf", func() {
   201  		schema = &openapi3.SchemaRef{
   202  			Value: &openapi3.Schema{
   203  				Type:  "string",
   204  				Title: "image",
   205  				OneOf: openapi3.SchemaRefs{
   206  					{
   207  						Value: &openapi3.Schema{
   208  							Enum: []interface{}{"go", "java", "python", "node", "ruby"},
   209  						},
   210  					},
   211  					{
   212  						Value: &openapi3.Schema{
   213  							Type: "string",
   214  						},
   215  					},
   216  				},
   217  			},
   218  		}
   219  
   220  		fixSchemaWithOneOf(schema)
   221  		Expect(schema.Value.OneOf).To(HaveLen(1))
   222  		Expect(schema.Value.OneOf[0].Value.Type).To(Equal("string"))
   223  		Expect(schema.Value.OneOf[0].Value.Enum).To(Equal([]interface{}{"go", "java", "python", "node", "ruby"}))
   224  	})
   225  })
   226  
   227  var _ = Describe("TestNewLanguageArgs", func() {
   228  	type args struct {
   229  		lang     string
   230  		langArgs []string
   231  	}
   232  	tests := []struct {
   233  		name    string
   234  		args    args
   235  		want    map[string]string
   236  		wantErr bool
   237  	}{
   238  		{
   239  			name: "should create a languageArgs struct with the correct values",
   240  			args: args{
   241  				lang:     "go",
   242  				langArgs: []string{"flag1=value1", "flag2=value2"},
   243  			},
   244  			want:    map[string]string{"flag1": "value1", "flag2": "value2"},
   245  			wantErr: false,
   246  		},
   247  		{
   248  			name: "should not set a value for an unknown flag",
   249  			args: args{
   250  				lang:     "go",
   251  				langArgs: []string{"unknownFlag=value"},
   252  			},
   253  			want:    map[string]string{},
   254  			wantErr: true,
   255  		},
   256  		{
   257  			name: "should warn if an argument is not in the key=value format",
   258  			args: args{
   259  				lang:     "go",
   260  				langArgs: []string{"invalidArgument"},
   261  			},
   262  			want:    map[string]string{},
   263  			wantErr: true,
   264  		},
   265  	}
   266  	for _, tt := range tests {
   267  		It(tt.name, func() {
   268  			got, err := NewLanguageArgs(tt.args.lang, tt.args.langArgs)
   269  			if tt.wantErr {
   270  				Expect(err).To(HaveOccurred())
   271  				return
   272  			}
   273  			for k, v := range tt.want {
   274  				Expect(got.Get(langArgKey(k))).To(Equal(v))
   275  			}
   276  		})
   277  	}
   278  
   279  })
   280  
   281  var _ = Describe("getValueType", func() {
   282  
   283  	type valueTypeTest struct {
   284  		input    interface{}
   285  		expected CUEType
   286  	}
   287  
   288  	tests := []valueTypeTest{
   289  		{nil, ""},
   290  		{"hello", "string"},
   291  		{42, "integer"},
   292  		{float32(3.14), "number"},
   293  		{3.14159265358979323846, "number"},
   294  		{true, "boolean"},
   295  		{map[string]interface{}{"key": "value"}, "object"},
   296  		{[]interface{}{1, 2, 3}, "array"},
   297  	}
   298  
   299  	for _, tt := range tests {
   300  		tt := tt // capture range variable
   301  		It("should return the correct CUEType for the input", func() {
   302  			Expect(getValueType(tt.input)).To(Equal(tt.expected))
   303  		})
   304  	}
   305  })
   306  
   307  var _ = Describe("type fit", func() {
   308  
   309  	var schema *openapi3.Schema
   310  
   311  	BeforeEach(func() {
   312  		schema = &openapi3.Schema{Type: "string"}
   313  	})
   314  
   315  	var testCases = []struct {
   316  		name        string
   317  		cueType     CUEType
   318  		expectedFit bool
   319  		schemaType  string
   320  	}{
   321  		{"string can be oas string", CUEType("string"), true, "string"},
   322  		{"string not oas integer", CUEType("string"), false, "integer"},
   323  		{"integer can be oas integer", CUEType("integer"), true, "integer"},
   324  		{"integer can be oas number", CUEType("integer"), true, "number"},
   325  		{"number can be oas number", CUEType("number"), true, "number"},
   326  		{"number not oas integer", CUEType("number"), false, "integer"},
   327  		{"boolean can be oas boolean", CUEType("boolean"), true, "boolean"},
   328  		{"array can be oas array", CUEType("array"), true, "array"},
   329  		{"invalid type and any schema", CUEType(""), false, "anyschema"},
   330  	}
   331  
   332  	It("should return whether the CUEType fits the schema type or not", func() {
   333  		for _, tc := range testCases {
   334  			schema.Type = tc.schemaType
   335  			result := tc.cueType.fit(schema)
   336  			Expect(result).To(Equal(tc.expectedFit), tc.name)
   337  		}
   338  	})
   339  })