1
16 package generators_test
17
18 import (
19 "testing"
20
21 "github.com/stretchr/testify/require"
22 "k8s.io/gengo/v2/types"
23 "k8s.io/kube-openapi/pkg/generators"
24 "k8s.io/kube-openapi/pkg/validation/spec"
25 "k8s.io/utils/ptr"
26 )
27
28 var structKind *types.Type = &types.Type{Kind: types.Struct, Name: types.Name{Name: "struct"}}
29 var mapType *types.Type = &types.Type{Kind: types.Map, Name: types.Name{Name: "map[string]int"}}
30 var arrayType *types.Type = &types.Type{Kind: types.Slice, Name: types.Name{Name: "[]int"}}
31
32 func TestParseCommentTags(t *testing.T) {
33
34 cases := []struct {
35 t *types.Type
36 name string
37 comments []string
38 expected *spec.Schema
39
40
41
42 expectedError string
43 }{
44 {
45 t: structKind,
46 name: "basic example",
47 comments: []string{
48 "comment",
49 "another + comment",
50 "+k8s:validation:minimum=10.0",
51 "+k8s:validation:maximum=20.0",
52 "+k8s:validation:minLength=20",
53 "+k8s:validation:maxLength=30",
54 `+k8s:validation:pattern="asdf"`,
55 "+k8s:validation:multipleOf=1.0",
56 "+k8s:validation:minItems=1",
57 "+k8s:validation:maxItems=2",
58 "+k8s:validation:uniqueItems=true",
59 "exclusiveMaximum=true",
60 "not+k8s:validation:Minimum=0.0",
61 },
62 expected: &spec.Schema{
63 SchemaProps: spec.SchemaProps{
64 Maximum: ptr.To(20.0),
65 Minimum: ptr.To(10.0),
66 MinLength: ptr.To[int64](20),
67 MaxLength: ptr.To[int64](30),
68 Pattern: "asdf",
69 MultipleOf: ptr.To(1.0),
70 MinItems: ptr.To[int64](1),
71 MaxItems: ptr.To[int64](2),
72 UniqueItems: true,
73 },
74 },
75 },
76 {
77 t: structKind,
78 name: "empty",
79 expected: &spec.Schema{},
80 },
81 {
82 t: types.Float64,
83 name: "single",
84 comments: []string{
85 "+k8s:validation:minimum=10.0",
86 },
87 expected: &spec.Schema{
88 SchemaProps: spec.SchemaProps{
89 Minimum: ptr.To(10.0),
90 },
91 },
92 },
93 {
94 t: types.Float64,
95 name: "multiple",
96 comments: []string{
97 "+k8s:validation:minimum=10.0",
98 "+k8s:validation:maximum=20.0",
99 },
100 expected: &spec.Schema{
101 SchemaProps: spec.SchemaProps{
102 Maximum: ptr.To(20.0),
103 Minimum: ptr.To(10.0),
104 },
105 },
106 },
107 {
108 t: types.Float64,
109 name: "invalid duplicate key",
110 comments: []string{
111 "+k8s:validation:minimum=10.0",
112 "+k8s:validation:maximum=20.0",
113 "+k8s:validation:minimum=30.0",
114 },
115 expectedError: `failed to parse marker comments: cannot have multiple values for key 'minimum'`,
116 },
117 {
118 t: structKind,
119 name: "unrecognized key is ignored",
120 comments: []string{
121 "+ignored=30.0",
122 },
123 expected: &spec.Schema{},
124 },
125 {
126 t: types.Float64,
127 name: "invalid: non-JSON value",
128 comments: []string{
129 `+k8s:validation:minimum=asdf`,
130 },
131 expectedError: `failed to parse marker comments: failed to parse value for key minimum as JSON: invalid character 'a' looking for beginning of value`,
132 },
133 {
134 t: types.Float64,
135 name: "invalid: invalid value type",
136 comments: []string{
137 `+k8s:validation:minimum="asdf"`,
138 },
139 expectedError: `failed to unmarshal marker comments: json: cannot unmarshal string into Go struct field commentTags.minimum of type float64`,
140 },
141 {
142
143 t: structKind,
144 name: "invalid: empty key",
145 comments: []string{
146 "+k8s:validation:",
147 },
148 expectedError: `failed to parse marker comments: cannot have empty key for marker comment`,
149 },
150 {
151 t: types.Float64,
152
153 name: "ignore refs",
154 comments: []string{
155 "+k8s:validation:pattern=ref(asdf)",
156 },
157 expected: &spec.Schema{},
158 },
159 {
160 t: types.Float64,
161 name: "cel rule",
162 comments: []string{
163 `+k8s:validation:cel[0]:rule="oldSelf == self"`,
164 `+k8s:validation:cel[0]:message="immutable field"`,
165 },
166 expected: &spec.Schema{
167 VendorExtensible: spec.VendorExtensible{
168 Extensions: map[string]interface{}{
169 "x-kubernetes-validations": []interface{}{
170 map[string]interface{}{
171 "rule": "oldSelf == self",
172 "message": "immutable field",
173 },
174 },
175 },
176 },
177 },
178 },
179 {
180 t: types.Float64,
181 name: "skipped CEL rule",
182 comments: []string{
183
184
185 `+k8s:validation:cel[0]:rule="oldSelf == self"`,
186 `+k8s:validation:cel[0]:message="immutable field"`,
187 `+k8s:validation:cel[2]:rule="self > 5"`,
188 `+k8s:validation:cel[2]:message="must be greater than 5"`,
189 },
190 expectedError: `failed to parse marker comments: error parsing cel[2]:rule="self > 5": non-consecutive index 2 for key 'cel'`,
191 },
192 {
193 t: types.Float64,
194 name: "multiple CEL params",
195 comments: []string{
196 `+k8s:validation:cel[0]:rule="oldSelf == self"`,
197 `+k8s:validation:cel[0]:message="immutable field"`,
198 `+k8s:validation:cel[1]:rule="self > 5"`,
199 `+k8s:validation:cel[1]:optionalOldSelf=true`,
200 `+k8s:validation:cel[1]:message="must be greater than 5"`,
201 },
202 expected: &spec.Schema{
203 VendorExtensible: spec.VendorExtensible{
204 Extensions: map[string]interface{}{
205 "x-kubernetes-validations": []interface{}{
206 map[string]interface{}{
207 "rule": "oldSelf == self",
208 "message": "immutable field",
209 },
210 map[string]interface{}{
211 "rule": "self > 5",
212 "optionalOldSelf": true,
213 "message": "must be greater than 5",
214 },
215 },
216 },
217 },
218 },
219 },
220 {
221 t: types.Float64,
222 name: "multiple rules with multiple params",
223 comments: []string{
224 `+k8s:validation:cel[0]:rule="oldSelf == self"`,
225 `+k8s:validation:cel[0]:optionalOldSelf`,
226 `+k8s:validation:cel[0]:messageExpression="self + ' must be equal to old value'"`,
227 `+k8s:validation:cel[1]:rule="self > 5"`,
228 `+k8s:validation:cel[1]:optionalOldSelf=true`,
229 `+k8s:validation:cel[1]:message="must be greater than 5"`,
230 },
231 expected: &spec.Schema{
232 VendorExtensible: spec.VendorExtensible{
233 Extensions: map[string]interface{}{
234 "x-kubernetes-validations": []interface{}{
235 map[string]interface{}{
236 "rule": "oldSelf == self",
237 "optionalOldSelf": true,
238 "messageExpression": "self + ' must be equal to old value'",
239 },
240 map[string]interface{}{
241 "rule": "self > 5",
242 "optionalOldSelf": true,
243 "message": "must be greater than 5",
244 },
245 },
246 },
247 },
248 },
249 },
250 {
251 t: types.Float64,
252 name: "skipped array index",
253 comments: []string{
254 `+k8s:validation:cel[0]:rule="oldSelf == self"`,
255 `+k8s:validation:cel[0]:optionalOldSelf`,
256 `+k8s:validation:cel[0]:messageExpression="self + ' must be equal to old value'"`,
257 `+k8s:validation:cel[2]:rule="self > 5"`,
258 `+k8s:validation:cel[2]:optionalOldSelf=true`,
259 `+k8s:validation:cel[2]:message="must be greater than 5"`,
260 },
261 expectedError: `failed to parse marker comments: error parsing cel[2]:rule="self > 5": non-consecutive index 2 for key 'cel'`,
262 },
263 {
264 t: types.Float64,
265 name: "non-consecutive array index",
266 comments: []string{
267 `+k8s:validation:cel[0]:rule="oldSelf == self"`,
268 `+k8s:validation:cel[1]:rule="self > 5"`,
269 `+k8s:validation:cel[1]:message="self > 5"`,
270 `+k8s:validation:cel[0]:optionalOldSelf=true`,
271 `+k8s:validation:cel[0]:message="must be greater than 5"`,
272 },
273 expectedError: "failed to parse marker comments: error parsing cel[0]:optionalOldSelf=true: non-consecutive index 0 for key 'cel'",
274 },
275 {
276 t: types.Float64,
277 name: "interjected array index",
278 comments: []string{
279 `+k8s:validation:cel[0]:rule="oldSelf == self"`,
280 `+k8s:validation:cel[0]:message="cant change"`,
281 `+k8s:validation:cel[1]:rule="self > 5"`,
282 `+k8s:validation:cel[1]:message="must be greater than 5"`,
283 `+k8s:validation:minimum=5`,
284 `+k8s:validation:cel[2]:rule="a rule"`,
285 `+k8s:validation:cel[2]:message="message 2"`,
286 },
287 expectedError: "failed to parse marker comments: error parsing cel[2]:rule=\"a rule\": non-consecutive index 2 for key 'cel'",
288 },
289 {
290 t: types.Float64,
291 name: "interjected array index with non-prefixed comment",
292 comments: []string{
293 `+k8s:validation:cel[0]:rule="oldSelf == self"`,
294 `+k8s:validation:cel[0]:message="cant change"`,
295 `+k8s:validation:cel[1]:rule="self > 5"`,
296 `+k8s:validation:cel[1]:message="must be greater than 5"`,
297 `+minimum=5`,
298 `+k8s:validation:cel[2]:rule="a rule"`,
299 `+k8s:validation:cel[2]:message="message 2"`,
300 },
301 expectedError: "failed to parse marker comments: error parsing cel[2]:rule=\"a rule\": non-consecutive index 2 for key 'cel'",
302 },
303 {
304 t: types.Float64,
305 name: "non-consecutive raw string indexing",
306 comments: []string{
307 `+k8s:validation:cel[0]:rule> raw string rule`,
308 `+k8s:validation:cel[1]:rule="self > 5"`,
309 `+k8s:validation:cel[1]:message="must be greater than 5"`,
310 `+k8s:validation:cel[0]:message>another raw string message`,
311 },
312 expectedError: "failed to parse marker comments: error parsing cel[0]:message>another raw string message: non-consecutive index 0 for key 'cel'",
313 },
314 {
315 t: types.String,
316 name: "non-consecutive string indexing false positive",
317 comments: []string{
318 `+k8s:validation:cel[0]:message>[3]string rule [1]`,
319 `+k8s:validation:cel[0]:rule="string rule [1]"`,
320 `+k8s:validation:pattern="self[3] == 'hi'"`,
321 },
322 expected: &spec.Schema{
323 SchemaProps: spec.SchemaProps{
324 Pattern: `self[3] == 'hi'`,
325 },
326 VendorExtensible: spec.VendorExtensible{
327 Extensions: map[string]interface{}{
328 "x-kubernetes-validations": []interface{}{
329 map[string]interface{}{
330 "rule": "string rule [1]",
331 "message": "[3]string rule [1]",
332 },
333 },
334 },
335 },
336 },
337 },
338 {
339 t: types.String,
340 name: "non-consecutive raw string indexing false positive",
341 comments: []string{
342 `+k8s:validation:cel[0]:message>[3]raw string message with subscirpt [3]"`,
343 `+k8s:validation:cel[0]:rule> raw string rule [1]`,
344 `+k8s:validation:pattern>"self[3] == 'hi'"`,
345 },
346 expected: &spec.Schema{
347 SchemaProps: spec.SchemaProps{
348 Pattern: `"self[3] == 'hi'"`,
349 },
350 VendorExtensible: spec.VendorExtensible{
351 Extensions: map[string]interface{}{
352 "x-kubernetes-validations": []interface{}{
353 map[string]interface{}{
354 "rule": "raw string rule [1]",
355 "message": "[3]raw string message with subscirpt [3]\"",
356 },
357 },
358 },
359 },
360 },
361 },
362 {
363 t: types.Float64,
364 name: "boolean key at invalid index",
365 comments: []string{
366 `+k8s:validation:cel[0]:rule="oldSelf == self"`,
367 `+k8s:validation:cel[0]:message="cant change"`,
368 `+k8s:validation:cel[2]:optionalOldSelf`,
369 },
370 expectedError: `failed to parse marker comments: error parsing cel[2]:optionalOldSelf: non-consecutive index 2 for key 'cel'`,
371 },
372 {
373 t: types.Float64,
374 name: "boolean key after non-prefixed comment",
375 comments: []string{
376 `+k8s:validation:cel[0]:rule="oldSelf == self"`,
377 `+k8s:validation:cel[0]:message="cant change"`,
378 `+k8s:validation:cel[1]:rule="self > 5"`,
379 `+k8s:validation:cel[1]:message="must be greater than 5"`,
380 `+minimum=5`,
381 `+k8s:validation:cel[1]:optionalOldSelf`,
382 },
383 expectedError: `failed to parse marker comments: error parsing cel[1]:optionalOldSelf: non-consecutive index 1 for key 'cel'`,
384 },
385 {
386 t: types.Float64,
387 name: "boolean key at index allowed",
388 comments: []string{
389 `+k8s:validation:cel[0]:rule="oldSelf == self"`,
390 `+k8s:validation:cel[0]:message="cant change"`,
391 `+k8s:validation:cel[1]:rule="self > 5"`,
392 `+k8s:validation:cel[1]:message="must be greater than 5"`,
393 `+k8s:validation:cel[1]:optionalOldSelf`,
394 },
395 expected: &spec.Schema{
396 VendorExtensible: spec.VendorExtensible{
397 Extensions: map[string]interface{}{
398 "x-kubernetes-validations": []interface{}{
399 map[string]interface{}{
400 "rule": "oldSelf == self",
401 "message": "cant change",
402 },
403 map[string]interface{}{
404 "rule": "self > 5",
405 "message": "must be greater than 5",
406 "optionalOldSelf": true,
407 },
408 },
409 },
410 },
411 },
412 },
413 {
414 t: types.Float64,
415 name: "raw string rule",
416 comments: []string{
417 `+k8s:validation:cel[0]:rule> raw string rule`,
418 `+k8s:validation:cel[0]:message="raw string message"`,
419 },
420 expected: &spec.Schema{
421 VendorExtensible: spec.VendorExtensible{
422 Extensions: map[string]interface{}{
423 "x-kubernetes-validations": []interface{}{
424 map[string]interface{}{
425 "rule": "raw string rule",
426 "message": "raw string message",
427 },
428 },
429 },
430 },
431 },
432 },
433 {
434 t: types.Float64,
435 name: "multiline string rule",
436 comments: []string{
437 `+k8s:validation:cel[0]:rule> self.length() % 2 == 0`,
438 `+k8s:validation:cel[0]:rule> ? self.field == self.name + ' is even'`,
439 `+k8s:validation:cel[0]:rule> : self.field == self.name + ' is odd'`,
440 `+k8s:validation:cel[0]:message>raw string message`,
441 },
442 expected: &spec.Schema{
443 VendorExtensible: spec.VendorExtensible{
444 Extensions: map[string]interface{}{
445 "x-kubernetes-validations": []interface{}{
446 map[string]interface{}{
447 "rule": "self.length() % 2 == 0\n? self.field == self.name + ' is even'\n: self.field == self.name + ' is odd'",
448 "message": "raw string message",
449 },
450 },
451 },
452 },
453 },
454 },
455 {
456 t: types.Float64,
457 name: "mix raw and non-raw string marker",
458 comments: []string{
459 `+k8s:validation:cel[0]:message>raw string message`,
460 `+k8s:validation:cel[0]:rule="self.length() % 2 == 0"`,
461 `+k8s:validation:cel[0]:rule> ? self.field == self.name + ' is even'`,
462 `+k8s:validation:cel[0]:rule> : self.field == self.name + ' is odd'`,
463 },
464 expected: &spec.Schema{
465 VendorExtensible: spec.VendorExtensible{
466 Extensions: map[string]interface{}{
467 "x-kubernetes-validations": []interface{}{
468 map[string]interface{}{
469 "rule": "self.length() % 2 == 0\n? self.field == self.name + ' is even'\n: self.field == self.name + ' is odd'",
470 "message": "raw string message",
471 },
472 },
473 },
474 },
475 },
476 },
477 {
478 name: "raw string with different key in between",
479 t: types.Float64,
480 comments: []string{
481 `+k8s:validation:cel[0]:message>raw string message`,
482 `+k8s:validation:cel[0]:rule="self.length() % 2 == 0"`,
483 `+k8s:validation:cel[0]:message>raw string message 2`,
484 },
485 expectedError: `failed to parse marker comments: concatenations to key 'cel[0]:message' must be consecutive with its assignment`,
486 },
487 {
488 name: "raw string with different raw string key in between",
489 t: types.Float64,
490 comments: []string{
491 `+k8s:validation:cel[0]:message>raw string message`,
492 `+k8s:validation:cel[0]:rule>self.length() % 2 == 0`,
493 `+k8s:validation:cel[0]:message>raw string message 2`,
494 },
495 expectedError: `failed to parse marker comments: concatenations to key 'cel[0]:message' must be consecutive with its assignment`,
496 },
497 }
498
499 for _, tc := range cases {
500 t.Run(tc.name, func(t *testing.T) {
501 actual, err := generators.ParseCommentTags(tc.t, tc.comments, "+k8s:validation:")
502 if tc.expectedError != "" {
503 require.Error(t, err)
504 require.EqualError(t, err, tc.expectedError)
505 return
506 } else {
507 require.NoError(t, err)
508 }
509
510 require.Equal(t, tc.expected, actual)
511 })
512 }
513 }
514
515
516 func TestCommentTags_Validate(t *testing.T) {
517
518 testCases := []struct {
519 name string
520 comments []string
521 t *types.Type
522 errorMessage string
523 }{
524 {
525 name: "invalid minimum type",
526 comments: []string{
527 `+k8s:validation:minimum=10.5`,
528 },
529 t: types.String,
530 errorMessage: "minimum can only be used on numeric types",
531 },
532 {
533 name: "invalid minLength type",
534 comments: []string{
535 `+k8s:validation:minLength=10`,
536 },
537 t: types.Bool,
538 errorMessage: "minLength can only be used on string types",
539 },
540 {
541 name: "invalid minItems type",
542 comments: []string{
543 `+k8s:validation:minItems=10`,
544 },
545 t: types.String,
546 errorMessage: "minItems can only be used on array types",
547 },
548 {
549 name: "invalid minProperties type",
550 comments: []string{
551 `+k8s:validation:minProperties=10`,
552 },
553 t: types.String,
554 errorMessage: "minProperties can only be used on map types",
555 },
556 {
557 name: "invalid exclusiveMinimum type",
558 comments: []string{
559 `+k8s:validation:exclusiveMinimum=true`,
560 },
561 t: arrayType,
562 errorMessage: "exclusiveMinimum can only be used on numeric types",
563 },
564 {
565 name: "invalid maximum type",
566 comments: []string{
567 `+k8s:validation:maximum=10.5`,
568 },
569 t: arrayType,
570 errorMessage: "maximum can only be used on numeric types",
571 },
572 {
573 name: "invalid maxLength type",
574 comments: []string{
575 `+k8s:validation:maxLength=10`,
576 },
577 t: mapType,
578 errorMessage: "maxLength can only be used on string types",
579 },
580 {
581 name: "invalid maxItems type",
582 comments: []string{
583 `+k8s:validation:maxItems=10`,
584 },
585 t: types.Bool,
586 errorMessage: "maxItems can only be used on array types",
587 },
588 {
589 name: "invalid maxProperties type",
590 comments: []string{
591 `+k8s:validation:maxProperties=10`,
592 },
593 t: types.Bool,
594 errorMessage: "maxProperties can only be used on map types",
595 },
596 {
597 name: "invalid exclusiveMaximum type",
598 comments: []string{
599 `+k8s:validation:exclusiveMaximum=true`,
600 },
601 t: mapType,
602 errorMessage: "exclusiveMaximum can only be used on numeric types",
603 },
604 {
605 name: "invalid pattern type",
606 comments: []string{
607 `+k8s:validation:pattern=".*"`,
608 },
609 t: types.Int,
610 errorMessage: "pattern can only be used on string types",
611 },
612 {
613 name: "invalid multipleOf type",
614 comments: []string{
615 `+k8s:validation:multipleOf=10.5`,
616 },
617 t: types.String,
618 errorMessage: "multipleOf can only be used on numeric types",
619 },
620 {
621 name: "invalid uniqueItems type",
622 comments: []string{
623 `+k8s:validation:uniqueItems=true`,
624 },
625 t: types.Int,
626 errorMessage: "uniqueItems can only be used on array types",
627 },
628 {
629 name: "negative minLength",
630 comments: []string{
631 `+k8s:validation:minLength=-10`,
632 },
633 t: types.String,
634 errorMessage: "minLength cannot be negative",
635 },
636 {
637 name: "negative minItems",
638 comments: []string{
639 `+k8s:validation:minItems=-10`,
640 },
641 t: arrayType,
642 errorMessage: "minItems cannot be negative",
643 },
644 {
645 name: "negative minProperties",
646 comments: []string{
647 `+k8s:validation:minProperties=-10`,
648 },
649 t: mapType,
650 errorMessage: "minProperties cannot be negative",
651 },
652 {
653 name: "negative maxLength",
654 comments: []string{
655 `+k8s:validation:maxLength=-10`,
656 },
657 t: types.String,
658 errorMessage: "maxLength cannot be negative",
659 },
660 {
661 name: "negative maxItems",
662 comments: []string{
663 `+k8s:validation:maxItems=-10`,
664 },
665 t: arrayType,
666 errorMessage: "maxItems cannot be negative",
667 },
668 {
669 name: "negative maxProperties",
670 comments: []string{
671 `+k8s:validation:maxProperties=-10`,
672 },
673 t: mapType,
674 errorMessage: "maxProperties cannot be negative",
675 },
676 {
677 name: "minimum > maximum",
678 comments: []string{
679 `+k8s:validation:minimum=10.5`,
680 `+k8s:validation:maximum=5.5`,
681 },
682 t: types.Float64,
683 errorMessage: "minimum 10.500000 is greater than maximum 5.500000",
684 },
685 {
686 name: "exclusiveMinimum when minimum == maximum",
687 comments: []string{
688 `+k8s:validation:minimum=10.5`,
689 `+k8s:validation:maximum=10.5`,
690 `+k8s:validation:exclusiveMinimum=true`,
691 },
692 t: types.Float64,
693 errorMessage: "exclusiveMinimum/Maximum cannot be set when minimum == maximum",
694 },
695 {
696 name: "exclusiveMaximum when minimum == maximum",
697 comments: []string{
698 `+k8s:validation:minimum=10.5`,
699 `+k8s:validation:maximum=10.5`,
700 `+k8s:validation:exclusiveMaximum=true`,
701 },
702 t: types.Float64,
703 errorMessage: "exclusiveMinimum/Maximum cannot be set when minimum == maximum",
704 },
705 {
706 name: "minLength > maxLength",
707 comments: []string{
708 `+k8s:validation:minLength=10`,
709 `+k8s:validation:maxLength=5`,
710 },
711 t: types.String,
712 errorMessage: "minLength 10 is greater than maxLength 5",
713 },
714 {
715 name: "minItems > maxItems",
716 comments: []string{
717 `+k8s:validation:minItems=10`,
718 `+k8s:validation:maxItems=5`,
719 },
720 t: arrayType,
721 errorMessage: "minItems 10 is greater than maxItems 5",
722 },
723 {
724 name: "minProperties > maxProperties",
725 comments: []string{
726 `+k8s:validation:minProperties=10`,
727 `+k8s:validation:maxProperties=5`,
728 },
729 t: mapType,
730 errorMessage: "minProperties 10 is greater than maxProperties 5",
731 },
732 {
733 name: "invalid pattern",
734 comments: []string{
735 `+k8s:validation:pattern="([a-z]+"`,
736 },
737 t: types.String,
738 errorMessage: "invalid pattern \"([a-z]+\": error parsing regexp: missing closing ): `([a-z]+`",
739 },
740 {
741 name: "multipleOf = 0",
742 comments: []string{
743 `+k8s:validation:multipleOf=0.0`,
744 },
745 t: types.Int,
746 errorMessage: "multipleOf cannot be 0",
747 },
748 {
749 name: "valid comment tags with no invalid validations",
750 comments: []string{
751 `+k8s:validation:pattern=".*"`,
752 },
753 t: types.String,
754 errorMessage: "",
755 },
756 }
757
758 for _, tc := range testCases {
759 t.Run(tc.name, func(t *testing.T) {
760 _, err := generators.ParseCommentTags(tc.t, tc.comments, "+k8s:validation:")
761 if tc.errorMessage != "" {
762 require.Error(t, err)
763 require.Equal(t, "invalid marker comments: "+tc.errorMessage, err.Error())
764 } else {
765 require.NoError(t, err)
766 }
767 })
768 }
769 }
770
View as plain text