cloudflare/pint

Public

mirrored from https://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.81.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

internal/comments/comments_test.go

883lines · modecode

1package comments_test
2
3import (
4 "fmt"
5 "testing"
6 "time"
7
8 "github.com/cloudflare/pint/internal/comments"
9 "github.com/cloudflare/pint/internal/diags"
10
11 "github.com/stretchr/testify/require"
12)
13
14func TestParse(t *testing.T) {
15 type testCaseT struct {
16 input string
17 output []comments.Comment
18 }
19
20 parseUntil := func(s string) time.Time {
21 until, err := time.Parse(time.RFC3339, s)
22 require.NoError(t, err)
23 return until
24 }
25
26 errUntil := func(s string) error {
27 _, err := time.Parse("2006-01-02", s)
28 require.Error(t, err)
29 return err
30 }
31
32 testCases := []testCaseT{
33 {
34 input: "code\n",
35 },
36 {
37 input: "code # bob\n",
38 },
39 {
40 input: "code # bob\ncode # alice\n",
41 },
42 {
43 input: "# pint bamboozle me this",
44 },
45 {
46 input: "# pint/xxx bamboozle me this",
47 },
48 {
49 input: "# pint bambo[]ozle me this",
50 },
51 {
52 input: "# pint ignore/file \t this file",
53 output: []comments.Comment{
54 {
55 Type: comments.InvalidComment,
56 Value: comments.Invalid{
57 Err: comments.CommentError{
58 Diagnostic: diags.Diagnostic{
59 Message: `This comment is not a valid pint control comment: unexpected comment suffix: "this file"`,
60 Pos: diags.PositionRanges{
61 {
62 Line: 1,
63 FirstColumn: 1,
64 LastColumn: 30,
65 },
66 },
67 FirstColumn: 1,
68 LastColumn: 30,
69 Kind: diags.Issue,
70 },
71 },
72 },
73 },
74 },
75 },
76 {
77 input: "# pint ignore/file",
78 output: []comments.Comment{
79 {Type: comments.IgnoreFileType},
80 },
81 },
82 {
83 input: "# pint ignore/line this line",
84 output: []comments.Comment{
85 {
86 Type: comments.InvalidComment,
87 Value: comments.Invalid{
88 Err: comments.CommentError{
89 Diagnostic: diags.Diagnostic{
90 Message: `This comment is not a valid pint control comment: unexpected comment suffix: "this line"`,
91 Pos: diags.PositionRanges{
92 {
93 Line: 1,
94 FirstColumn: 1,
95 LastColumn: 28,
96 },
97 },
98 FirstColumn: 1,
99 LastColumn: 28,
100 },
101 },
102 },
103 },
104 },
105 },
106 {
107 input: "# pint ignore/line",
108 output: []comments.Comment{
109 {Type: comments.IgnoreLineType},
110 },
111 },
112 {
113 input: "# pint ignore/begin here",
114 output: []comments.Comment{
115 {
116 Type: comments.InvalidComment,
117 Value: comments.Invalid{
118 Err: comments.CommentError{
119 Diagnostic: diags.Diagnostic{
120 Message: `This comment is not a valid pint control comment: unexpected comment suffix: "here"`,
121 Pos: diags.PositionRanges{
122 {
123 Line: 1,
124 FirstColumn: 1,
125 LastColumn: 24,
126 },
127 },
128 FirstColumn: 1,
129 LastColumn: 24,
130 Kind: diags.Issue,
131 },
132 },
133 },
134 },
135 },
136 },
137 {
138 input: "# pint ignore/begin",
139 output: []comments.Comment{
140 {Type: comments.IgnoreBeginType},
141 },
142 },
143 {
144 input: "# pint ignore/end here",
145 output: []comments.Comment{
146 {
147 Type: comments.InvalidComment,
148 Value: comments.Invalid{
149 Err: comments.CommentError{
150 Diagnostic: diags.Diagnostic{
151 Message: `This comment is not a valid pint control comment: unexpected comment suffix: "here"`,
152 Pos: diags.PositionRanges{
153 {
154 Line: 1,
155 FirstColumn: 1,
156 LastColumn: 22,
157 },
158 },
159 FirstColumn: 1,
160 LastColumn: 22,
161 Kind: diags.Issue,
162 },
163 },
164 },
165 },
166 },
167 },
168 {
169 input: "# pint ignore/end",
170 output: []comments.Comment{
171 {Type: comments.IgnoreEndType},
172 },
173 },
174 {
175 input: "# pint ignore/next-line\there",
176 output: []comments.Comment{
177 {
178 Type: comments.InvalidComment,
179 Value: comments.Invalid{
180 Err: comments.CommentError{
181 Diagnostic: diags.Diagnostic{
182 Message: `This comment is not a valid pint control comment: unexpected comment suffix: "here"`,
183 Pos: diags.PositionRanges{
184 {
185 Line: 1,
186 FirstColumn: 1,
187 LastColumn: 30,
188 },
189 },
190 FirstColumn: 1,
191 LastColumn: 30,
192 Kind: diags.Issue,
193 },
194 },
195 },
196 },
197 },
198 },
199 {
200 input: "# pint ignore/next-line",
201 output: []comments.Comment{
202 {Type: comments.IgnoreNextLineType},
203 },
204 },
205 {
206 input: "# pint file/owner",
207 output: []comments.Comment{
208 {
209 Type: comments.InvalidComment,
210 Value: comments.Invalid{
211 Err: comments.CommentError{
212 Diagnostic: diags.Diagnostic{
213 Message: `This comment is not a valid pint control comment: missing file/owner value`,
214 Pos: diags.PositionRanges{
215 {
216 Line: 1,
217 FirstColumn: 1,
218 LastColumn: 19,
219 },
220 },
221 FirstColumn: 1,
222 LastColumn: 19,
223 Kind: diags.Issue,
224 },
225 },
226 },
227 },
228 },
229 },
230 {
231 input: "# pint file/owner bob and alice",
232 output: []comments.Comment{
233 {
234 Type: comments.FileOwnerType,
235 Value: comments.Owner{
236 Name: "bob and alice",
237 Line: 1,
238 },
239 },
240 },
241 },
242 {
243 input: "# pint rule/owner",
244 output: []comments.Comment{
245 {
246 Type: comments.InvalidComment,
247 Value: comments.Invalid{
248 Err: comments.CommentError{
249 Diagnostic: diags.Diagnostic{
250 Message: `This comment is not a valid pint control comment: missing rule/owner value`,
251 Pos: diags.PositionRanges{
252 {
253 Line: 1,
254 FirstColumn: 1,
255 LastColumn: 19,
256 },
257 },
258 FirstColumn: 1,
259 LastColumn: 19,
260 Kind: diags.Issue,
261 },
262 },
263 },
264 },
265 },
266 },
267 {
268 input: "# pint rule/owner bob and alice",
269 output: []comments.Comment{
270 {
271 Type: comments.RuleOwnerType,
272 Value: comments.Owner{
273 Name: "bob and alice",
274 },
275 },
276 },
277 },
278 {
279 input: "# pint file/disable",
280 output: []comments.Comment{
281 {
282 Type: comments.InvalidComment,
283 Value: comments.Invalid{
284 Err: comments.CommentError{
285 Diagnostic: diags.Diagnostic{
286 Message: `This comment is not a valid pint control comment: missing file/disable value`,
287 Pos: diags.PositionRanges{
288 {
289 Line: 1,
290 FirstColumn: 1,
291 LastColumn: 21,
292 },
293 },
294 FirstColumn: 1,
295 LastColumn: 21,
296 Kind: diags.Issue,
297 },
298 },
299 },
300 },
301 },
302 },
303 {
304 input: `# pint file/disable promql/series(http_errors_total{label="this has spaces"})`,
305 output: []comments.Comment{
306 {
307 Type: comments.FileDisableType,
308 Value: comments.Disable{Match: `promql/series(http_errors_total{label="this has spaces"})`},
309 },
310 },
311 },
312 {
313 input: "# pint disable",
314 output: []comments.Comment{
315 {
316 Type: comments.InvalidComment,
317 Value: comments.Invalid{
318 Err: comments.CommentError{
319 Diagnostic: diags.Diagnostic{
320 Message: `This comment is not a valid pint control comment: missing disable value`,
321 Pos: diags.PositionRanges{
322 {
323 Line: 1,
324 FirstColumn: 1,
325 LastColumn: 16,
326 },
327 },
328 FirstColumn: 1,
329 LastColumn: 16,
330 Kind: diags.Issue,
331 },
332 },
333 },
334 },
335 },
336 },
337 {
338 input: `# pint disable promql/series(http_errors_total{label="this has spaces"})`,
339 output: []comments.Comment{
340 {
341 Type: comments.DisableType,
342 Value: comments.Disable{Match: `promql/series(http_errors_total{label="this has spaces"})`},
343 },
344 },
345 },
346 {
347 input: `# pint disable promql/series(http_errors_total{label="this has spaces and a # symbol"})`,
348 output: []comments.Comment{
349 {
350 Type: comments.DisableType,
351 Value: comments.Disable{Match: `promql/series(http_errors_total{label="this has spaces and a # symbol"})`},
352 },
353 },
354 },
355 {
356 input: "# pint file/snooze",
357 output: []comments.Comment{
358 {
359 Type: comments.InvalidComment,
360 Value: comments.Invalid{
361 Err: comments.CommentError{
362 Diagnostic: diags.Diagnostic{
363 Message: `This comment is not a valid pint control comment: missing file/snooze value`,
364 Pos: diags.PositionRanges{
365 {
366 Line: 1,
367 FirstColumn: 1,
368 LastColumn: 20,
369 },
370 },
371 FirstColumn: 1,
372 LastColumn: 20,
373 Kind: diags.Issue,
374 },
375 },
376 },
377 },
378 },
379 },
380 {
381 input: "# pint file/snooze 2023-12-31",
382 output: []comments.Comment{
383 {
384 Type: comments.InvalidComment,
385 Value: comments.Invalid{
386 Err: comments.CommentError{
387 Diagnostic: diags.Diagnostic{
388 Message: `This comment is not a valid pint control comment: invalid snooze comment, expected '$TIME $MATCH' got "2023-12-31"`,
389 Pos: diags.PositionRanges{
390 {
391 Line: 1,
392 FirstColumn: 1,
393 LastColumn: 31,
394 },
395 },
396 FirstColumn: 1,
397 LastColumn: 31,
398 Kind: diags.Issue,
399 },
400 },
401 },
402 },
403 },
404 },
405 {
406 input: "# pint file/snooze abc",
407 output: []comments.Comment{
408 {
409 Type: comments.InvalidComment,
410 Value: comments.Invalid{
411 Err: comments.CommentError{
412 Diagnostic: diags.Diagnostic{
413 Message: `This comment is not a valid pint control comment: invalid snooze comment, expected '$TIME $MATCH' got "abc"`,
414 Pos: diags.PositionRanges{
415 {
416 Line: 1,
417 FirstColumn: 1,
418 LastColumn: 24,
419 },
420 },
421 FirstColumn: 1,
422 LastColumn: 24,
423 Kind: diags.Issue,
424 },
425 },
426 },
427 },
428 },
429 },
430 {
431 input: `# pint file/snooze 2023-1231 promql/series(http_errors_total{label="this has spaces"})`,
432 output: []comments.Comment{
433 {
434 Type: comments.InvalidComment,
435 Value: comments.Invalid{Err: comments.CommentError{
436 Diagnostic: diags.Diagnostic{
437 Message: fmt.Sprintf("This comment is not a valid pint control comment: invalid snooze timestamp: %s", errUntil("2023-1231")),
438 Pos: diags.PositionRanges{
439 {
440 Line: 1,
441 FirstColumn: 1,
442 LastColumn: 86,
443 },
444 },
445 FirstColumn: 1,
446 LastColumn: 86,
447 Kind: diags.Issue,
448 },
449 }},
450 },
451 },
452 },
453 {
454 input: `# pint file/snooze 2023-12-31 promql/series(http_errors_total{label="this has spaces"})`,
455 output: []comments.Comment{
456 {
457 Type: comments.FileSnoozeType,
458 Value: comments.Snooze{
459 Until: parseUntil("2023-12-31T00:00:00Z"),
460 Match: `promql/series(http_errors_total{label="this has spaces"})`,
461 },
462 },
463 },
464 },
465 {
466 input: "# pint snooze",
467 output: []comments.Comment{
468 {
469 Type: comments.InvalidComment,
470 Value: comments.Invalid{
471 Err: comments.CommentError{
472 Diagnostic: diags.Diagnostic{
473 Message: `This comment is not a valid pint control comment: missing snooze value`,
474 Pos: diags.PositionRanges{
475 {
476 Line: 1,
477 FirstColumn: 1,
478 LastColumn: 15,
479 },
480 },
481 FirstColumn: 1,
482 LastColumn: 15,
483 Kind: diags.Issue,
484 },
485 },
486 },
487 },
488 },
489 },
490 {
491 input: "# pint snooze 2023-12-31",
492 output: []comments.Comment{
493 {
494 Type: comments.InvalidComment,
495 Value: comments.Invalid{
496 Err: comments.CommentError{
497 Diagnostic: diags.Diagnostic{
498 Message: `This comment is not a valid pint control comment: invalid snooze comment, expected '$TIME $MATCH' got "2023-12-31"`,
499 Pos: diags.PositionRanges{
500 {
501 Line: 1,
502 FirstColumn: 1,
503 LastColumn: 26,
504 },
505 },
506 FirstColumn: 1,
507 LastColumn: 26,
508 Kind: diags.Issue,
509 },
510 },
511 },
512 },
513 },
514 },
515 {
516 input: "# pint snooze abc",
517 output: []comments.Comment{
518 {
519 Type: comments.InvalidComment,
520 Value: comments.Invalid{
521 Err: comments.CommentError{
522 Diagnostic: diags.Diagnostic{
523 Message: `This comment is not a valid pint control comment: invalid snooze comment, expected '$TIME $MATCH' got "abc"`,
524 Pos: diags.PositionRanges{
525 {
526 Line: 1,
527 FirstColumn: 1,
528 LastColumn: 19,
529 },
530 },
531 FirstColumn: 1,
532 LastColumn: 19,
533 Kind: diags.Issue,
534 },
535 },
536 },
537 },
538 },
539 },
540 {
541 input: `# pint snooze 2023-1231 promql/series(http_errors_total{label="this has spaces"})`,
542 output: []comments.Comment{
543 {
544 Type: comments.InvalidComment,
545 Value: comments.Invalid{Err: comments.CommentError{
546 Diagnostic: diags.Diagnostic{
547 Message: fmt.Sprintf("This comment is not a valid pint control comment: invalid snooze timestamp: %s", errUntil("2023-1231")),
548 Pos: diags.PositionRanges{
549 {
550 Line: 1,
551 FirstColumn: 1,
552 LastColumn: 81,
553 },
554 },
555 FirstColumn: 1,
556 LastColumn: 81,
557 Kind: diags.Issue,
558 },
559 }},
560 },
561 },
562 },
563 {
564 input: `# pint snooze 2023-12-31 promql/series(http_errors_total{label="this has spaces"})`,
565 output: []comments.Comment{
566 {
567 Type: comments.SnoozeType,
568 Value: comments.Snooze{
569 Until: parseUntil("2023-12-31T00:00:00Z"),
570 Match: `promql/series(http_errors_total{label="this has spaces"})`,
571 },
572 },
573 },
574 },
575 {
576 input: `# pint snooze 2023-12-31 promql/series(http_errors_total{label="this has spaces"})`,
577 output: []comments.Comment{
578 {
579 Type: comments.SnoozeType,
580 Value: comments.Snooze{
581 Until: parseUntil("2023-12-31T00:00:00Z"),
582 Match: `promql/series(http_errors_total{label="this has spaces"})`,
583 },
584 },
585 },
586 },
587 {
588 input: "# pint rule/set",
589 output: []comments.Comment{
590 {
591 Type: comments.InvalidComment,
592 Value: comments.Invalid{
593 Err: comments.CommentError{
594 Diagnostic: diags.Diagnostic{
595 Message: `This comment is not a valid pint control comment: missing rule/set value`,
596 Pos: diags.PositionRanges{
597 {
598 Line: 1,
599 FirstColumn: 1,
600 LastColumn: 17,
601 },
602 },
603 FirstColumn: 1,
604 LastColumn: 17,
605 Kind: diags.Issue,
606 },
607 },
608 },
609 },
610 },
611 },
612 {
613 input: "# pint rule/set bob and alice",
614 output: []comments.Comment{
615 {
616 Type: comments.RuleSetType,
617 Value: comments.RuleSet{Value: "bob and alice"},
618 },
619 },
620 },
621 {
622 input: "code # pint disable xxx \ncode # alice\n",
623 output: []comments.Comment{
624 {
625 Type: comments.DisableType,
626 Value: comments.Disable{Match: "xxx"},
627 Offset: len("code "),
628 },
629 },
630 },
631 {
632 input: "code # pint disable xxx yyy \n # pint\tfile/owner bob",
633 output: []comments.Comment{
634 {
635 Type: comments.DisableType,
636 Value: comments.Disable{Match: "xxx yyy"},
637 Offset: len("code "),
638 },
639 {
640 Type: comments.FileOwnerType,
641 Value: comments.Owner{
642 Name: "bob",
643 Line: 2,
644 },
645 Offset: 1,
646 },
647 },
648 },
649 {
650 input: "# pint rule/set promql/series(found) min-age foo",
651 output: []comments.Comment{
652 {
653 Type: comments.RuleSetType,
654 Value: comments.RuleSet{Value: "promql/series(found) min-age foo"},
655 },
656 },
657 },
658 {
659 input: "{#- comment #} # pint ignore/line",
660 output: []comments.Comment{
661 {
662 Type: comments.IgnoreLineType,
663 Offset: len("{#- comment #} "),
664 },
665 },
666 },
667 {
668 input: "{# comment #} # pint ignore/line",
669 output: []comments.Comment{
670 {
671 Type: comments.IgnoreLineType,
672 Offset: len("{# comment #} "),
673 },
674 },
675 },
676 {
677 input: "#pint # pint # pint boo # pint ignore/line",
678 output: []comments.Comment{
679 {
680 Type: comments.IgnoreLineType,
681 Offset: len("#pint # pint # pint boo "),
682 },
683 },
684 },
685 {
686 input: "{# comment #} # pint ignore/line # pint ignore/file",
687 output: []comments.Comment{
688 {
689 Type: comments.InvalidComment,
690 Value: comments.Invalid{
691 Err: comments.CommentError{
692 Diagnostic: diags.Diagnostic{
693 Message: `This comment is not a valid pint control comment: unexpected comment suffix: "# pint ignore/file"`,
694 Pos: diags.PositionRanges{
695 {
696 Line: 1,
697 FirstColumn: 15,
698 LastColumn: 51,
699 },
700 },
701 FirstColumn: 1,
702 LastColumn: 51,
703 Kind: diags.Issue,
704 },
705 },
706 },
707 Offset: 14,
708 },
709 },
710 },
711 {
712 input: "{#- JIRA-12345: foo<->bar example comment ' -#} # pint ignore/line",
713 output: []comments.Comment{
714 {
715 Type: comments.IgnoreLineType,
716 Offset: len("{#- JIRA-12345: foo<->bar example comment ' -#} "),
717 },
718 },
719 },
720 // CRLF line endings must be handled the same as LF.
721 {
722 input: "# pint ignore/file\r\n",
723 output: []comments.Comment{
724 {Type: comments.IgnoreFileType},
725 },
726 },
727 {
728 input: "# pint file/owner bob\r\n",
729 output: []comments.Comment{
730 {
731 Type: comments.FileOwnerType,
732 Value: comments.Owner{
733 Name: "bob",
734 Line: 1,
735 },
736 },
737 },
738 },
739 {
740 input: "code # pint disable xxx\r\n # pint\tfile/owner bob\r\n",
741 output: []comments.Comment{
742 {
743 Type: comments.DisableType,
744 Value: comments.Disable{Match: "xxx"},
745 Offset: len("code "),
746 },
747 {
748 Type: comments.FileOwnerType,
749 Value: comments.Owner{
750 Name: "bob",
751 Line: 2,
752 },
753 Offset: 1,
754 },
755 },
756 },
757 {
758 input: "# pint rule/set promql/series(found)\r\n",
759 output: []comments.Comment{
760 {
761 Type: comments.RuleSetType,
762 Value: comments.RuleSet{Value: "promql/series(found)"},
763 },
764 },
765 },
766 {
767 input: "# pint ignore/line\r",
768 output: []comments.Comment{
769 {Type: comments.IgnoreLineType},
770 },
771 },
772 }
773
774 for _, tc := range testCases {
775 t.Run(tc.input, func(t *testing.T) {
776 output := comments.Parse(1, tc.input)
777 require.Equal(t, tc.output, output)
778 })
779 }
780}
781
782func TestCommentValueString(t *testing.T) {
783 type testCaseT struct {
784 comment comments.CommentValue
785 expected string
786 }
787
788 parseUntil := func(s string) time.Time {
789 until, err := time.Parse(time.RFC3339, s)
790 require.NoError(t, err)
791 return until
792 }
793
794 testCases := []testCaseT{
795 {
796 comment: comments.Invalid{Err: comments.CommentError{
797 Diagnostic: diags.Diagnostic{
798 Message: "foo bar",
799 Pos: diags.PositionRanges{
800 {
801 Line: 1,
802 FirstColumn: 1,
803 LastColumn: 100,
804 },
805 },
806 FirstColumn: 1,
807 LastColumn: 100,
808 Kind: diags.Issue,
809 },
810 }},
811 expected: "foo bar",
812 },
813 {
814 comment: comments.Invalid{Err: comments.CommentError{
815 Diagnostic: diags.Diagnostic{
816 Message: "foo bar",
817 Pos: diags.PositionRanges{
818 {
819 Line: 1,
820 FirstColumn: 1,
821 LastColumn: 100,
822 },
823 },
824 FirstColumn: 1,
825 LastColumn: 100,
826 Kind: diags.Issue,
827 },
828 }},
829 expected: "foo bar",
830 },
831 {
832 comment: comments.Owner{Name: "bob & alice"},
833 expected: "bob & alice",
834 },
835 {
836 comment: comments.Disable{Match: `promql/series({code="500"})`},
837 expected: `promql/series({code="500"})`,
838 },
839 {
840 comment: comments.RuleSet{Value: "bob & alice"},
841 expected: "bob & alice",
842 },
843 {
844 comment: comments.Snooze{Match: `promql/series({code="500"})`, Until: parseUntil("2023-11-28T00:00:00Z")},
845 expected: `2023-11-28T00:00:00Z promql/series({code="500"})`,
846 },
847 }
848
849 for _, tc := range testCases {
850 t.Run(tc.expected, func(t *testing.T) {
851 require.Equal(t, tc.expected, tc.comment.String())
852 })
853 }
854}
855
856func TestIsRuleComment(t *testing.T) {
857 type testCaseT struct {
858 ctype comments.Type
859 expected bool
860 }
861
862 testCases := []testCaseT{
863 {comments.RuleOwnerType, true},
864 {comments.DisableType, true},
865 {comments.SnoozeType, true},
866 {comments.RuleSetType, true},
867 {comments.IgnoreLineType, false},
868 {comments.FileOwnerType, false},
869 }
870
871 for _, tc := range testCases {
872 t.Run(fmt.Sprintf("%v", tc.ctype), func(t *testing.T) {
873 require.Equal(t, tc.expected, comments.IsRuleComment(tc.ctype))
874 })
875 }
876}
877
878func TestOwnerError(t *testing.T) {
879 oe := comments.OwnerError{
880 Diagnostic: diags.Diagnostic{Message: "test error"},
881 }
882 require.Equal(t, "test error", oe.Error())
883}
884