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