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