k8s.io/kube-openapi@v0.0.0-20240826222958-65a50c78dec5/pkg/generators/openapi_test.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes 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 generators
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"go/format"
    23  	"path"
    24  	"strings"
    25  	"testing"
    26  
    27  	"github.com/google/go-cmp/cmp"
    28  	"golang.org/x/tools/go/packages"
    29  	"golang.org/x/tools/go/packages/packagestest"
    30  	"k8s.io/gengo/v2/generator"
    31  	"k8s.io/gengo/v2/namer"
    32  	"k8s.io/gengo/v2/parser"
    33  	"k8s.io/gengo/v2/types"
    34  )
    35  
    36  func construct(t *testing.T, cfg *packages.Config, nameSystems namer.NameSystems, defaultSystem string, pkg string) *generator.Context {
    37  	p := parser.New()
    38  	if err := p.LoadPackagesWithConfigForTesting(cfg, pkg); err != nil {
    39  		t.Fatalf("failed to load package: %v", err)
    40  	}
    41  	c, err := generator.NewContext(p, nameSystems, defaultSystem)
    42  	if err != nil {
    43  		t.Fatalf("failed to make a context: %v", err)
    44  	}
    45  	return c
    46  }
    47  
    48  func testOpenAPITypeWriter(t *testing.T, cfg *packages.Config) (error, error, *bytes.Buffer, *bytes.Buffer, []string) {
    49  	pkgBase := "example.com/base"
    50  	// `path` vs. `filepath` because packages use '/'
    51  	inputPkg := path.Join(pkgBase, "foo")
    52  	outputPkg := path.Join(pkgBase, "output")
    53  	imports := generator.NewImportTrackerForPackage(outputPkg)
    54  	rawNamer := namer.NewRawNamer(outputPkg, imports)
    55  	namers := namer.NameSystems{
    56  		"raw": rawNamer,
    57  		"private": &namer.NameStrategy{
    58  			Join: func(pre string, in []string, post string) string {
    59  				return strings.Join(in, "_")
    60  			},
    61  			PrependPackageNames: 4, // enough to fully qualify from k8s.io/api/...
    62  		},
    63  	}
    64  	context := construct(t, cfg, namers, "raw", inputPkg)
    65  	universe := context.Universe
    66  	blahT := universe.Type(types.Name{Package: inputPkg, Name: "Blah"})
    67  
    68  	callBuffer := &bytes.Buffer{}
    69  	callSW := generator.NewSnippetWriter(callBuffer, context, "$", "$")
    70  	callError := newOpenAPITypeWriter(callSW, context).generateCall(blahT)
    71  
    72  	funcBuffer := &bytes.Buffer{}
    73  	funcSW := generator.NewSnippetWriter(funcBuffer, context, "$", "$")
    74  	funcError := newOpenAPITypeWriter(funcSW, context).generate(blahT)
    75  
    76  	return callError, funcError, callBuffer, funcBuffer, imports.ImportLines()
    77  }
    78  
    79  // NOTE: the usual order of arguments for an assertion would be want, got, but
    80  // this helper function flips that in favor of callsite readability.
    81  func assertEqual(t *testing.T, got, want string) {
    82  	t.Helper()
    83  	want = strings.TrimSpace(want)
    84  	got = strings.TrimSpace(got)
    85  	if !cmp.Equal(want, got) {
    86  		t.Errorf("Wrong result:\n%s", cmp.Diff(want, got))
    87  	}
    88  }
    89  
    90  func TestSimple(t *testing.T) {
    91  	inputFile := `
    92  		package foo
    93  
    94  		// Blah is a test.
    95  		// +k8s:openapi-gen=true
    96  		// +k8s:openapi-gen=x-kubernetes-type-tag:type_test
    97  		type Blah struct {
    98  			// A simple string
    99  			String string
   100  			// A simple int
   101  			Int int ` + "`" + `json:",omitempty"` + "`" + `
   102  			// An int considered string simple int
   103  			IntString int ` + "`" + `json:",string"` + "`" + `
   104  			// A simple int64
   105  			Int64 int64
   106  			// A simple int32
   107  			Int32 int32
   108  			// A simple int16
   109  			Int16 int16
   110  			// A simple int8
   111  			Int8 int8
   112  			// A simple int
   113  			Uint uint
   114  			// A simple int64
   115  			Uint64 uint64
   116  			// A simple int32
   117  			Uint32 uint32
   118  			// A simple int16
   119  			Uint16 uint16
   120  			// A simple int8
   121  			Uint8 uint8
   122  			// A simple byte
   123  			Byte byte
   124  			// A simple boolean
   125  			Bool bool
   126  			// A simple float64
   127  			Float64 float64
   128  			// A simple float32
   129  			Float32 float32
   130  			// a base64 encoded characters
   131  			ByteArray []byte
   132  			// a member with an extension
   133  			// +k8s:openapi-gen=x-kubernetes-member-tag:member_test
   134  			WithExtension string
   135  			// a member with struct tag as extension
   136  			// +patchStrategy=merge
   137  			// +patchMergeKey=pmk
   138  			WithStructTagExtension string ` + "`" + `patchStrategy:"merge" patchMergeKey:"pmk"` + "`" + `
   139  			// a member with a list type
   140  			// +listType=atomic
   141  			// +default=["foo", "bar"]
   142  			WithListType []string
   143  			// a member with a map type
   144  			// +listType=atomic
   145  			// +default={"foo": "bar", "fizz": "buzz"}
   146  			Map map[string]string
   147  			// a member with a string pointer
   148  			// +default="foo"
   149  			StringPointer *string
   150  			// an int member with a default
   151  			// +default=1
   152  			OmittedInt int ` + "`" + `json:"omitted,omitempty"` + "`" + `
   153  			// a field with an invalid escape sequence in comment
   154  			// ex) regexp:^.*\.yaml$
   155  			InvalidEscapeSequenceInComment string
   156  		}`
   157  
   158  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
   159  		e := packagestest.Export(t, x, []packagestest.Module{{
   160  			Name: "example.com/base/foo",
   161  			Files: map[string]interface{}{
   162  				"foo.go": inputFile,
   163  			},
   164  		}})
   165  		defer e.Cleanup()
   166  
   167  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
   168  		if callErr != nil {
   169  			t.Fatal(callErr)
   170  		}
   171  		if funcErr != nil {
   172  			t.Fatal(funcErr)
   173  		}
   174  		assertEqual(t, callBuffer.String(),
   175  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
   176  
   177  		assertEqual(t, funcBuffer.String(),
   178  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
   179  return common.OpenAPIDefinition{
   180  Schema: spec.Schema{
   181  SchemaProps: spec.SchemaProps{
   182  Description: "Blah is a test.",
   183  Type: []string{"object"},
   184  Properties: map[string]spec.Schema{
   185  "String": {
   186  SchemaProps: spec.SchemaProps{
   187  Description: "A simple string",
   188  Default: "",
   189  Type: []string{"string"},
   190  Format: "",
   191  },
   192  },
   193  "Int64": {
   194  SchemaProps: spec.SchemaProps{
   195  Description: "A simple int64",
   196  Default: 0,
   197  Type: []string{"integer"},
   198  Format: "int64",
   199  },
   200  },
   201  "Int32": {
   202  SchemaProps: spec.SchemaProps{
   203  Description: "A simple int32",
   204  Default: 0,
   205  Type: []string{"integer"},
   206  Format: "int32",
   207  },
   208  },
   209  "Int16": {
   210  SchemaProps: spec.SchemaProps{
   211  Description: "A simple int16",
   212  Default: 0,
   213  Type: []string{"integer"},
   214  Format: "int32",
   215  },
   216  },
   217  "Int8": {
   218  SchemaProps: spec.SchemaProps{
   219  Description: "A simple int8",
   220  Default: 0,
   221  Type: []string{"integer"},
   222  Format: "byte",
   223  },
   224  },
   225  "Uint": {
   226  SchemaProps: spec.SchemaProps{
   227  Description: "A simple int",
   228  Default: 0,
   229  Type: []string{"integer"},
   230  Format: "int32",
   231  },
   232  },
   233  "Uint64": {
   234  SchemaProps: spec.SchemaProps{
   235  Description: "A simple int64",
   236  Default: 0,
   237  Type: []string{"integer"},
   238  Format: "int64",
   239  },
   240  },
   241  "Uint32": {
   242  SchemaProps: spec.SchemaProps{
   243  Description: "A simple int32",
   244  Default: 0,
   245  Type: []string{"integer"},
   246  Format: "int64",
   247  },
   248  },
   249  "Uint16": {
   250  SchemaProps: spec.SchemaProps{
   251  Description: "A simple int16",
   252  Default: 0,
   253  Type: []string{"integer"},
   254  Format: "int32",
   255  },
   256  },
   257  "Uint8": {
   258  SchemaProps: spec.SchemaProps{
   259  Description: "A simple int8",
   260  Default: 0,
   261  Type: []string{"integer"},
   262  Format: "byte",
   263  },
   264  },
   265  "Byte": {
   266  SchemaProps: spec.SchemaProps{
   267  Description: "A simple byte",
   268  Default: 0,
   269  Type: []string{"integer"},
   270  Format: "byte",
   271  },
   272  },
   273  "Bool": {
   274  SchemaProps: spec.SchemaProps{
   275  Description: "A simple boolean",
   276  Default: false,
   277  Type: []string{"boolean"},
   278  Format: "",
   279  },
   280  },
   281  "Float64": {
   282  SchemaProps: spec.SchemaProps{
   283  Description: "A simple float64",
   284  Default: 0,
   285  Type: []string{"number"},
   286  Format: "double",
   287  },
   288  },
   289  "Float32": {
   290  SchemaProps: spec.SchemaProps{
   291  Description: "A simple float32",
   292  Default: 0,
   293  Type: []string{"number"},
   294  Format: "float",
   295  },
   296  },
   297  "ByteArray": {
   298  SchemaProps: spec.SchemaProps{
   299  Description: "a base64 encoded characters",
   300  Type: []string{"string"},
   301  Format: "byte",
   302  },
   303  },
   304  "WithExtension": {
   305  VendorExtensible: spec.VendorExtensible{
   306  Extensions: spec.Extensions{
   307  "x-kubernetes-member-tag": "member_test",
   308  },
   309  },
   310  SchemaProps: spec.SchemaProps{
   311  Description: "a member with an extension",
   312  Default: "",
   313  Type: []string{"string"},
   314  Format: "",
   315  },
   316  },
   317  "WithStructTagExtension": {
   318  VendorExtensible: spec.VendorExtensible{
   319  Extensions: spec.Extensions{
   320  "x-kubernetes-patch-merge-key": "pmk",
   321  "x-kubernetes-patch-strategy": "merge",
   322  },
   323  },
   324  SchemaProps: spec.SchemaProps{
   325  Description: "a member with struct tag as extension",
   326  Default: "",
   327  Type: []string{"string"},
   328  Format: "",
   329  },
   330  },
   331  "WithListType": {
   332  VendorExtensible: spec.VendorExtensible{
   333  Extensions: spec.Extensions{
   334  "x-kubernetes-list-type": "atomic",
   335  },
   336  },
   337  SchemaProps: spec.SchemaProps{
   338  Description: "a member with a list type",
   339  Default: []interface {}{"foo", "bar"},
   340  Type: []string{"array"},
   341  Items: &spec.SchemaOrArray{
   342  Schema: &spec.Schema{
   343  SchemaProps: spec.SchemaProps{
   344  Default: "",
   345  Type: []string{"string"},
   346  Format: "",
   347  },
   348  },
   349  },
   350  },
   351  },
   352  "Map": {
   353  VendorExtensible: spec.VendorExtensible{
   354  Extensions: spec.Extensions{
   355  "x-kubernetes-list-type": "atomic",
   356  },
   357  },
   358  SchemaProps: spec.SchemaProps{
   359  Description: "a member with a map type",
   360  Default: map[string]interface {}{"fizz":"buzz", "foo":"bar"},
   361  Type: []string{"object"},
   362  AdditionalProperties: &spec.SchemaOrBool{
   363  Allows: true,
   364  Schema: &spec.Schema{
   365  SchemaProps: spec.SchemaProps{
   366  Default: "",
   367  Type: []string{"string"},
   368  Format: "",
   369  },
   370  },
   371  },
   372  },
   373  },
   374  "StringPointer": {
   375  SchemaProps: spec.SchemaProps{
   376  Description: "a member with a string pointer",
   377  Default: "foo",
   378  Type: []string{"string"},
   379  Format: "",
   380  },
   381  },
   382  "omitted": {
   383  SchemaProps: spec.SchemaProps{
   384  Description: "an int member with a default",
   385  Default: 1,
   386  Type: []string{"integer"},
   387  Format: "int32",
   388  },
   389  },
   390  "InvalidEscapeSequenceInComment": {
   391  SchemaProps: spec.SchemaProps{
   392  Description: "a field with an invalid escape sequence in comment ex) regexp:^.*\\.yaml$",
   393  Default: "",
   394  Type: []string{"string"},
   395  Format: "",
   396  },
   397  },
   398  },
   399  Required: []string{"String","Int64","Int32","Int16","Int8","Uint","Uint64","Uint32","Uint16","Uint8","Byte","Bool","Float64","Float32","ByteArray","WithExtension","WithStructTagExtension","WithListType","Map","StringPointer","InvalidEscapeSequenceInComment"},
   400  },
   401  VendorExtensible: spec.VendorExtensible{
   402  Extensions: spec.Extensions{
   403  "x-kubernetes-type-tag": "type_test",
   404  },
   405  },
   406  },
   407  }
   408  }`)
   409  	})
   410  }
   411  
   412  func TestEmptyProperties(t *testing.T) {
   413  	inputFile := `
   414  		package foo
   415  
   416  		// Blah demonstrate a struct without fields.
   417  		type Blah struct {
   418  		}`
   419  
   420  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
   421  		e := packagestest.Export(t, x, []packagestest.Module{{
   422  			Name: "example.com/base/foo",
   423  			Files: map[string]interface{}{
   424  				"foo.go": inputFile,
   425  			},
   426  		}})
   427  		defer e.Cleanup()
   428  
   429  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
   430  		if callErr != nil {
   431  			t.Fatal(callErr)
   432  		}
   433  		if funcErr != nil {
   434  			t.Fatal(funcErr)
   435  		}
   436  		assertEqual(t, callBuffer.String(),
   437  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
   438  		assertEqual(t, funcBuffer.String(),
   439  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
   440  return common.OpenAPIDefinition{
   441  Schema: spec.Schema{
   442  SchemaProps: spec.SchemaProps{
   443  Description: "Blah demonstrate a struct without fields.",
   444  Type: []string{"object"},
   445  },
   446  },
   447  }
   448  }`)
   449  	})
   450  }
   451  
   452  func TestNestedStruct(t *testing.T) {
   453  	inputFile := `
   454  		package foo
   455  
   456  		// Nested is used as struct field
   457  		type Nested struct {
   458  		  // A simple string
   459  		  String string
   460  		}
   461  
   462  		// Blah demonstrate a struct with struct field.
   463  		type Blah struct {
   464  		  // A struct field
   465  		  Field Nested
   466  		}`
   467  
   468  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
   469  		e := packagestest.Export(t, x, []packagestest.Module{{
   470  			Name: "example.com/base/foo",
   471  			Files: map[string]interface{}{
   472  				"foo.go": inputFile,
   473  			},
   474  		}})
   475  		defer e.Cleanup()
   476  
   477  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
   478  		if callErr != nil {
   479  			t.Fatal(callErr)
   480  		}
   481  		if funcErr != nil {
   482  			t.Fatal(funcErr)
   483  		}
   484  		assertEqual(t, callBuffer.String(),
   485  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
   486  		assertEqual(t, funcBuffer.String(),
   487  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
   488  return common.OpenAPIDefinition{
   489  Schema: spec.Schema{
   490  SchemaProps: spec.SchemaProps{
   491  Description: "Blah demonstrate a struct with struct field.",
   492  Type: []string{"object"},
   493  Properties: map[string]spec.Schema{
   494  "Field": {
   495  SchemaProps: spec.SchemaProps{
   496  Description: "A struct field",
   497  Default: map[string]interface {}{},
   498  Ref: ref("example.com/base/foo.Nested"),
   499  },
   500  },
   501  },
   502  Required: []string{"Field"},
   503  },
   504  },
   505  Dependencies: []string{
   506  "example.com/base/foo.Nested",},
   507  }
   508  }`)
   509  	})
   510  }
   511  
   512  func TestNestedStructPointer(t *testing.T) {
   513  	inputFile := `
   514  		package foo
   515  
   516  		// Nested is used as struct pointer field
   517  		type Nested struct {
   518  		  // A simple string
   519  		  String string
   520  		}
   521  
   522  		// Blah demonstrate a struct with struct pointer field.
   523  		type Blah struct {
   524  		  // A struct pointer field
   525  		  Field *Nested
   526  		}`
   527  
   528  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
   529  		e := packagestest.Export(t, x, []packagestest.Module{{
   530  			Name: "example.com/base/foo",
   531  			Files: map[string]interface{}{
   532  				"foo.go": inputFile,
   533  			},
   534  		}})
   535  		defer e.Cleanup()
   536  
   537  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
   538  
   539  		if callErr != nil {
   540  			t.Fatal(callErr)
   541  		}
   542  		if funcErr != nil {
   543  			t.Fatal(funcErr)
   544  		}
   545  		assertEqual(t, callBuffer.String(),
   546  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
   547  		assertEqual(t, funcBuffer.String(),
   548  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
   549  return common.OpenAPIDefinition{
   550  Schema: spec.Schema{
   551  SchemaProps: spec.SchemaProps{
   552  Description: "Blah demonstrate a struct with struct pointer field.",
   553  Type: []string{"object"},
   554  Properties: map[string]spec.Schema{
   555  "Field": {
   556  SchemaProps: spec.SchemaProps{
   557  Description: "A struct pointer field",
   558  Ref: ref("example.com/base/foo.Nested"),
   559  },
   560  },
   561  },
   562  Required: []string{"Field"},
   563  },
   564  },
   565  Dependencies: []string{
   566  "example.com/base/foo.Nested",},
   567  }
   568  }`)
   569  	})
   570  }
   571  
   572  func TestEmbeddedStruct(t *testing.T) {
   573  	inputFile := `
   574  		package foo
   575  
   576  		// Nested is used as embedded struct field
   577  		type Nested struct {
   578  		  // A simple string
   579  		  String string
   580  		}
   581  
   582  		// Blah demonstrate a struct with embedded struct field.
   583  		type Blah struct {
   584  		  // An embedded struct field
   585  		  Nested
   586  		}`
   587  
   588  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
   589  		e := packagestest.Export(t, x, []packagestest.Module{{
   590  			Name: "example.com/base/foo",
   591  			Files: map[string]interface{}{
   592  				"foo.go": inputFile,
   593  			},
   594  		}})
   595  		defer e.Cleanup()
   596  
   597  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
   598  		if callErr != nil {
   599  			t.Fatal(callErr)
   600  		}
   601  		if funcErr != nil {
   602  			t.Fatal(funcErr)
   603  		}
   604  		assertEqual(t, callBuffer.String(),
   605  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
   606  		assertEqual(t, funcBuffer.String(),
   607  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
   608  return common.OpenAPIDefinition{
   609  Schema: spec.Schema{
   610  SchemaProps: spec.SchemaProps{
   611  Description: "Blah demonstrate a struct with embedded struct field.",
   612  Type: []string{"object"},
   613  Properties: map[string]spec.Schema{
   614  "Nested": {
   615  SchemaProps: spec.SchemaProps{
   616  Description: "An embedded struct field",
   617  Default: map[string]interface {}{},
   618  Ref: ref("example.com/base/foo.Nested"),
   619  },
   620  },
   621  },
   622  Required: []string{"Nested"},
   623  },
   624  },
   625  Dependencies: []string{
   626  "example.com/base/foo.Nested",},
   627  }
   628  }`)
   629  	})
   630  }
   631  
   632  func TestSingleEmbeddedStruct(t *testing.T) {
   633  	inputFile := `
   634  		package foo
   635  
   636  		import "time"
   637  
   638  		// Nested is used as embedded struct field
   639  		type Nested struct {
   640  		  // A simple string
   641  		  time.Duration
   642  		}
   643  
   644  		// Blah demonstrate a struct with embedded struct field.
   645  		type Blah struct {
   646  		  // An embedded struct field
   647  		  // +default="10ms"
   648  		  Nested ` + "`" + `json:"nested,omitempty" protobuf:"bytes,5,opt,name=nested"` + "`" + `
   649  		}`
   650  
   651  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
   652  		e := packagestest.Export(t, x, []packagestest.Module{{
   653  			Name: "example.com/base/foo",
   654  			Files: map[string]interface{}{
   655  				"foo.go": inputFile,
   656  			},
   657  		}})
   658  		defer e.Cleanup()
   659  
   660  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
   661  		if callErr != nil {
   662  			t.Fatal(callErr)
   663  		}
   664  		if funcErr != nil {
   665  			t.Fatal(funcErr)
   666  		}
   667  		assertEqual(t, callBuffer.String(),
   668  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
   669  		assertEqual(t, funcBuffer.String(),
   670  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
   671  return common.OpenAPIDefinition{
   672  Schema: spec.Schema{
   673  SchemaProps: spec.SchemaProps{
   674  Description: "Blah demonstrate a struct with embedded struct field.",
   675  Type: []string{"object"},
   676  Properties: map[string]spec.Schema{
   677  "nested": {
   678  SchemaProps: spec.SchemaProps{
   679  Description: "An embedded struct field",
   680  Default: "10ms",
   681  Ref: ref("example.com/base/foo.Nested"),
   682  },
   683  },
   684  },
   685  },
   686  },
   687  Dependencies: []string{
   688  "example.com/base/foo.Nested",},
   689  }
   690  }`)
   691  	})
   692  }
   693  
   694  func TestEmbeddedInlineStruct(t *testing.T) {
   695  	inputFile := `
   696  	package foo
   697  
   698  		// Nested is used as embedded inline struct field
   699  		type Nested struct {
   700  		  // A simple string
   701  		  String string
   702  		}
   703  
   704  		// Blah demonstrate a struct with embedded inline struct field.
   705  		type Blah struct {
   706  		  // An embedded inline struct field
   707  		  Nested ` + "`" + `json:",inline,omitempty"` + "`" + `
   708  		}`
   709  
   710  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
   711  		e := packagestest.Export(t, x, []packagestest.Module{{
   712  			Name: "example.com/base/foo",
   713  			Files: map[string]interface{}{
   714  				"foo.go": inputFile,
   715  			},
   716  		}})
   717  		defer e.Cleanup()
   718  
   719  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
   720  		if callErr != nil {
   721  			t.Fatal(callErr)
   722  		}
   723  		if funcErr != nil {
   724  			t.Fatal(funcErr)
   725  		}
   726  		assertEqual(t, callBuffer.String(),
   727  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
   728  		assertEqual(t, funcBuffer.String(),
   729  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
   730  return common.OpenAPIDefinition{
   731  Schema: spec.Schema{
   732  SchemaProps: spec.SchemaProps{
   733  Description: "Blah demonstrate a struct with embedded inline struct field.",
   734  Type: []string{"object"},
   735  Properties: map[string]spec.Schema{
   736  "String": {
   737  SchemaProps: spec.SchemaProps{
   738  Description: "A simple string",
   739  Default: "",
   740  Type: []string{"string"},
   741  Format: "",
   742  },
   743  },
   744  },
   745  Required: []string{"String"},
   746  },
   747  },
   748  }
   749  }`)
   750  	})
   751  }
   752  
   753  func TestEmbeddedInlineStructPointer(t *testing.T) {
   754  	inputFile := `
   755  		package foo
   756  
   757  		// Nested is used as embedded inline struct pointer field.
   758  		type Nested struct {
   759  		  // A simple string
   760  		  String string
   761  		}
   762  
   763  		// Blah demonstrate a struct with embedded inline struct pointer field.
   764  		type Blah struct {
   765  		  // An embedded inline struct pointer field
   766  		  *Nested ` + "`" + `json:",inline,omitempty"` + "`" + `
   767  		}`
   768  
   769  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
   770  		e := packagestest.Export(t, x, []packagestest.Module{{
   771  			Name: "example.com/base/foo",
   772  			Files: map[string]interface{}{
   773  				"foo.go": inputFile,
   774  			},
   775  		}})
   776  		defer e.Cleanup()
   777  
   778  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
   779  		if callErr != nil {
   780  			t.Fatal(callErr)
   781  		}
   782  		if funcErr != nil {
   783  			t.Fatal(funcErr)
   784  		}
   785  		assertEqual(t, callBuffer.String(),
   786  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
   787  		assertEqual(t, funcBuffer.String(),
   788  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
   789  return common.OpenAPIDefinition{
   790  Schema: spec.Schema{
   791  SchemaProps: spec.SchemaProps{
   792  Description: "Blah demonstrate a struct with embedded inline struct pointer field.",
   793  Type: []string{"object"},
   794  Properties: map[string]spec.Schema{
   795  "String": {
   796  SchemaProps: spec.SchemaProps{
   797  Description: "A simple string",
   798  Default: "",
   799  Type: []string{"string"},
   800  Format: "",
   801  },
   802  },
   803  },
   804  Required: []string{"String"},
   805  },
   806  },
   807  }
   808  }`)
   809  	})
   810  }
   811  
   812  func TestNestedMapString(t *testing.T) {
   813  	inputFile := `
   814  		package foo
   815  
   816  		// Map sample tests openAPIGen.generateMapProperty method.
   817  		type Blah struct {
   818  			// A sample String to String map
   819  			StringToArray map[string]map[string]string
   820  		}`
   821  
   822  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
   823  		e := packagestest.Export(t, x, []packagestest.Module{{
   824  			Name: "example.com/base/foo",
   825  			Files: map[string]interface{}{
   826  				"foo.go": inputFile,
   827  			},
   828  		}})
   829  		defer e.Cleanup()
   830  
   831  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
   832  		if callErr != nil {
   833  			t.Fatal(callErr)
   834  		}
   835  		if funcErr != nil {
   836  			t.Fatal(funcErr)
   837  		}
   838  		assertEqual(t, callBuffer.String(),
   839  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
   840  		assertEqual(t, funcBuffer.String(),
   841  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
   842  return common.OpenAPIDefinition{
   843  Schema: spec.Schema{
   844  SchemaProps: spec.SchemaProps{
   845  Description: "Map sample tests openAPIGen.generateMapProperty method.",
   846  Type: []string{"object"},
   847  Properties: map[string]spec.Schema{
   848  "StringToArray": {
   849  SchemaProps: spec.SchemaProps{
   850  Description: "A sample String to String map",
   851  Type: []string{"object"},
   852  AdditionalProperties: &spec.SchemaOrBool{
   853  Allows: true,
   854  Schema: &spec.Schema{
   855  SchemaProps: spec.SchemaProps{
   856  Type: []string{"object"},
   857  AdditionalProperties: &spec.SchemaOrBool{
   858  Allows: true,
   859  Schema: &spec.Schema{
   860  SchemaProps: spec.SchemaProps{
   861  Default: "",
   862  Type: []string{"string"},
   863  Format: "",
   864  },
   865  },
   866  },
   867  },
   868  },
   869  },
   870  },
   871  },
   872  },
   873  Required: []string{"StringToArray"},
   874  },
   875  },
   876  }
   877  }`)
   878  	})
   879  }
   880  
   881  func TestNestedMapInt(t *testing.T) {
   882  	inputFile := `
   883  		package foo
   884  
   885  		// Map sample tests openAPIGen.generateMapProperty method.
   886  		type Blah struct {
   887  			// A sample String to String map
   888  			StringToArray map[string]map[string]int
   889  		}`
   890  
   891  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
   892  		e := packagestest.Export(t, x, []packagestest.Module{{
   893  			Name: "example.com/base/foo",
   894  			Files: map[string]interface{}{
   895  				"foo.go": inputFile,
   896  			},
   897  		}})
   898  		defer e.Cleanup()
   899  
   900  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
   901  		if callErr != nil {
   902  			t.Fatal(callErr)
   903  		}
   904  		if funcErr != nil {
   905  			t.Fatal(funcErr)
   906  		}
   907  		assertEqual(t, callBuffer.String(),
   908  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
   909  		assertEqual(t, funcBuffer.String(),
   910  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
   911  return common.OpenAPIDefinition{
   912  Schema: spec.Schema{
   913  SchemaProps: spec.SchemaProps{
   914  Description: "Map sample tests openAPIGen.generateMapProperty method.",
   915  Type: []string{"object"},
   916  Properties: map[string]spec.Schema{
   917  "StringToArray": {
   918  SchemaProps: spec.SchemaProps{
   919  Description: "A sample String to String map",
   920  Type: []string{"object"},
   921  AdditionalProperties: &spec.SchemaOrBool{
   922  Allows: true,
   923  Schema: &spec.Schema{
   924  SchemaProps: spec.SchemaProps{
   925  Type: []string{"object"},
   926  AdditionalProperties: &spec.SchemaOrBool{
   927  Allows: true,
   928  Schema: &spec.Schema{
   929  SchemaProps: spec.SchemaProps{
   930  Default: 0,
   931  Type: []string{"integer"},
   932  Format: "int32",
   933  },
   934  },
   935  },
   936  },
   937  },
   938  },
   939  },
   940  },
   941  },
   942  Required: []string{"StringToArray"},
   943  },
   944  },
   945  }
   946  }`)
   947  	})
   948  }
   949  
   950  func TestNestedMapBoolean(t *testing.T) {
   951  	inputFile := `
   952  		package foo
   953  
   954  		// Map sample tests openAPIGen.generateMapProperty method.
   955  		type Blah struct {
   956  			// A sample String to String map
   957  			StringToArray map[string]map[string]bool
   958  		}`
   959  
   960  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
   961  		e := packagestest.Export(t, x, []packagestest.Module{{
   962  			Name: "example.com/base/foo",
   963  			Files: map[string]interface{}{
   964  				"foo.go": inputFile,
   965  			},
   966  		}})
   967  		defer e.Cleanup()
   968  
   969  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
   970  		if callErr != nil {
   971  			t.Fatal(callErr)
   972  		}
   973  		if funcErr != nil {
   974  			t.Fatal(funcErr)
   975  		}
   976  		assertEqual(t, callBuffer.String(),
   977  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
   978  		assertEqual(t, funcBuffer.String(),
   979  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
   980  return common.OpenAPIDefinition{
   981  Schema: spec.Schema{
   982  SchemaProps: spec.SchemaProps{
   983  Description: "Map sample tests openAPIGen.generateMapProperty method.",
   984  Type: []string{"object"},
   985  Properties: map[string]spec.Schema{
   986  "StringToArray": {
   987  SchemaProps: spec.SchemaProps{
   988  Description: "A sample String to String map",
   989  Type: []string{"object"},
   990  AdditionalProperties: &spec.SchemaOrBool{
   991  Allows: true,
   992  Schema: &spec.Schema{
   993  SchemaProps: spec.SchemaProps{
   994  Type: []string{"object"},
   995  AdditionalProperties: &spec.SchemaOrBool{
   996  Allows: true,
   997  Schema: &spec.Schema{
   998  SchemaProps: spec.SchemaProps{
   999  Default: false,
  1000  Type: []string{"boolean"},
  1001  Format: "",
  1002  },
  1003  },
  1004  },
  1005  },
  1006  },
  1007  },
  1008  },
  1009  },
  1010  },
  1011  Required: []string{"StringToArray"},
  1012  },
  1013  },
  1014  }
  1015  }`)
  1016  	})
  1017  }
  1018  
  1019  func TestFailingSample1(t *testing.T) {
  1020  	inputFile := `
  1021  		package foo
  1022  
  1023  		// Map sample tests openAPIGen.generateMapProperty method.
  1024  		type Blah struct {
  1025  			// A sample String to String map
  1026  			StringToArray map[string]map[string]map[int]string
  1027  		}`
  1028  
  1029  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1030  		e := packagestest.Export(t, x, []packagestest.Module{{
  1031  			Name: "example.com/base/foo",
  1032  			Files: map[string]interface{}{
  1033  				"foo.go": inputFile,
  1034  			},
  1035  		}})
  1036  		defer e.Cleanup()
  1037  
  1038  		_, funcErr, _, _, _ := testOpenAPITypeWriter(t, e.Config)
  1039  		if funcErr == nil {
  1040  			t.Fatalf("An error was expected")
  1041  		}
  1042  		assertEqual(t,
  1043  			"failed to generate map property in example.com/base/foo.Blah: StringToArray: map with non-string keys are not supported by OpenAPI in map[int]string",
  1044  			funcErr.Error())
  1045  	})
  1046  }
  1047  
  1048  func TestFailingSample2(t *testing.T) {
  1049  	inputFile := `
  1050  		package foo
  1051  
  1052  		// Map sample tests openAPIGen.generateMapProperty method.
  1053  		type Blah struct {
  1054  			// A sample String to String map
  1055  			StringToArray map[int]string
  1056  		}`
  1057  
  1058  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1059  		e := packagestest.Export(t, x, []packagestest.Module{{
  1060  			Name: "example.com/base/foo",
  1061  			Files: map[string]interface{}{
  1062  				"foo.go": inputFile,
  1063  			},
  1064  		}})
  1065  		defer e.Cleanup()
  1066  
  1067  		_, funcErr, _, _, _ := testOpenAPITypeWriter(t, e.Config)
  1068  		if funcErr == nil {
  1069  			t.Fatalf("An error was expected")
  1070  		}
  1071  		assertEqual(t,
  1072  			"failed to generate map property in example.com/base/foo.Blah: StringToArray: map with non-string keys are not supported by OpenAPI in map[int]string",
  1073  			funcErr.Error())
  1074  	})
  1075  }
  1076  
  1077  func TestFailingDefaultEnforced(t *testing.T) {
  1078  	tests := []struct {
  1079  		definition    string
  1080  		expectedError string
  1081  	}{{
  1082  		definition: `
  1083  			package foo
  1084  
  1085  			type Blah struct {
  1086  				// +default=5
  1087  				Int int
  1088  			}`,
  1089  		expectedError: "failed to generate default in example.com/base/foo.Blah: Int: invalid default value (5) for non-pointer/non-omitempty. If specified, must be: 0",
  1090  	}, {
  1091  		definition: `
  1092  			package foo
  1093  
  1094  			type Blah struct {
  1095  				// +default={"foo": 5}
  1096  				Struct struct{
  1097  					foo int
  1098  				}
  1099  			}`,
  1100  		expectedError: `failed to generate default in example.com/base/foo.Blah: Struct: invalid default value (map[string]interface {}{"foo":5}) for non-pointer/non-omitempty. If specified, must be: {}`,
  1101  	}, {
  1102  		definition: `
  1103  			package foo
  1104  
  1105  			type Blah struct {
  1106  				List []Item
  1107  
  1108  			}
  1109  
  1110  			// +default="foo"
  1111  			type Item string`,
  1112  		expectedError: `failed to generate slice property in example.com/base/foo.Blah: List: invalid default value ("foo") for non-pointer/non-omitempty. If specified, must be: ""`,
  1113  	}, {
  1114  		definition: `
  1115  			package foo
  1116  
  1117  			type Blah struct {
  1118  				Map map[string]Item
  1119  
  1120  			}
  1121  
  1122  			// +default="foo"
  1123  			type Item string`,
  1124  		expectedError: `failed to generate map property in example.com/base/foo.Blah: Map: invalid default value ("foo") for non-pointer/non-omitempty. If specified, must be: ""`,
  1125  	}}
  1126  
  1127  	for i, test := range tests {
  1128  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
  1129  			packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1130  				e := packagestest.Export(t, x, []packagestest.Module{{
  1131  					Name: "example.com/base/foo",
  1132  					Files: map[string]interface{}{
  1133  						"foo.go": test.definition,
  1134  					},
  1135  				}})
  1136  				defer e.Cleanup()
  1137  
  1138  				_, funcErr, _, _, _ := testOpenAPITypeWriter(t, e.Config)
  1139  				if funcErr == nil {
  1140  					t.Fatalf("An error was expected")
  1141  				}
  1142  				assertEqual(t, test.expectedError, funcErr.Error())
  1143  			})
  1144  		})
  1145  	}
  1146  }
  1147  
  1148  func TestCustomDef(t *testing.T) {
  1149  	inputFile := `
  1150  		package foo
  1151  
  1152  		import openapi "k8s.io/kube-openapi/pkg/common"
  1153  
  1154  		type Blah struct {
  1155  		}
  1156  
  1157  		func (_ Blah) OpenAPIDefinition() openapi.OpenAPIDefinition {
  1158  			return openapi.OpenAPIDefinition{
  1159  				Schema: spec.Schema{
  1160  					SchemaProps: spec.SchemaProps{
  1161  						Type:   []string{"string"},
  1162  						Format: "date-time",
  1163  					},
  1164  				},
  1165  			}
  1166  		}`
  1167  	commonFile := `package common
  1168  
  1169  		type OpenAPIDefinition struct {}`
  1170  
  1171  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1172  		e := packagestest.Export(t, x, []packagestest.Module{{
  1173  			Name: "example.com/base/foo",
  1174  			Files: map[string]interface{}{
  1175  				"foo.go": inputFile,
  1176  			},
  1177  		}, {
  1178  			Name: "k8s.io/kube-openapi/pkg/common",
  1179  			Files: map[string]interface{}{
  1180  				"common.go": commonFile,
  1181  			},
  1182  		}})
  1183  		defer e.Cleanup()
  1184  
  1185  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  1186  		if callErr != nil {
  1187  			t.Fatal(callErr)
  1188  		}
  1189  		if funcErr != nil {
  1190  			t.Fatal(funcErr)
  1191  		}
  1192  		assertEqual(t, callBuffer.String(),
  1193  			`"example.com/base/foo.Blah": foo.Blah{}.OpenAPIDefinition(),`)
  1194  		assertEqual(t, "", funcBuffer.String())
  1195  	})
  1196  }
  1197  
  1198  func TestCustomDefV3(t *testing.T) {
  1199  	inputFile := `
  1200  		package foo
  1201  
  1202  		import openapi "k8s.io/kube-openapi/pkg/common"
  1203  
  1204  		type Blah struct {
  1205  		}
  1206  
  1207  		func (_ Blah) OpenAPIV3Definition() openapi.OpenAPIDefinition {
  1208  			return openapi.OpenAPIDefinition{
  1209  				Schema: spec.Schema{
  1210  					SchemaProps: spec.SchemaProps{
  1211  						Type:   []string{"string"},
  1212  						Format: "date-time",
  1213  					},
  1214  				},
  1215  			}
  1216  		}`
  1217  	commonFile := `package common
  1218  
  1219  		type OpenAPIDefinition struct {}`
  1220  
  1221  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1222  		e := packagestest.Export(t, x, []packagestest.Module{{
  1223  			Name: "example.com/base/foo",
  1224  			Files: map[string]interface{}{
  1225  				"foo.go": inputFile,
  1226  			},
  1227  		}, {
  1228  			Name: "k8s.io/kube-openapi/pkg/common",
  1229  			Files: map[string]interface{}{
  1230  				"common.go": commonFile,
  1231  			},
  1232  		}})
  1233  		defer e.Cleanup()
  1234  
  1235  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  1236  		if callErr != nil {
  1237  			t.Fatal(callErr)
  1238  		}
  1239  		if funcErr != nil {
  1240  			t.Fatal(funcErr)
  1241  		}
  1242  		assertEqual(t, callBuffer.String(),
  1243  			`"example.com/base/foo.Blah": foo.Blah{}.OpenAPIV3Definition(),`)
  1244  		assertEqual(t, "", funcBuffer.String())
  1245  	})
  1246  }
  1247  
  1248  func TestCustomDefV2AndV3(t *testing.T) {
  1249  	inputFile := `
  1250  		package foo
  1251  
  1252  		import openapi "k8s.io/kube-openapi/pkg/common"
  1253  
  1254  		type Blah struct {
  1255  		}
  1256  
  1257  		func (_ Blah) OpenAPIV3Definition() openapi.OpenAPIDefinition {
  1258  			return openapi.OpenAPIDefinition{
  1259  				Schema: spec.Schema{
  1260  					SchemaProps: spec.SchemaProps{
  1261  						Type:   []string{"string"},
  1262  						Format: "date-time",
  1263  					},
  1264  				},
  1265  			}
  1266  		}
  1267  
  1268  		func (_ Blah) OpenAPIDefinition() openapi.OpenAPIDefinition {
  1269  			return openapi.OpenAPIDefinition{
  1270  				Schema: spec.Schema{
  1271  					SchemaProps: spec.SchemaProps{
  1272  						Type:   []string{"string"},
  1273  						Format: "date-time",
  1274  					},
  1275  				},
  1276  			}
  1277  		}`
  1278  	commonFile := `package common
  1279  
  1280  		type OpenAPIDefinition struct {}`
  1281  
  1282  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1283  		e := packagestest.Export(t, x, []packagestest.Module{{
  1284  			Name: "example.com/base/foo",
  1285  			Files: map[string]interface{}{
  1286  				"foo.go": inputFile,
  1287  			},
  1288  		}, {
  1289  			Name: "k8s.io/kube-openapi/pkg/common",
  1290  			Files: map[string]interface{}{
  1291  				"common.go": commonFile,
  1292  			},
  1293  		}})
  1294  		defer e.Cleanup()
  1295  
  1296  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  1297  		if callErr != nil {
  1298  			t.Fatal(callErr)
  1299  		}
  1300  		if funcErr != nil {
  1301  			t.Fatal(funcErr)
  1302  		}
  1303  		assertEqual(t, callBuffer.String(),
  1304  			`"example.com/base/foo.Blah": common.EmbedOpenAPIDefinitionIntoV2Extension(foo.Blah{}.OpenAPIV3Definition(), foo.Blah{}.OpenAPIDefinition()),`)
  1305  		assertEqual(t, "", funcBuffer.String())
  1306  	})
  1307  }
  1308  
  1309  func TestCustomDefs(t *testing.T) {
  1310  	inputFile := `
  1311  		package foo
  1312  
  1313  		// Blah is a custom type
  1314  		type Blah struct {
  1315  		}
  1316  
  1317  		func (_ Blah) OpenAPISchemaType() []string { return []string{"string"} }
  1318  		func (_ Blah) OpenAPISchemaFormat() string { return "date-time" }`
  1319  
  1320  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1321  		e := packagestest.Export(t, x, []packagestest.Module{{
  1322  			Name: "example.com/base/foo",
  1323  			Files: map[string]interface{}{
  1324  				"foo.go": inputFile,
  1325  			},
  1326  		}})
  1327  		defer e.Cleanup()
  1328  
  1329  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  1330  		if callErr != nil {
  1331  			t.Fatal(callErr)
  1332  		}
  1333  		if funcErr != nil {
  1334  			t.Fatal(funcErr)
  1335  		}
  1336  		assertEqual(t, callBuffer.String(),
  1337  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
  1338  		assertEqual(t, funcBuffer.String(),
  1339  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  1340  return common.OpenAPIDefinition{
  1341  Schema: spec.Schema{
  1342  SchemaProps: spec.SchemaProps{
  1343  Description: "Blah is a custom type",
  1344  Type:foo.Blah{}.OpenAPISchemaType(),
  1345  Format:foo.Blah{}.OpenAPISchemaFormat(),
  1346  },
  1347  },
  1348  }
  1349  }`)
  1350  	})
  1351  }
  1352  
  1353  func TestCustomDefsV3(t *testing.T) {
  1354  	inputFile := `
  1355  		package foo
  1356  
  1357  		import openapi "k8s.io/kube-openapi/pkg/common"
  1358  
  1359  		// Blah is a custom type
  1360  		type Blah struct {
  1361  		}
  1362  
  1363  		func (_ Blah) OpenAPIV3Definition() openapi.OpenAPIDefinition {
  1364  			return openapi.OpenAPIDefinition{
  1365  				Schema: spec.Schema{
  1366  					SchemaProps: spec.SchemaProps{
  1367  						Type:   []string{"string"},
  1368  						Format: "date-time",
  1369  					},
  1370  				},
  1371  			}
  1372  		}
  1373  
  1374  		func (_ Blah) OpenAPISchemaType() []string { return []string{"string"} }
  1375  		func (_ Blah) OpenAPISchemaFormat() string { return "date-time" }`
  1376  	commonFile := `package common
  1377  
  1378  		type OpenAPIDefinition struct {}`
  1379  
  1380  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1381  		e := packagestest.Export(t, x, []packagestest.Module{{
  1382  			Name: "example.com/base/foo",
  1383  			Files: map[string]interface{}{
  1384  				"foo.go": inputFile,
  1385  			},
  1386  		}, {
  1387  			Name: "k8s.io/kube-openapi/pkg/common",
  1388  			Files: map[string]interface{}{
  1389  				"common.go": commonFile,
  1390  			},
  1391  		}})
  1392  		defer e.Cleanup()
  1393  
  1394  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  1395  		if callErr != nil {
  1396  			t.Fatal(callErr)
  1397  		}
  1398  		if funcErr != nil {
  1399  			t.Fatal(funcErr)
  1400  		}
  1401  		assertEqual(t, callBuffer.String(),
  1402  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
  1403  		assertEqual(t, funcBuffer.String(),
  1404  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  1405  return common.EmbedOpenAPIDefinitionIntoV2Extension(foo.Blah{}.OpenAPIV3Definition(), common.OpenAPIDefinition{
  1406  Schema: spec.Schema{
  1407  SchemaProps: spec.SchemaProps{
  1408  Description: "Blah is a custom type",
  1409  Type:foo.Blah{}.OpenAPISchemaType(),
  1410  Format:foo.Blah{}.OpenAPISchemaFormat(),
  1411  },
  1412  },
  1413  })
  1414  }`)
  1415  	})
  1416  }
  1417  
  1418  func TestV3OneOfTypes(t *testing.T) {
  1419  	inputFile := `
  1420  		package foo
  1421  
  1422  		// Blah is a custom type
  1423  		type Blah struct {
  1424  		}
  1425  
  1426  		func (_ Blah) OpenAPISchemaType() []string { return []string{"string"} }
  1427  		func (_ Blah) OpenAPISchemaFormat() string { return "date-time" }
  1428  		func (_ Blah) OpenAPIV3OneOfTypes() []string { return []string{"string", "number"} }`
  1429  
  1430  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1431  		e := packagestest.Export(t, x, []packagestest.Module{{
  1432  			Name: "example.com/base/foo",
  1433  			Files: map[string]interface{}{
  1434  				"foo.go": inputFile,
  1435  			},
  1436  		}})
  1437  		defer e.Cleanup()
  1438  
  1439  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  1440  		if callErr != nil {
  1441  			t.Fatal(callErr)
  1442  		}
  1443  		if funcErr != nil {
  1444  			t.Fatal(funcErr)
  1445  		}
  1446  		assertEqual(t, callBuffer.String(),
  1447  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
  1448  		assertEqual(t, funcBuffer.String(),
  1449  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  1450  return common.EmbedOpenAPIDefinitionIntoV2Extension(common.OpenAPIDefinition{
  1451  Schema: spec.Schema{
  1452  SchemaProps: spec.SchemaProps{
  1453  Description: "Blah is a custom type",
  1454  OneOf:common.GenerateOpenAPIV3OneOfSchema(foo.Blah{}.OpenAPIV3OneOfTypes()),
  1455  Format:foo.Blah{}.OpenAPISchemaFormat(),
  1456  },
  1457  },
  1458  },common.OpenAPIDefinition{
  1459  Schema: spec.Schema{
  1460  SchemaProps: spec.SchemaProps{
  1461  Description: "Blah is a custom type",
  1462  Type:foo.Blah{}.OpenAPISchemaType(),
  1463  Format:foo.Blah{}.OpenAPISchemaFormat(),
  1464  },
  1465  },
  1466  })
  1467  }`)
  1468  	})
  1469  }
  1470  
  1471  func TestPointer(t *testing.T) {
  1472  	inputFile := `
  1473  		package foo
  1474  
  1475  		// PointerSample demonstrate pointer's properties
  1476  		type Blah struct {
  1477  			// A string pointer
  1478  			StringPointer *string
  1479  			// A struct pointer
  1480  			StructPointer *Blah
  1481  			// A slice pointer
  1482  			SlicePointer *[]string
  1483  			// A map pointer
  1484  			MapPointer *map[string]string
  1485  		}`
  1486  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1487  		e := packagestest.Export(t, x, []packagestest.Module{{
  1488  			Name: "example.com/base/foo",
  1489  			Files: map[string]interface{}{
  1490  				"foo.go": inputFile,
  1491  			},
  1492  		}})
  1493  		defer e.Cleanup()
  1494  
  1495  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  1496  		if callErr != nil {
  1497  			t.Fatal(callErr)
  1498  		}
  1499  		if funcErr != nil {
  1500  			t.Fatal(funcErr)
  1501  		}
  1502  		assertEqual(t, callBuffer.String(),
  1503  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
  1504  		assertEqual(t, funcBuffer.String(),
  1505  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  1506  return common.OpenAPIDefinition{
  1507  Schema: spec.Schema{
  1508  SchemaProps: spec.SchemaProps{
  1509  Description: "PointerSample demonstrate pointer's properties",
  1510  Type: []string{"object"},
  1511  Properties: map[string]spec.Schema{
  1512  "StringPointer": {
  1513  SchemaProps: spec.SchemaProps{
  1514  Description: "A string pointer",
  1515  Type: []string{"string"},
  1516  Format: "",
  1517  },
  1518  },
  1519  "StructPointer": {
  1520  SchemaProps: spec.SchemaProps{
  1521  Description: "A struct pointer",
  1522  Ref: ref("example.com/base/foo.Blah"),
  1523  },
  1524  },
  1525  "SlicePointer": {
  1526  SchemaProps: spec.SchemaProps{
  1527  Description: "A slice pointer",
  1528  Type: []string{"array"},
  1529  Items: &spec.SchemaOrArray{
  1530  Schema: &spec.Schema{
  1531  SchemaProps: spec.SchemaProps{
  1532  Default: "",
  1533  Type: []string{"string"},
  1534  Format: "",
  1535  },
  1536  },
  1537  },
  1538  },
  1539  },
  1540  "MapPointer": {
  1541  SchemaProps: spec.SchemaProps{
  1542  Description: "A map pointer",
  1543  Type: []string{"object"},
  1544  AdditionalProperties: &spec.SchemaOrBool{
  1545  Allows: true,
  1546  Schema: &spec.Schema{
  1547  SchemaProps: spec.SchemaProps{
  1548  Default: "",
  1549  Type: []string{"string"},
  1550  Format: "",
  1551  },
  1552  },
  1553  },
  1554  },
  1555  },
  1556  },
  1557  Required: []string{"StringPointer","StructPointer","SlicePointer","MapPointer"},
  1558  },
  1559  },
  1560  Dependencies: []string{
  1561  "example.com/base/foo.Blah",},
  1562  }
  1563  }`)
  1564  	})
  1565  }
  1566  
  1567  func TestNestedLists(t *testing.T) {
  1568  	inputFile := `
  1569  		package foo
  1570  
  1571  		// Blah is a test.
  1572  		// +k8s:openapi-gen=true
  1573  		// +k8s:openapi-gen=x-kubernetes-type-tag:type_test
  1574  		type Blah struct {
  1575  			// Nested list
  1576  			NestedList [][]int64
  1577  		}`
  1578  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1579  		e := packagestest.Export(t, x, []packagestest.Module{{
  1580  			Name: "example.com/base/foo",
  1581  			Files: map[string]interface{}{
  1582  				"foo.go": inputFile,
  1583  			},
  1584  		}})
  1585  		defer e.Cleanup()
  1586  
  1587  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  1588  		if callErr != nil {
  1589  			t.Fatal(callErr)
  1590  		}
  1591  		if funcErr != nil {
  1592  			t.Fatal(funcErr)
  1593  		}
  1594  		assertEqual(t, callBuffer.String(),
  1595  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
  1596  		assertEqual(t, funcBuffer.String(),
  1597  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  1598  return common.OpenAPIDefinition{
  1599  Schema: spec.Schema{
  1600  SchemaProps: spec.SchemaProps{
  1601  Description: "Blah is a test.",
  1602  Type: []string{"object"},
  1603  Properties: map[string]spec.Schema{
  1604  "NestedList": {
  1605  SchemaProps: spec.SchemaProps{
  1606  Description: "Nested list",
  1607  Type: []string{"array"},
  1608  Items: &spec.SchemaOrArray{
  1609  Schema: &spec.Schema{
  1610  SchemaProps: spec.SchemaProps{
  1611  Type: []string{"array"},
  1612  Items: &spec.SchemaOrArray{
  1613  Schema: &spec.Schema{
  1614  SchemaProps: spec.SchemaProps{
  1615  Default: 0,
  1616  Type: []string{"integer"},
  1617  Format: "int64",
  1618  },
  1619  },
  1620  },
  1621  },
  1622  },
  1623  },
  1624  },
  1625  },
  1626  },
  1627  Required: []string{"NestedList"},
  1628  },
  1629  VendorExtensible: spec.VendorExtensible{
  1630  Extensions: spec.Extensions{
  1631  "x-kubernetes-type-tag": "type_test",
  1632  },
  1633  },
  1634  },
  1635  }
  1636  }`)
  1637  	})
  1638  }
  1639  
  1640  func TestNestListOfMaps(t *testing.T) {
  1641  	inputFile := `
  1642  		package foo
  1643  
  1644  		// Blah is a test.
  1645  		// +k8s:openapi-gen=true
  1646  		// +k8s:openapi-gen=x-kubernetes-type-tag:type_test
  1647  		type Blah struct {
  1648  			// Nested list of maps
  1649  			NestedListOfMaps [][]map[string]string
  1650  		}`
  1651  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1652  		e := packagestest.Export(t, x, []packagestest.Module{{
  1653  			Name: "example.com/base/foo",
  1654  			Files: map[string]interface{}{
  1655  				"foo.go": inputFile,
  1656  			},
  1657  		}})
  1658  		defer e.Cleanup()
  1659  
  1660  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  1661  		if callErr != nil {
  1662  			t.Fatal(callErr)
  1663  		}
  1664  		if funcErr != nil {
  1665  			t.Fatal(funcErr)
  1666  		}
  1667  		assertEqual(t, callBuffer.String(),
  1668  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
  1669  		assertEqual(t, funcBuffer.String(),
  1670  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  1671  return common.OpenAPIDefinition{
  1672  Schema: spec.Schema{
  1673  SchemaProps: spec.SchemaProps{
  1674  Description: "Blah is a test.",
  1675  Type: []string{"object"},
  1676  Properties: map[string]spec.Schema{
  1677  "NestedListOfMaps": {
  1678  SchemaProps: spec.SchemaProps{
  1679  Description: "Nested list of maps",
  1680  Type: []string{"array"},
  1681  Items: &spec.SchemaOrArray{
  1682  Schema: &spec.Schema{
  1683  SchemaProps: spec.SchemaProps{
  1684  Type: []string{"array"},
  1685  Items: &spec.SchemaOrArray{
  1686  Schema: &spec.Schema{
  1687  SchemaProps: spec.SchemaProps{
  1688  Type: []string{"object"},
  1689  AdditionalProperties: &spec.SchemaOrBool{
  1690  Allows: true,
  1691  Schema: &spec.Schema{
  1692  SchemaProps: spec.SchemaProps{
  1693  Default: "",
  1694  Type: []string{"string"},
  1695  Format: "",
  1696  },
  1697  },
  1698  },
  1699  },
  1700  },
  1701  },
  1702  },
  1703  },
  1704  },
  1705  },
  1706  },
  1707  },
  1708  Required: []string{"NestedListOfMaps"},
  1709  },
  1710  VendorExtensible: spec.VendorExtensible{
  1711  Extensions: spec.Extensions{
  1712  "x-kubernetes-type-tag": "type_test",
  1713  },
  1714  },
  1715  },
  1716  }
  1717  }`)
  1718  	})
  1719  }
  1720  
  1721  func TestExtensions(t *testing.T) {
  1722  	inputFile := `
  1723  		package foo
  1724  
  1725  		// Blah is a test.
  1726  		// +k8s:openapi-gen=true
  1727  		// +k8s:openapi-gen=x-kubernetes-type-tag:type_test
  1728  		type Blah struct {
  1729  			// a member with a list type with two map keys
  1730  			// +listType=map
  1731  			// +listMapKey=port
  1732  			// +listMapKey=protocol
  1733  			WithListField []string
  1734  
  1735  			// another member with a list type with one map key
  1736  			// +listType=map
  1737  			// +listMapKey=port
  1738  			WithListField2 []string
  1739  		}`
  1740  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1741  		e := packagestest.Export(t, x, []packagestest.Module{{
  1742  			Name: "example.com/base/foo",
  1743  			Files: map[string]interface{}{
  1744  				"foo.go": inputFile,
  1745  			},
  1746  		}})
  1747  		defer e.Cleanup()
  1748  
  1749  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  1750  		if callErr != nil {
  1751  			t.Fatal(callErr)
  1752  		}
  1753  		if funcErr != nil {
  1754  			t.Fatal(funcErr)
  1755  		}
  1756  		assertEqual(t, callBuffer.String(),
  1757  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
  1758  		assertEqual(t, funcBuffer.String(),
  1759  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  1760  return common.OpenAPIDefinition{
  1761  Schema: spec.Schema{
  1762  SchemaProps: spec.SchemaProps{
  1763  Description: "Blah is a test.",
  1764  Type: []string{"object"},
  1765  Properties: map[string]spec.Schema{
  1766  "WithListField": {
  1767  VendorExtensible: spec.VendorExtensible{
  1768  Extensions: spec.Extensions{
  1769  "x-kubernetes-list-map-keys": []interface{}{
  1770  "port",
  1771  "protocol",
  1772  },
  1773  "x-kubernetes-list-type": "map",
  1774  },
  1775  },
  1776  SchemaProps: spec.SchemaProps{
  1777  Description: "a member with a list type with two map keys",
  1778  Type: []string{"array"},
  1779  Items: &spec.SchemaOrArray{
  1780  Schema: &spec.Schema{
  1781  SchemaProps: spec.SchemaProps{
  1782  Default: "",
  1783  Type: []string{"string"},
  1784  Format: "",
  1785  },
  1786  },
  1787  },
  1788  },
  1789  },
  1790  "WithListField2": {
  1791  VendorExtensible: spec.VendorExtensible{
  1792  Extensions: spec.Extensions{
  1793  "x-kubernetes-list-map-keys": []interface{}{
  1794  "port",
  1795  },
  1796  "x-kubernetes-list-type": "map",
  1797  },
  1798  },
  1799  SchemaProps: spec.SchemaProps{
  1800  Description: "another member with a list type with one map key",
  1801  Type: []string{"array"},
  1802  Items: &spec.SchemaOrArray{
  1803  Schema: &spec.Schema{
  1804  SchemaProps: spec.SchemaProps{
  1805  Default: "",
  1806  Type: []string{"string"},
  1807  Format: "",
  1808  },
  1809  },
  1810  },
  1811  },
  1812  },
  1813  },
  1814  Required: []string{"WithListField","WithListField2"},
  1815  },
  1816  VendorExtensible: spec.VendorExtensible{
  1817  Extensions: spec.Extensions{
  1818  "x-kubernetes-type-tag": "type_test",
  1819  },
  1820  },
  1821  },
  1822  }
  1823  }`)
  1824  	})
  1825  }
  1826  
  1827  func TestUnion(t *testing.T) {
  1828  	inputFile := `
  1829  		package foo
  1830  
  1831  		// Blah is a test.
  1832  		// +k8s:openapi-gen=true
  1833  		// +k8s:openapi-gen=x-kubernetes-type-tag:type_test
  1834  		// +union
  1835  		type Blah struct {
  1836  			// +unionDiscriminator
  1837  			Discriminator *string ` + "`" + `json:"discriminator"` + "`" + `
  1838  				// +optional
  1839  				Numeric int ` + "`" + `json:"numeric"` + "`" + `
  1840  				// +optional
  1841  				String string ` + "`" + `json:"string"` + "`" + `
  1842  				// +optional
  1843  				Float float64 ` + "`" + `json:"float"` + "`" + `
  1844  		}`
  1845  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1846  		e := packagestest.Export(t, x, []packagestest.Module{{
  1847  			Name: "example.com/base/foo",
  1848  			Files: map[string]interface{}{
  1849  				"foo.go": inputFile,
  1850  			},
  1851  		}})
  1852  		defer e.Cleanup()
  1853  
  1854  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  1855  		if callErr != nil {
  1856  			t.Fatal(callErr)
  1857  		}
  1858  		if funcErr != nil {
  1859  			t.Fatal(funcErr)
  1860  		}
  1861  		assertEqual(t, callBuffer.String(),
  1862  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
  1863  		assertEqual(t, funcBuffer.String(),
  1864  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  1865  return common.OpenAPIDefinition{
  1866  Schema: spec.Schema{
  1867  SchemaProps: spec.SchemaProps{
  1868  Description: "Blah is a test.",
  1869  Type: []string{"object"},
  1870  Properties: map[string]spec.Schema{
  1871  "discriminator": {
  1872  SchemaProps: spec.SchemaProps{
  1873  Type: []string{"string"},
  1874  Format: "",
  1875  },
  1876  },
  1877  "numeric": {
  1878  SchemaProps: spec.SchemaProps{
  1879  Default: 0,
  1880  Type: []string{"integer"},
  1881  Format: "int32",
  1882  },
  1883  },
  1884  "string": {
  1885  SchemaProps: spec.SchemaProps{
  1886  Default: "",
  1887  Type: []string{"string"},
  1888  Format: "",
  1889  },
  1890  },
  1891  "float": {
  1892  SchemaProps: spec.SchemaProps{
  1893  Default: 0,
  1894  Type: []string{"number"},
  1895  Format: "double",
  1896  },
  1897  },
  1898  },
  1899  Required: []string{"discriminator"},
  1900  },
  1901  VendorExtensible: spec.VendorExtensible{
  1902  Extensions: spec.Extensions{
  1903  "x-kubernetes-type-tag": "type_test",
  1904  "x-kubernetes-unions": []interface{}{
  1905  map[string]interface{}{
  1906  "discriminator": "discriminator",
  1907  "fields-to-discriminateBy": map[string]interface{}{
  1908  "float": "Float",
  1909  "numeric": "Numeric",
  1910  "string": "String",
  1911  },
  1912  },
  1913  },
  1914  },
  1915  },
  1916  },
  1917  }
  1918  }`)
  1919  	})
  1920  }
  1921  
  1922  func TestEnumAlias(t *testing.T) {
  1923  	inputFile := `
  1924  		package foo
  1925  
  1926  		import "example.com/base/bar"
  1927  
  1928  		// EnumType is the enumType.
  1929  		// +enum
  1930  		type EnumType = bar.EnumType
  1931  
  1932  		// EnumA is a.
  1933  		const EnumA EnumType = bar.EnumA
  1934  		// EnumB is b.
  1935  		const EnumB EnumType = bar.EnumB
  1936  
  1937  		// Blah is a test.
  1938  		// +k8s:openapi-gen=true
  1939  		type Blah struct {
  1940  			// Value is the value.
  1941  			Value EnumType
  1942  		}`
  1943  	otherFile := `
  1944  		package bar
  1945  
  1946  		// EnumType is the enumType.
  1947  		// +enum
  1948  		type EnumType string
  1949  
  1950  		// EnumA is a.
  1951  		const EnumA EnumType = "a"
  1952  		// EnumB is b.
  1953  		const EnumB EnumType = "b"`
  1954  
  1955  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  1956  		e := packagestest.Export(t, x, []packagestest.Module{{
  1957  			Name: "example.com/base/foo",
  1958  			Files: map[string]interface{}{
  1959  				"foo.go": inputFile,
  1960  			},
  1961  		}, {
  1962  			Name: "example.com/base/bar",
  1963  			Files: map[string]interface{}{
  1964  				"bar.go": otherFile,
  1965  			},
  1966  		}})
  1967  		defer e.Cleanup()
  1968  
  1969  		callErr, funcErr, _, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  1970  		if callErr != nil {
  1971  			t.Fatal(callErr)
  1972  		}
  1973  		if funcErr != nil {
  1974  			t.Fatal(funcErr)
  1975  		}
  1976  		assertEqual(t, funcBuffer.String(),
  1977  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  1978  return common.OpenAPIDefinition{
  1979  Schema: spec.Schema{
  1980  SchemaProps: spec.SchemaProps{
  1981  Description: "Blah is a test.",
  1982  Type: []string{"object"},
  1983  Properties: map[string]spec.Schema{
  1984  "Value": {
  1985  SchemaProps: spec.SchemaProps{`+"\n"+
  1986  				"Description: \"Value is the value.\\n\\nPossible enum values:\\n - `\\\"a\\\"` is a.\\n - `\\\"b\\\"` is b.\","+`
  1987  Default: "",
  1988  Type: []string{"string"},
  1989  Format: "",
  1990  Enum: []interface{}{"a", "b"},
  1991  },
  1992  },
  1993  },
  1994  Required: []string{"Value"},
  1995  },
  1996  },
  1997  }
  1998  }`)
  1999  	})
  2000  }
  2001  
  2002  func TestEnum(t *testing.T) {
  2003  	inputFile := `
  2004  		package foo
  2005  
  2006  		// EnumType is the enumType.
  2007  		// +enum
  2008  		type EnumType string
  2009  
  2010  		// EnumA is a.
  2011  		const EnumA EnumType = "a"
  2012  		// EnumB is b.
  2013  		const EnumB EnumType = "b"
  2014  
  2015  		// Blah is a test.
  2016  		// +k8s:openapi-gen=true
  2017  		// +k8s:openapi-gen=x-kubernetes-type-tag:type_test
  2018  		type Blah struct {
  2019  		  // Value is the value.
  2020  			Value EnumType
  2021  			NoCommentEnum EnumType
  2022  		  // +optional
  2023  			OptionalEnum *EnumType
  2024  			List []EnumType
  2025  			Map map[string]EnumType
  2026  		}`
  2027  
  2028  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  2029  		e := packagestest.Export(t, x, []packagestest.Module{{
  2030  			Name: "example.com/base/foo",
  2031  			Files: map[string]interface{}{
  2032  				"foo.go": inputFile,
  2033  			},
  2034  		}})
  2035  		defer e.Cleanup()
  2036  
  2037  		callErr, funcErr, _, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  2038  		if callErr != nil {
  2039  			t.Fatal(callErr)
  2040  		}
  2041  		if funcErr != nil {
  2042  			t.Fatal(funcErr)
  2043  		}
  2044  		assertEqual(t, funcBuffer.String(),
  2045  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  2046  return common.OpenAPIDefinition{
  2047  Schema: spec.Schema{
  2048  SchemaProps: spec.SchemaProps{
  2049  Description: "Blah is a test.",
  2050  Type: []string{"object"},
  2051  Properties: map[string]spec.Schema{
  2052  "Value": {
  2053  SchemaProps: spec.SchemaProps{`+"\n"+
  2054  				"Description: \"Value is the value.\\n\\nPossible enum values:\\n - `\\\"a\\\"` is a.\\n - `\\\"b\\\"` is b.\","+`
  2055  Default: "",
  2056  Type: []string{"string"},
  2057  Format: "",
  2058  Enum: []interface{}{"a", "b"},
  2059  },
  2060  },
  2061  "NoCommentEnum": {
  2062  SchemaProps: spec.SchemaProps{`+"\n"+
  2063  				"Description: \"Possible enum values:\\n - `\\\"a\\\"` is a.\\n - `\\\"b\\\"` is b.\","+`
  2064  Default: "",
  2065  Type: []string{"string"},
  2066  Format: "",
  2067  Enum: []interface{}{"a", "b"},
  2068  },
  2069  },
  2070  "OptionalEnum": {
  2071  SchemaProps: spec.SchemaProps{`+"\n"+
  2072  				"Description: \"Possible enum values:\\n - `\\\"a\\\"` is a.\\n - `\\\"b\\\"` is b.\","+`
  2073  Type: []string{"string"},
  2074  Format: "",
  2075  Enum: []interface{}{"a", "b"},
  2076  },
  2077  },
  2078  "List": {
  2079  SchemaProps: spec.SchemaProps{
  2080  Type: []string{"array"},
  2081  Items: &spec.SchemaOrArray{
  2082  Schema: &spec.Schema{
  2083  SchemaProps: spec.SchemaProps{
  2084  Default: "",
  2085  Type: []string{"string"},
  2086  Format: "",
  2087  Enum: []interface{}{"a", "b"},
  2088  },
  2089  },
  2090  },
  2091  },
  2092  },
  2093  "Map": {
  2094  SchemaProps: spec.SchemaProps{
  2095  Type: []string{"object"},
  2096  AdditionalProperties: &spec.SchemaOrBool{
  2097  Allows: true,
  2098  Schema: &spec.Schema{
  2099  SchemaProps: spec.SchemaProps{
  2100  Default: "",
  2101  Type: []string{"string"},
  2102  Format: "",
  2103  Enum: []interface{}{"a", "b"},
  2104  },
  2105  },
  2106  },
  2107  },
  2108  },
  2109  },
  2110  Required: []string{"Value","NoCommentEnum","List","Map"},
  2111  },
  2112  VendorExtensible: spec.VendorExtensible{
  2113  Extensions: spec.Extensions{
  2114  "x-kubernetes-type-tag": "type_test",
  2115  },
  2116  },
  2117  },
  2118  }
  2119  }`)
  2120  	})
  2121  }
  2122  
  2123  func TestSymbolReference(t *testing.T) {
  2124  	inputFile := `
  2125  		package foo
  2126  
  2127  		// +k8s:openapi-gen=true
  2128  		type Blah struct {
  2129  			// +default="A Default Value"
  2130  			// +optional
  2131  			Value *string
  2132  
  2133  			// User constant local to the output package fully qualified
  2134  			// +default=ref(example.com/base/output.MyConst)
  2135  			// +optional
  2136  			FullyQualifiedOutputValue *string
  2137  
  2138  			// Local to types but not to output
  2139  			// +default=ref(MyConst)
  2140  			// +optional
  2141  			LocalValue *string
  2142  
  2143  			// +default=ref(example.com/base/foo.MyConst)
  2144  			// +optional
  2145  			FullyQualifiedLocalValue *string
  2146  
  2147  			// +default=ref(k8s.io/api/v1.TerminationPathDefault)
  2148  			// +optional
  2149  			FullyQualifiedExternalValue *string
  2150  		}`
  2151  
  2152  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  2153  		e := packagestest.Export(t, x, []packagestest.Module{{
  2154  			Name: "example.com/base/foo",
  2155  			Files: map[string]interface{}{
  2156  				"foo.go": inputFile,
  2157  			},
  2158  		}})
  2159  		defer e.Cleanup()
  2160  
  2161  		callErr, funcErr, _, funcBuffer, imports := testOpenAPITypeWriter(t, e.Config)
  2162  		if funcErr != nil {
  2163  			t.Fatalf("Unexpected funcErr: %v", funcErr)
  2164  		}
  2165  		if callErr != nil {
  2166  			t.Fatalf("Unexpected callErr: %v", callErr)
  2167  		}
  2168  		expImports := []string{
  2169  			`foo "example.com/base/foo"`,
  2170  			`v1 "k8s.io/api/v1"`,
  2171  			`common "k8s.io/kube-openapi/pkg/common"`,
  2172  			`spec "k8s.io/kube-openapi/pkg/validation/spec"`,
  2173  		}
  2174  		if !cmp.Equal(imports, expImports) {
  2175  			t.Errorf("wrong imports:\n%s", cmp.Diff(expImports, imports))
  2176  		}
  2177  
  2178  		if formatted, err := format.Source(funcBuffer.Bytes()); err != nil {
  2179  			t.Fatal(err)
  2180  		} else {
  2181  			assertEqual(t, string(formatted), `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  2182  	return common.OpenAPIDefinition{
  2183  		Schema: spec.Schema{
  2184  			SchemaProps: spec.SchemaProps{
  2185  				Type: []string{"object"},
  2186  				Properties: map[string]spec.Schema{
  2187  					"Value": {
  2188  						SchemaProps: spec.SchemaProps{
  2189  							Default: "A Default Value",
  2190  							Type:    []string{"string"},
  2191  							Format:  "",
  2192  						},
  2193  					},
  2194  					"FullyQualifiedOutputValue": {
  2195  						SchemaProps: spec.SchemaProps{
  2196  							Description: "User constant local to the output package fully qualified",
  2197  							Default:     MyConst,
  2198  							Type:        []string{"string"},
  2199  							Format:      "",
  2200  						},
  2201  					},
  2202  					"LocalValue": {
  2203  						SchemaProps: spec.SchemaProps{
  2204  							Description: "Local to types but not to output",
  2205  							Default:     foo.MyConst,
  2206  							Type:        []string{"string"},
  2207  							Format:      "",
  2208  						},
  2209  					},
  2210  					"FullyQualifiedLocalValue": {
  2211  						SchemaProps: spec.SchemaProps{
  2212  							Default: foo.MyConst,
  2213  							Type:    []string{"string"},
  2214  							Format:  "",
  2215  						},
  2216  					},
  2217  					"FullyQualifiedExternalValue": {
  2218  						SchemaProps: spec.SchemaProps{
  2219  							Default: v1.TerminationPathDefault,
  2220  							Type:    []string{"string"},
  2221  							Format:  "",
  2222  						},
  2223  					},
  2224  				},
  2225  			},
  2226  		},
  2227  	}
  2228  }`)
  2229  		}
  2230  	})
  2231  }
  2232  
  2233  // Show that types with unmarshalJSON in their hierarchy do not have struct
  2234  // defaults enforced, and that aliases and embededd types are respected
  2235  func TestMustEnforceDefaultStruct(t *testing.T) {
  2236  	inputFile := `
  2237  		package foo
  2238  
  2239  		type Time struct {
  2240  			value interface{}
  2241  		}
  2242  
  2243  
  2244  		type TimeWithoutUnmarshal struct {
  2245  			value interface{}
  2246  		}
  2247  
  2248  		func (_ TimeWithoutUnmarshal) OpenAPISchemaType() []string { return []string{"string"} }
  2249  		func (_ TimeWithoutUnmarshal) OpenAPISchemaFormat() string { return "date-time" }
  2250  
  2251  		func (_ Time) UnmarshalJSON([]byte) error {
  2252  			return nil
  2253  		}
  2254  
  2255  
  2256  		func (_ Time) OpenAPISchemaType() []string { return []string{"string"} }
  2257  		func (_ Time) OpenAPISchemaFormat() string { return "date-time" }
  2258  
  2259  		// Time with UnmarshalJSON defined on pointer instead of struct
  2260  		type MicroTime struct {
  2261  			value interface{}
  2262  		}
  2263  
  2264  		func (t *MicroTime) UnmarshalJSON([]byte) error {
  2265  			return nil
  2266  		}
  2267  
  2268  		func (_ MicroTime) OpenAPISchemaType() []string { return []string{"string"} }
  2269  		func (_ MicroTime) OpenAPISchemaFormat() string { return "date-time" }
  2270  
  2271  		type Int64 int64
  2272  
  2273  		type Duration struct {
  2274  			Int64
  2275  		}
  2276  
  2277  		func (_ Duration) OpenAPISchemaType() []string { return []string{"string"} }
  2278  		func (_ Duration) OpenAPISchemaFormat() string { return "" }
  2279  
  2280  		type NothingSpecial struct {
  2281  			Field string
  2282  		}
  2283  
  2284  		// +k8s:openapi-gen=true
  2285  		type Blah struct {
  2286  			Embedded Duration
  2287  			PointerUnmarshal MicroTime
  2288  			StructUnmarshal Time
  2289  			NoUnmarshal TimeWithoutUnmarshal
  2290  			Regular NothingSpecial
  2291  		}`
  2292  
  2293  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  2294  		e := packagestest.Export(t, x, []packagestest.Module{{
  2295  			Name: "example.com/base/foo",
  2296  			Files: map[string]interface{}{
  2297  				"foo.go": inputFile,
  2298  			},
  2299  		}})
  2300  		defer e.Cleanup()
  2301  
  2302  		callErr, funcErr, _, funcBuffer, imports := testOpenAPITypeWriter(t, e.Config)
  2303  		if funcErr != nil {
  2304  			t.Fatalf("Unexpected funcErr: %v", funcErr)
  2305  		}
  2306  		if callErr != nil {
  2307  			t.Fatalf("Unexpected callErr: %v", callErr)
  2308  		}
  2309  		expImports := []string{
  2310  			`foo "example.com/base/foo"`,
  2311  			`common "k8s.io/kube-openapi/pkg/common"`,
  2312  			`spec "k8s.io/kube-openapi/pkg/validation/spec"`,
  2313  		}
  2314  		if !cmp.Equal(imports, expImports) {
  2315  			t.Errorf("wrong imports:\n%s", cmp.Diff(expImports, imports))
  2316  		}
  2317  
  2318  		if formatted, err := format.Source(funcBuffer.Bytes()); err != nil {
  2319  			t.Fatal(err)
  2320  		} else {
  2321  			assertEqual(t, string(formatted), `func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  2322  	return common.OpenAPIDefinition{
  2323  		Schema: spec.Schema{
  2324  			SchemaProps: spec.SchemaProps{
  2325  				Type: []string{"object"},
  2326  				Properties: map[string]spec.Schema{
  2327  					"Embedded": {
  2328  						SchemaProps: spec.SchemaProps{
  2329  							Default: 0,
  2330  							Ref:     ref("example.com/base/foo.Duration"),
  2331  						},
  2332  					},
  2333  					"PointerUnmarshal": {
  2334  						SchemaProps: spec.SchemaProps{
  2335  							Ref: ref("example.com/base/foo.MicroTime"),
  2336  						},
  2337  					},
  2338  					"StructUnmarshal": {
  2339  						SchemaProps: spec.SchemaProps{
  2340  							Ref: ref("example.com/base/foo.Time"),
  2341  						},
  2342  					},
  2343  					"NoUnmarshal": {
  2344  						SchemaProps: spec.SchemaProps{
  2345  							Default: map[string]interface{}{},
  2346  							Ref:     ref("example.com/base/foo.TimeWithoutUnmarshal"),
  2347  						},
  2348  					},
  2349  					"Regular": {
  2350  						SchemaProps: spec.SchemaProps{
  2351  							Default: map[string]interface{}{},
  2352  							Ref:     ref("example.com/base/foo.NothingSpecial"),
  2353  						},
  2354  					},
  2355  				},
  2356  				Required: []string{"Embedded", "PointerUnmarshal", "StructUnmarshal", "NoUnmarshal", "Regular"},
  2357  			},
  2358  		},
  2359  		Dependencies: []string{
  2360  			"example.com/base/foo.Duration", "example.com/base/foo.MicroTime", "example.com/base/foo.NothingSpecial", "example.com/base/foo.Time", "example.com/base/foo.TimeWithoutUnmarshal"},
  2361  	}
  2362  }`)
  2363  		}
  2364  	})
  2365  }
  2366  
  2367  func TestMarkerComments(t *testing.T) {
  2368  	inputFile := `
  2369  		package foo
  2370  
  2371  		// +k8s:openapi-gen=true
  2372  		// +k8s:validation:maxProperties=10
  2373  		// +k8s:validation:minProperties=1
  2374  		type Blah struct {
  2375  
  2376  			// Integer with min and max values
  2377  			// +k8s:validation:minimum=0
  2378  			// +k8s:validation:maximum=10
  2379  			// +k8s:validation:exclusiveMinimum
  2380  			// +k8s:validation:exclusiveMaximum
  2381  			IntValue int
  2382  
  2383  			// String with min and max lengths
  2384  			// +k8s:validation:minLength=1
  2385  			// +k8s:validation:maxLength=10
  2386  			// +k8s:validation:pattern="^foo$[0-9]+"
  2387  			StringValue string
  2388  
  2389  			// +k8s:validation:maxitems=10
  2390  			// +k8s:validation:minItems=1
  2391  			// +k8s:validation:uniqueItems
  2392  			ArrayValue []string
  2393  
  2394  			// +k8s:validation:maxProperties=10
  2395  			// +k8s:validation:minProperties=1
  2396  			ObjValue map[string]interface{}
  2397  		}`
  2398  
  2399  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  2400  		e := packagestest.Export(t, x, []packagestest.Module{{
  2401  			Name: "example.com/base/foo",
  2402  			Files: map[string]interface{}{
  2403  				"foo.go": inputFile,
  2404  			},
  2405  		}})
  2406  		defer e.Cleanup()
  2407  
  2408  		callErr, funcErr, _, funcBuffer, imports := testOpenAPITypeWriter(t, e.Config)
  2409  		if funcErr != nil {
  2410  			t.Fatalf("Unexpected funcErr: %v", funcErr)
  2411  		}
  2412  		if callErr != nil {
  2413  			t.Fatalf("Unexpected callErr: %v", callErr)
  2414  		}
  2415  		expImports := []string{
  2416  			`foo "example.com/base/foo"`,
  2417  			`common "k8s.io/kube-openapi/pkg/common"`,
  2418  			`spec "k8s.io/kube-openapi/pkg/validation/spec"`,
  2419  			`ptr "k8s.io/utils/ptr"`,
  2420  		}
  2421  		if !cmp.Equal(imports, expImports) {
  2422  			t.Errorf("wrong imports:\n%s", cmp.Diff(expImports, imports))
  2423  		}
  2424  
  2425  		if formatted, err := format.Source(funcBuffer.Bytes()); err != nil {
  2426  			t.Fatalf("%v\n%v", err, string(funcBuffer.Bytes()))
  2427  		} else {
  2428  			formatted_expected, ree := format.Source([]byte(`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  2429  		return common.OpenAPIDefinition{
  2430  			Schema: spec.Schema{
  2431  				SchemaProps: spec.SchemaProps{
  2432  					Type: 			  []string{"object"},
  2433  					MinProperties:	  ptr.To[int64](1),
  2434  					MaxProperties:	  ptr.To[int64](10),
  2435  					Properties: map[string]spec.Schema{
  2436  						"IntValue": {
  2437  							SchemaProps: spec.SchemaProps{
  2438  								Description: "Integer with min and max values",
  2439  								Default: 	 0,
  2440  								Minimum:	 ptr.To[float64](0),
  2441  								Maximum:	 ptr.To[float64](10),
  2442  								ExclusiveMinimum: true,
  2443  								ExclusiveMaximum: true,
  2444  								Type:        []string{"integer"},
  2445  								Format:	  	 "int32",
  2446  							},
  2447  						},
  2448  						"StringValue": {
  2449  							SchemaProps: spec.SchemaProps{
  2450  								Description: "String with min and max lengths",
  2451  								Default:	 "",
  2452  								MinLength:	 ptr.To[int64](1),
  2453  								MaxLength:	 ptr.To[int64](10),
  2454  								Pattern:	 "^foo$[0-9]+",
  2455  								Type:        []string{"string"},
  2456  								Format:	  	 "",
  2457  							},
  2458  						},
  2459  						"ArrayValue": {
  2460  							SchemaProps: spec.SchemaProps{
  2461  								MinItems:	 ptr.To[int64](1),
  2462  								MaxItems:	 ptr.To[int64](10),
  2463  								UniqueItems: true,
  2464  								Type: []string{"array"},
  2465  								Items: &spec.SchemaOrArray{
  2466  									Schema: &spec.Schema{
  2467  										SchemaProps: spec.SchemaProps{
  2468  											Default: "",
  2469  											Type:    []string{"string"},
  2470  											Format:  "",
  2471  										},
  2472  									},
  2473  								},
  2474  							},
  2475  						},
  2476  						"ObjValue": {
  2477  							SchemaProps: spec.SchemaProps{
  2478  								MinProperties:	 ptr.To[int64](1),
  2479  								MaxProperties:	 ptr.To[int64](10),
  2480  								Type: []string{"object"},
  2481  									AdditionalProperties: &spec.SchemaOrBool{
  2482  										Allows: true,
  2483  										Schema: &spec.Schema{
  2484  											SchemaProps: spec.SchemaProps{
  2485  												Type:   []string{"object"},
  2486  												Format: "",
  2487  											},
  2488  										},
  2489  									},
  2490  							},
  2491  						},
  2492  					},
  2493  					Required: []string{"IntValue", "StringValue", "ArrayValue", "ObjValue"},
  2494  				},
  2495  			},
  2496  		}
  2497  	}`))
  2498  			if ree != nil {
  2499  				t.Fatal(ree)
  2500  			}
  2501  			assertEqual(t, string(formatted), string(formatted_expected))
  2502  		}
  2503  	})
  2504  }
  2505  
  2506  func TestCELMarkerComments(t *testing.T) {
  2507  	inputFile := `
  2508  		package foo
  2509  
  2510  		// +k8s:openapi-gen=true
  2511  		// +k8s:validation:cel[0]:rule="self == oldSelf"
  2512  		// +k8s:validation:cel[0]:message="message1"
  2513  		type Blah struct {
  2514  			// +k8s:validation:cel[0]:rule="self.length() > 0"
  2515  			// +k8s:validation:cel[0]:message="string message"
  2516  			// +k8s:validation:cel[1]:rule="self.length() % 2 == 0"
  2517  			// +k8s:validation:cel[1]:messageExpression="self + ' hello'"
  2518  			// +k8s:validation:cel[1]:optionalOldSelf
  2519  			// +optional
  2520  			Field string
  2521  		}`
  2522  
  2523  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  2524  		e := packagestest.Export(t, x, []packagestest.Module{{
  2525  			Name: "example.com/base/foo",
  2526  			Files: map[string]interface{}{
  2527  				"foo.go": inputFile,
  2528  			},
  2529  		}})
  2530  		defer e.Cleanup()
  2531  
  2532  		callErr, funcErr, _, funcBuffer, imports := testOpenAPITypeWriter(t, e.Config)
  2533  		if funcErr != nil {
  2534  			t.Fatalf("Unexpected funcErr: %v", funcErr)
  2535  		}
  2536  		if callErr != nil {
  2537  			t.Fatalf("Unexpected callErr: %v", callErr)
  2538  		}
  2539  		expImports := []string{
  2540  			`foo "example.com/base/foo"`,
  2541  			`common "k8s.io/kube-openapi/pkg/common"`,
  2542  			`spec "k8s.io/kube-openapi/pkg/validation/spec"`,
  2543  		}
  2544  		if !cmp.Equal(imports, expImports) {
  2545  			t.Errorf("wrong imports:\n%s", cmp.Diff(expImports, imports))
  2546  		}
  2547  
  2548  		if formatted, err := format.Source(funcBuffer.Bytes()); err != nil {
  2549  			t.Fatalf("%v\n%v", err, string(funcBuffer.Bytes()))
  2550  		} else {
  2551  			formatted_expected, ree := format.Source([]byte(`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  2552  		return common.OpenAPIDefinition{
  2553  			Schema: spec.Schema{
  2554  				SchemaProps: spec.SchemaProps{
  2555  					Type: 			  []string{"object"},
  2556  					Properties: map[string]spec.Schema{
  2557  						"Field": {
  2558  							VendorExtensible: spec.VendorExtensible{
  2559  								Extensions: spec.Extensions{
  2560  									"x-kubernetes-validations": []interface{}{map[string]interface{}{"message": "string message", "rule": "self.length() > 0"}, map[string]interface{}{"messageExpression": "self + ' hello'", "optionalOldSelf": true, "rule": "self.length() % 2 == 0"}},
  2561  								},
  2562  							},
  2563  							SchemaProps: spec.SchemaProps{
  2564  								Default: "",
  2565  								Type:    []string{"string"},
  2566  								Format:  "",
  2567  							},
  2568  						},
  2569  					},
  2570  				},
  2571  				VendorExtensible: spec.VendorExtensible{
  2572  					Extensions: spec.Extensions{
  2573  						"x-kubernetes-validations": []interface{}{map[string]interface{}{"message": "message1", "rule": "self == oldSelf"}},
  2574  					},
  2575  				},
  2576  			},
  2577  		}
  2578  	}`))
  2579  			if ree != nil {
  2580  				t.Fatal(ree)
  2581  			}
  2582  			assertEqual(t, string(formatted_expected), string(formatted))
  2583  		}
  2584  	})
  2585  }
  2586  
  2587  func TestMultilineCELMarkerComments(t *testing.T) {
  2588  	inputFile := `
  2589  		package foo
  2590  
  2591  		// +k8s:openapi-gen=true
  2592  		// +k8s:validation:cel[0]:rule="self == oldSelf"
  2593  		// +k8s:validation:cel[0]:message="message1"
  2594  		// +k8s:validation:cel[0]:fieldPath="field"
  2595  		type Blah struct {
  2596  			// +k8s:validation:cel[0]:rule="self.length() > 0"
  2597  			// +k8s:validation:cel[0]:message="string message"
  2598  			// +k8s:validation:cel[0]:reason="Invalid"
  2599  			// +k8s:validation:cel[1]:rule>  !oldSelf.hasValue() || self.length() % 2 == 0
  2600  			// +k8s:validation:cel[1]:rule>     ? self.field == "even"
  2601  			// +k8s:validation:cel[1]:rule>     : self.field == "odd"
  2602  			// +k8s:validation:cel[1]:messageExpression="field must be whether the length of the string is even or odd"
  2603  			// +k8s:validation:cel[1]:optionalOldSelf
  2604  			// +optional
  2605  			Field string
  2606  		}`
  2607  
  2608  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  2609  		e := packagestest.Export(t, x, []packagestest.Module{{
  2610  			Name: "example.com/base/foo",
  2611  			Files: map[string]interface{}{
  2612  				"foo.go": inputFile,
  2613  			},
  2614  		}})
  2615  		defer e.Cleanup()
  2616  
  2617  		callErr, funcErr, _, funcBuffer, imports := testOpenAPITypeWriter(t, e.Config)
  2618  		if funcErr != nil {
  2619  			t.Fatalf("Unexpected funcErr: %v", funcErr)
  2620  		}
  2621  		if callErr != nil {
  2622  			t.Fatalf("Unexpected callErr: %v", callErr)
  2623  		}
  2624  		expImports := []string{
  2625  			`foo "example.com/base/foo"`,
  2626  			`common "k8s.io/kube-openapi/pkg/common"`,
  2627  			`spec "k8s.io/kube-openapi/pkg/validation/spec"`,
  2628  		}
  2629  		if !cmp.Equal(imports, expImports) {
  2630  			t.Errorf("wrong imports:\n%s", cmp.Diff(expImports, imports))
  2631  		}
  2632  
  2633  		if formatted, err := format.Source(funcBuffer.Bytes()); err != nil {
  2634  			t.Fatalf("%v\n%v", err, string(funcBuffer.Bytes()))
  2635  		} else {
  2636  			formatted_expected, ree := format.Source([]byte(`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  2637  		return common.OpenAPIDefinition{
  2638  			Schema: spec.Schema{
  2639  				SchemaProps: spec.SchemaProps{
  2640  					Type: 			  []string{"object"},
  2641  					Properties: map[string]spec.Schema{
  2642  						"Field": {
  2643  							VendorExtensible: spec.VendorExtensible{
  2644  								Extensions: spec.Extensions{
  2645  									"x-kubernetes-validations": []interface{}{map[string]interface{}{"message": "string message", "reason": "Invalid", "rule": "self.length() > 0"}, map[string]interface{}{"messageExpression": "field must be whether the length of the string is even or odd", "optionalOldSelf": true, "rule": "!oldSelf.hasValue() || self.length() % 2 == 0\n? self.field == \"even\"\n: self.field == \"odd\""}},
  2646  								},
  2647  							},
  2648  							SchemaProps: spec.SchemaProps{
  2649  								Default: "",
  2650  								Type:    []string{"string"},
  2651  								Format:  "",
  2652  							},
  2653  						},
  2654  					},
  2655  				},
  2656  				VendorExtensible: spec.VendorExtensible{
  2657  					Extensions: spec.Extensions{
  2658  						"x-kubernetes-validations": []interface{}{map[string]interface{}{"fieldPath": "field", "message": "message1", "rule": "self == oldSelf"}},
  2659  					},
  2660  				},
  2661  			},
  2662  		}
  2663  	}`))
  2664  			if ree != nil {
  2665  				t.Fatal(ree)
  2666  			}
  2667  			assertEqual(t, string(formatted_expected), string(formatted))
  2668  		}
  2669  	})
  2670  }
  2671  
  2672  func TestRequired(t *testing.T) {
  2673  	inputFile := `
  2674  		package foo
  2675  
  2676  		// +k8s:openapi-gen=true
  2677  		type Blah struct {
  2678  			// +optional
  2679  			OptionalField string
  2680  
  2681  			// +required
  2682  			RequiredField string
  2683  
  2684  			// +required
  2685  			RequiredPointerField *string ` + "`json:\"requiredPointerField,omitempty\"`" + `
  2686  
  2687  			// +optional
  2688  			OptionalPointerField *string ` + "`json:\"optionalPointerField,omitempty\"`" + `
  2689  
  2690  			ImplicitlyRequiredField string
  2691  			ImplicitlyOptionalField string ` + "`json:\"implicitlyOptionalField,omitempty\"`" + `
  2692  		}`
  2693  
  2694  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  2695  		e := packagestest.Export(t, x, []packagestest.Module{{
  2696  			Name: "example.com/base/foo",
  2697  			Files: map[string]interface{}{
  2698  				"foo.go": inputFile,
  2699  			},
  2700  		}})
  2701  		defer e.Cleanup()
  2702  
  2703  		callErr, funcErr, _, funcBuffer, imports := testOpenAPITypeWriter(t, e.Config)
  2704  		if funcErr != nil {
  2705  			t.Fatalf("Unexpected funcErr: %v", funcErr)
  2706  		}
  2707  		if callErr != nil {
  2708  			t.Fatalf("Unexpected callErr: %v", callErr)
  2709  		}
  2710  		expImports := []string{
  2711  			`foo "example.com/base/foo"`,
  2712  			`common "k8s.io/kube-openapi/pkg/common"`,
  2713  			`spec "k8s.io/kube-openapi/pkg/validation/spec"`,
  2714  		}
  2715  		if !cmp.Equal(imports, expImports) {
  2716  			t.Errorf("wrong imports:\n%s", cmp.Diff(expImports, imports))
  2717  		}
  2718  
  2719  		if formatted, err := format.Source(funcBuffer.Bytes()); err != nil {
  2720  			t.Fatalf("%v\n%v", err, string(funcBuffer.Bytes()))
  2721  		} else {
  2722  			formatted_expected, ree := format.Source([]byte(`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  2723  		return common.OpenAPIDefinition{
  2724  			Schema: spec.Schema{
  2725  				SchemaProps: spec.SchemaProps{
  2726  					Type: 			  []string{"object"},
  2727  					Properties: map[string]spec.Schema{
  2728  						"OptionalField": {
  2729  							SchemaProps: spec.SchemaProps{
  2730  								Default: "",
  2731  								Type:    []string{"string"},
  2732  								Format:  "",
  2733  							},
  2734  						},
  2735     						"RequiredField": {
  2736     							SchemaProps: spec.SchemaProps{
  2737     								Default: "",
  2738     								Type:    []string{"string"},
  2739     								Format:  "",
  2740     							},
  2741     						},
  2742     						"requiredPointerField": {
  2743     							SchemaProps: spec.SchemaProps{
  2744     								Type:   []string{"string"},
  2745     								Format: "",
  2746     							},
  2747     						},
  2748     						"optionalPointerField": {
  2749     							SchemaProps: spec.SchemaProps{
  2750     								Type:   []string{"string"},
  2751     								Format: "",
  2752     							},
  2753     						},
  2754     						"ImplicitlyRequiredField": {
  2755     							SchemaProps: spec.SchemaProps{
  2756     								Default: "",
  2757     								Type:    []string{"string"},
  2758     								Format:  "",
  2759     							},
  2760     						},
  2761     						"implicitlyOptionalField": {
  2762     							SchemaProps: spec.SchemaProps{
  2763     								Type:   []string{"string"},
  2764     								Format: "",
  2765     							},
  2766     						},
  2767  					},
  2768  					Required: []string{"RequiredField", "requiredPointerField", "ImplicitlyRequiredField"},
  2769  				},
  2770  			},
  2771  		}
  2772  	}`))
  2773  			if ree != nil {
  2774  				t.Fatal(ree)
  2775  			}
  2776  			assertEqual(t, string(formatted_expected), string(formatted))
  2777  		}
  2778  	})
  2779  
  2780  	// Show specifying both is an error
  2781  	badFile := `
  2782  		package foo
  2783  
  2784  		// +k8s:openapi-gen=true
  2785  		type Blah struct {
  2786  			// +optional
  2787  			// +required
  2788  			ConfusingField string
  2789  		}`
  2790  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  2791  		e := packagestest.Export(t, x, []packagestest.Module{{
  2792  			Name: "example.com/base/foo",
  2793  			Files: map[string]interface{}{
  2794  				"foo.go": badFile,
  2795  			},
  2796  		}})
  2797  		defer e.Cleanup()
  2798  
  2799  		callErr, funcErr, _, _, _ := testOpenAPITypeWriter(t, e.Config)
  2800  		if callErr != nil {
  2801  			t.Errorf("Unexpected callErr: %v", callErr)
  2802  		}
  2803  		if funcErr == nil {
  2804  			t.Fatalf("Expected funcErr")
  2805  		}
  2806  		if !strings.Contains(funcErr.Error(), "cannot be both optional and required") {
  2807  			t.Errorf("Unexpected error: %v", funcErr)
  2808  		}
  2809  	})
  2810  }
  2811  
  2812  func TestMarkerCommentsCustomDefsV3(t *testing.T) {
  2813  	inputFile := `
  2814  		package foo
  2815  
  2816  		import openapi "k8s.io/kube-openapi/pkg/common"
  2817  
  2818  		// +k8s:validation:maxProperties=10
  2819  		type Blah struct {
  2820  		}
  2821  
  2822  		func (_ Blah) OpenAPIV3Definition() openapi.OpenAPIDefinition {
  2823  			return openapi.OpenAPIDefinition{
  2824  				Schema: spec.Schema{
  2825  					SchemaProps: spec.SchemaProps{
  2826  						Type:   []string{"object"},
  2827  						MaxProperties: ptr.To[int64](10),
  2828  						Format: "ipv4",
  2829  					},
  2830  				},
  2831  			}
  2832  		}
  2833  
  2834  		func (_ Blah) OpenAPISchemaType() []string { return []string{"object"} }
  2835  		func (_ Blah) OpenAPISchemaFormat() string { return "ipv4" }`
  2836  	commonFile := `package common
  2837  
  2838  		type OpenAPIDefinition struct {}`
  2839  
  2840  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  2841  		e := packagestest.Export(t, x, []packagestest.Module{{
  2842  			Name: "example.com/base/foo",
  2843  			Files: map[string]interface{}{
  2844  				"foo.go": inputFile,
  2845  			},
  2846  		}, {
  2847  			Name: "k8s.io/kube-openapi/pkg/common",
  2848  			Files: map[string]interface{}{
  2849  				"common.go": commonFile,
  2850  			},
  2851  		}})
  2852  		defer e.Cleanup()
  2853  
  2854  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  2855  		if callErr != nil {
  2856  			t.Fatal(callErr)
  2857  		}
  2858  		if funcErr != nil {
  2859  			t.Fatal(funcErr)
  2860  		}
  2861  		assertEqual(t, callBuffer.String(),
  2862  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
  2863  		assertEqual(t, funcBuffer.String(),
  2864  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  2865  return common.EmbedOpenAPIDefinitionIntoV2Extension(foo.Blah{}.OpenAPIV3Definition(), common.OpenAPIDefinition{
  2866  Schema: spec.Schema{
  2867  SchemaProps: spec.SchemaProps{
  2868  Type:foo.Blah{}.OpenAPISchemaType(),
  2869  Format:foo.Blah{}.OpenAPISchemaFormat(),
  2870  MaxProperties: ptr.To[int64](10),
  2871  },
  2872  },
  2873  })
  2874  }`)
  2875  	})
  2876  }
  2877  
  2878  func TestMarkerCommentsV3OneOfTypes(t *testing.T) {
  2879  	inputFile := `
  2880  		package foo
  2881  
  2882  		// +k8s:validation:maxLength=10
  2883  		type Blah struct {
  2884  		}
  2885  
  2886  		func (_ Blah) OpenAPISchemaType() []string { return []string{"string"} }
  2887  		func (_ Blah) OpenAPIV3OneOfTypes() []string { return []string{"string", "array"} }
  2888  		func (_ Blah) OpenAPISchemaFormat() string { return "ipv4" }`
  2889  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  2890  		e := packagestest.Export(t, x, []packagestest.Module{{
  2891  			Name: "example.com/base/foo",
  2892  			Files: map[string]interface{}{
  2893  				"foo.go": inputFile,
  2894  			},
  2895  		}})
  2896  		defer e.Cleanup()
  2897  
  2898  		callErr, funcErr, callBuffer, funcBuffer, _ := testOpenAPITypeWriter(t, e.Config)
  2899  		if callErr != nil {
  2900  			t.Fatal(callErr)
  2901  		}
  2902  		if funcErr != nil {
  2903  			t.Fatal(funcErr)
  2904  		}
  2905  		assertEqual(t, callBuffer.String(),
  2906  			`"example.com/base/foo.Blah": schema_examplecom_base_foo_Blah(ref),`)
  2907  		assertEqual(t, funcBuffer.String(),
  2908  			`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  2909  return common.EmbedOpenAPIDefinitionIntoV2Extension(common.OpenAPIDefinition{
  2910  Schema: spec.Schema{
  2911  SchemaProps: spec.SchemaProps{
  2912  OneOf:common.GenerateOpenAPIV3OneOfSchema(foo.Blah{}.OpenAPIV3OneOfTypes()),
  2913  Format:foo.Blah{}.OpenAPISchemaFormat(),
  2914  MaxLength: ptr.To[int64](10),
  2915  },
  2916  },
  2917  },common.OpenAPIDefinition{
  2918  Schema: spec.Schema{
  2919  SchemaProps: spec.SchemaProps{
  2920  Type:foo.Blah{}.OpenAPISchemaType(),
  2921  Format:foo.Blah{}.OpenAPISchemaFormat(),
  2922  MaxLength: ptr.To[int64](10),
  2923  },
  2924  },
  2925  })
  2926  }`)
  2927  	})
  2928  }
  2929  
  2930  func TestNestedMarkers(t *testing.T) {
  2931  	inputFile := `
  2932  		package foo
  2933  
  2934  		// +k8s:openapi-gen=true
  2935  		// +k8s:validation:properties:field:items:maxLength=10
  2936  		// +k8s:validation:properties:aliasMap:additionalProperties:pattern>^foo$
  2937  		type Blah struct {
  2938  			// +k8s:validation:items:cel[0]:rule="self.length() % 2 == 0"
  2939  			Field MyAlias ` + "`json:\"field,omitempty\"`" + `
  2940  
  2941  			// +k8s:validation:additionalProperties:maxLength=10
  2942  			AliasMap MyAliasMap ` + "`json:\"aliasMap,omitempty\"`" + `
  2943  		}
  2944  		
  2945  		type MyAliasMap map[string]MyAlias
  2946  		type MyAlias []string`
  2947  
  2948  	packagestest.TestAll(t, func(t *testing.T, x packagestest.Exporter) {
  2949  		e := packagestest.Export(t, x, []packagestest.Module{{
  2950  			Name: "example.com/base/foo",
  2951  			Files: map[string]interface{}{
  2952  				"foo.go": inputFile,
  2953  			},
  2954  		}})
  2955  		defer e.Cleanup()
  2956  
  2957  		callErr, funcErr, _, funcBuffer, imports := testOpenAPITypeWriter(t, e.Config)
  2958  		if funcErr != nil {
  2959  			t.Fatalf("Unexpected funcErr: %v", funcErr)
  2960  		}
  2961  		if callErr != nil {
  2962  			t.Fatalf("Unexpected callErr: %v", callErr)
  2963  		}
  2964  		expImports := []string{
  2965  			`foo "example.com/base/foo"`,
  2966  			`common "k8s.io/kube-openapi/pkg/common"`,
  2967  			`spec "k8s.io/kube-openapi/pkg/validation/spec"`,
  2968  			`ptr "k8s.io/utils/ptr"`,
  2969  		}
  2970  		if !cmp.Equal(imports, expImports) {
  2971  			t.Errorf("wrong imports:\n%s", cmp.Diff(expImports, imports))
  2972  		}
  2973  
  2974  		if formatted, err := format.Source(funcBuffer.Bytes()); err != nil {
  2975  			t.Fatalf("%v\n%v", err, funcBuffer.String())
  2976  		} else {
  2977  			formatted_expected, ree := format.Source([]byte(`func schema_examplecom_base_foo_Blah(ref common.ReferenceCallback) common.OpenAPIDefinition {
  2978                  return common.OpenAPIDefinition{
  2979                          Schema: spec.Schema{
  2980                                  SchemaProps: spec.SchemaProps{
  2981                                          Type: []string{"object"},
  2982                                          AllOf: []spec.Schema{
  2983                                                  {
  2984                                                          SchemaProps: spec.SchemaProps{
  2985                                                                  Properties: map[string]spec.Schema{
  2986                                                                          "aliasMap": {
  2987                                                                                  SchemaProps: spec.SchemaProps{
  2988                                                                                                  AllOf: []spec.Schema{
  2989                                                                                                   {
  2990                                                                                                   SchemaProps: spec.SchemaProps{
  2991                                                                                                   AdditionalProperties: &spec.SchemaOrBool{
  2992  																							     Allows: true,
  2993                                                                                                   Schema: &spec.Schema{
  2994                                                                                                   SchemaProps: spec.SchemaProps{
  2995                                                                                                   Pattern: "^foo$",
  2996                                                                                                   },
  2997                                                                                                   },
  2998                                                                                                   },
  2999                                                                                                   },
  3000                                                                                                   },
  3001                                                                                                  },
  3002                                                                                  },
  3003                                                                          },
  3004                                                                          "field": {
  3005                                                                                  SchemaProps: spec.SchemaProps{
  3006                                                                                                  AllOf: []spec.Schema{
  3007                                                                                                   {
  3008                                                                                                   SchemaProps: spec.SchemaProps{
  3009                                                                                                   Items: &spec.SchemaOrArray{
  3010                                                                                                   Schema: &spec.Schema{
  3011                                                                                                   SchemaProps: spec.SchemaProps{
  3012                                                                                                   MaxLength: ptr.To[int64](10),
  3013                                                                                                   },
  3014                                                                                                   },
  3015                                                                                                   },
  3016                                                                                                   },
  3017                                                                                                   },
  3018                                                                                                  },
  3019                                                                                  },
  3020                                                                          },
  3021                                                                  },
  3022                                                          },
  3023                                                  },
  3024                                          },
  3025                                          Properties: map[string]spec.Schema{
  3026                                                  "field": {
  3027                                                          SchemaProps: spec.SchemaProps{
  3028                                                                  AllOf: []spec.Schema{
  3029                                                                          {
  3030                                                                                  SchemaProps: spec.SchemaProps{
  3031                                                                                          Items: &spec.SchemaOrArray{
  3032                                                                                                  Schema: &spec.Schema{
  3033                                                                                                   VendorExtensible: spec.VendorExtensible{
  3034                                                                                                   Extensions: spec.Extensions{
  3035                                                                                                   "x-kubernetes-validations": []interface{}{map[string]interface{}{"rule": "self.length() % 2 == 0"}},
  3036                                                                                                   },
  3037                                                                                                   },
  3038                                                                                                  },
  3039                                                                                          },
  3040                                                                                  },
  3041                                                                          },
  3042                                                                  },
  3043                                                                  Type: []string{"array"},
  3044                                                                  Items: &spec.SchemaOrArray{
  3045                                                                          Schema: &spec.Schema{
  3046                                                                                  SchemaProps: spec.SchemaProps{
  3047                                                                                          Default: "",
  3048                                                                                          Type:    []string{"string"},
  3049                                                                                          Format:  "",
  3050                                                                                  },
  3051                                                                          },
  3052                                                                  },
  3053                                                          },
  3054                                                  },
  3055                                                  "aliasMap": {
  3056                                                          SchemaProps: spec.SchemaProps{
  3057                                                                  AllOf: []spec.Schema{
  3058                                                                          {
  3059                                                                                  SchemaProps: spec.SchemaProps{
  3060                                                                                          AdditionalProperties: &spec.SchemaOrBool{
  3061  																							    Allows: true,
  3062                                                                                                  Schema: &spec.Schema{
  3063                                                                                                   SchemaProps: spec.SchemaProps{
  3064                                                                                                   MaxLength: ptr.To[int64](10),
  3065                                                                                                   },
  3066                                                                                                  },
  3067                                                                                          },
  3068                                                                                  },
  3069                                                                          },
  3070                                                                  },
  3071                                                                  Type: []string{"object"},
  3072                                                                  AdditionalProperties: &spec.SchemaOrBool{
  3073                                                                          Allows: true,
  3074                                                                          Schema: &spec.Schema{
  3075                                                                                  SchemaProps: spec.SchemaProps{
  3076                                                                                          Type: []string{"array"},
  3077                                                                                          Items: &spec.SchemaOrArray{
  3078                                                                                                  Schema: &spec.Schema{
  3079                                                                                                   SchemaProps: spec.SchemaProps{
  3080                                                                                                   Default: "",
  3081                                                                                                   Type:    []string{"string"},
  3082                                                                                                   Format:  "",
  3083                                                                                                   },
  3084                                                                                                  },
  3085                                                                                          },
  3086                                                                                  },
  3087                                                                          },
  3088                                                                  },
  3089                                                          },
  3090                                                  },
  3091                                          },
  3092                                  },
  3093                          },
  3094                  }
  3095          }`))
  3096  			if ree != nil {
  3097  				t.Fatal(ree)
  3098  			}
  3099  			assertEqual(t, string(formatted), string(formatted_expected))
  3100  		}
  3101  	})
  3102  
  3103  }