cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.66.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/reporter/comments_test.go

904lines · modecode

1package reporter
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "log/slog"
8 "net/http"
9 "net/http/httptest"
10 "strings"
11 "testing"
12 "time"
13
14 "github.com/neilotoole/slogt"
15 "github.com/stretchr/testify/require"
16
17 "github.com/cloudflare/pint/internal/checks"
18 "github.com/cloudflare/pint/internal/discovery"
19 "github.com/cloudflare/pint/internal/parser"
20)
21
22var (
23 errSummary = errors.New("Summary() error")
24 errList = errors.New("List() error")
25 errCreate = errors.New("Create() error")
26 errDelete = errors.New("Delete() error")
27)
28
29type testCommenter struct {
30 destinations func(context.Context) ([]any, error)
31 summary func(context.Context, any, Summary, []error) error
32 list func(context.Context, any) ([]ExistingComment, error)
33 create func(context.Context, any, PendingComment) error
34 delete func(context.Context, any, ExistingComment) error
35 canCreate func(int) bool
36 isEqual func(ExistingComment, PendingComment) bool
37}
38
39func (tc testCommenter) Describe() string {
40 return "testCommenter"
41}
42
43func (tc testCommenter) Destinations(ctx context.Context) ([]any, error) {
44 return tc.destinations(ctx)
45}
46
47func (tc testCommenter) Summary(ctx context.Context, dst any, s Summary, errs []error) error {
48 return tc.summary(ctx, dst, s, errs)
49}
50
51func (tc testCommenter) List(ctx context.Context, dst any) ([]ExistingComment, error) {
52 return tc.list(ctx, dst)
53}
54
55func (tc testCommenter) Create(ctx context.Context, dst any, comment PendingComment) error {
56 return tc.create(ctx, dst, comment)
57}
58
59func (tc testCommenter) Delete(ctx context.Context, dst any, comment ExistingComment) error {
60 return tc.delete(ctx, dst, comment)
61}
62
63func (tc testCommenter) CanDelete(ExistingComment) bool {
64 return true
65}
66
67func (tc testCommenter) CanCreate(n int) bool {
68 return tc.canCreate(n)
69}
70
71func (tc testCommenter) IsEqual(_ any, e ExistingComment, p PendingComment) bool {
72 return tc.isEqual(e, p)
73}
74
75func TestCommenter(t *testing.T) {
76 p := parser.NewParser(false)
77 mockRules, _ := p.Parse([]byte(`
78- record: target is down
79 expr: up == 0
80- record: sum errors
81 expr: sum(errors) by (job)
82`))
83
84 fooReport := Report{
85 Path: discovery.Path{
86 SymlinkTarget: "foo.txt",
87 Name: "foo.txt",
88 },
89 ModifiedLines: []int{2},
90 Rule: mockRules[0],
91 Problem: checks.Problem{
92 Reporter: "foo",
93 Text: "foo error",
94 Details: "foo details",
95 Lines: parser.LineRange{First: 1, Last: 3},
96 Severity: checks.Fatal,
97 Anchor: checks.AnchorAfter,
98 },
99 }
100 fooComment := ExistingComment{
101 path: "foo.txt",
102 line: 2,
103 text: `:stop_sign: **Fatal** reported by [pint](https://cloudflare.github.io/pint/) **foo** check.
104
105------
106
107foo error
108
109foo details
110
111------
112
113:information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/foo.html).
114`,
115 meta: nil,
116 }
117
118 barReport := Report{
119 Path: discovery.Path{
120 SymlinkTarget: "bar.txt",
121 Name: "bar.txt",
122 },
123 ModifiedLines: []int{1},
124 Rule: mockRules[0],
125 Problem: checks.Problem{
126 Reporter: "bar",
127 Text: "bar warning",
128 Details: "",
129 Lines: parser.LineRange{First: 1, Last: 1},
130 Severity: checks.Warning,
131 Anchor: checks.AnchorBefore,
132 },
133 }
134 barComment := ExistingComment{
135 path: "bar.txt",
136 line: 1,
137 text: `:warning: **Warning** reported by [pint](https://cloudflare.github.io/pint/) **bar** check.
138
139------
140
141bar warning
142
143------
144
145:information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/bar.html).
146`,
147 meta: nil,
148 }
149
150 type testCaseT struct {
151 commenter Commenter
152 checkErr func(t *testing.T, err error)
153 description string
154 reports []Report
155 }
156
157 testCases := []testCaseT{
158 {
159 description: "no-op when there are no destinations",
160 reports: []Report{},
161 commenter: testCommenter{
162 destinations: func(_ context.Context) ([]any, error) {
163 return []any{}, nil
164 },
165 },
166 checkErr: func(t *testing.T, err error) {
167 require.NoError(t, err)
168 },
169 },
170 {
171 description: "stops if List() fails",
172 reports: []Report{},
173 commenter: testCommenter{
174 destinations: func(_ context.Context) ([]any, error) {
175 return []any{1}, nil
176 },
177 summary: func(_ context.Context, _ any, _ Summary, errs []error) error {
178 if len(errs) != 0 {
179 return fmt.Errorf("Expected empty errs, got %v", errs)
180 }
181 return nil
182 },
183 list: func(_ context.Context, _ any) ([]ExistingComment, error) {
184 return nil, errList
185 },
186 },
187 checkErr: func(t *testing.T, err error) {
188 require.ErrorIs(t, err, errList)
189 },
190 },
191 {
192 description: "no-op when all comments already exist",
193 reports: []Report{fooReport, barReport},
194 commenter: testCommenter{
195 destinations: func(_ context.Context) ([]any, error) {
196 return []any{1}, nil
197 },
198 summary: func(_ context.Context, _ any, _ Summary, errs []error) error {
199 if len(errs) != 0 {
200 return fmt.Errorf("Expected empty errs, got %v", errs)
201 }
202 return nil
203 },
204 list: func(_ context.Context, _ any) ([]ExistingComment, error) {
205 return []ExistingComment{fooComment, barComment}, nil
206 },
207 create: func(_ context.Context, _ any, p PendingComment) error {
208 return fmt.Errorf("shouldn't try to create %s:%d", p.path, p.line)
209 },
210 delete: func(_ context.Context, _ any, e ExistingComment) error {
211 return fmt.Errorf("shouldn't try to delete %s:%d", e.path, e.line)
212 },
213 isEqual: func(e ExistingComment, p PendingComment) bool {
214 return e.path == p.path && e.line == p.line && e.text == p.text
215 },
216 canCreate: func(_ int) bool {
217 return true
218 },
219 },
220 checkErr: func(t *testing.T, err error) {
221 require.NoError(t, err)
222 },
223 },
224 {
225 description: "creates missing comments",
226 reports: []Report{fooReport, barReport},
227 commenter: testCommenter{
228 destinations: func(_ context.Context) ([]any, error) {
229 return []any{1}, nil
230 },
231 summary: func(_ context.Context, _ any, _ Summary, errs []error) error {
232 if len(errs) != 0 {
233 return fmt.Errorf("Expected empty errs, got %v", errs)
234 }
235 return nil
236 },
237 list: func(_ context.Context, _ any) ([]ExistingComment, error) {
238 return []ExistingComment{fooComment}, nil
239 },
240 create: func(_ context.Context, _ any, p PendingComment) error {
241 if p.path == barComment.path && p.line == barComment.line && p.text == barComment.text {
242 return nil
243 }
244 return fmt.Errorf("shouldn't try to create %s:%d", p.path, p.line)
245 },
246 delete: func(_ context.Context, _ any, e ExistingComment) error {
247 return fmt.Errorf("shouldn't try to delete %s:%d", e.path, e.line)
248 },
249 isEqual: func(e ExistingComment, p PendingComment) bool {
250 return e.path == p.path && e.line == p.line && e.text == p.text
251 },
252 canCreate: func(_ int) bool {
253 return true
254 },
255 },
256 checkErr: func(t *testing.T, err error) {
257 require.NoError(t, err)
258 },
259 },
260 {
261 description: "skips creating comments when CanCreate() is false",
262 reports: []Report{barReport, fooReport},
263 commenter: testCommenter{
264 destinations: func(_ context.Context) ([]any, error) {
265 return []any{1}, nil
266 },
267 summary: func(_ context.Context, _ any, _ Summary, errs []error) error {
268 if len(errs) != 0 {
269 return fmt.Errorf("Expected empty errs, got %v", errs)
270 }
271 return nil
272 },
273 list: func(_ context.Context, _ any) ([]ExistingComment, error) {
274 return nil, nil
275 },
276 create: func(_ context.Context, _ any, p PendingComment) error {
277 if p.path == barComment.path && p.line == barComment.line && p.text == barComment.text {
278 return nil
279 }
280 return fmt.Errorf("shouldn't try to create %s:%d", p.path, p.line)
281 },
282 delete: func(_ context.Context, _ any, e ExistingComment) error {
283 return fmt.Errorf("shouldn't try to delete %s:%d", e.path, e.line)
284 },
285 isEqual: func(e ExistingComment, p PendingComment) bool {
286 return e.path == p.path && e.line == p.line && e.text == p.text
287 },
288 canCreate: func(n int) bool {
289 return n == 0
290 },
291 },
292 checkErr: func(t *testing.T, err error) {
293 require.NoError(t, err)
294 },
295 },
296 {
297 description: "Create() fails",
298 reports: []Report{barReport, fooReport},
299 commenter: testCommenter{
300 destinations: func(_ context.Context) ([]any, error) {
301 return []any{1}, nil
302 },
303 summary: func(_ context.Context, _ any, _ Summary, errs []error) error {
304 if len(errs) != 0 {
305 return fmt.Errorf("Expected empty errs, got %v", errs)
306 }
307 return nil
308 },
309 list: func(_ context.Context, _ any) ([]ExistingComment, error) {
310 return nil, nil
311 },
312 create: func(_ context.Context, _ any, p PendingComment) error {
313 if p.path == barComment.path && p.line == barComment.line && p.text == barComment.text {
314 return nil
315 }
316 return errCreate
317 },
318 delete: func(_ context.Context, _ any, e ExistingComment) error {
319 return fmt.Errorf("shouldn't try to delete %s:%d", e.path, e.line)
320 },
321 isEqual: func(e ExistingComment, p PendingComment) bool {
322 return e.path == p.path && e.line == p.line && e.text == p.text
323 },
324 canCreate: func(_ int) bool {
325 return true
326 },
327 },
328 checkErr: func(t *testing.T, err error) {
329 require.ErrorIs(t, err, errCreate)
330 },
331 },
332 {
333 description: "Create() works",
334 reports: []Report{barReport, fooReport},
335 commenter: testCommenter{
336 destinations: func(_ context.Context) ([]any, error) {
337 return []any{1}, nil
338 },
339 summary: func(_ context.Context, _ any, _ Summary, errs []error) error {
340 if len(errs) != 0 {
341 return fmt.Errorf("Expected empty errs, got %v", errs)
342 }
343 return nil
344 },
345 list: func(_ context.Context, _ any) ([]ExistingComment, error) {
346 return nil, nil
347 },
348 create: func(_ context.Context, _ any, p PendingComment) error {
349 if p.path == barComment.path && p.line == barComment.line && p.text == barComment.text {
350 return nil
351 }
352 if p.path == fooComment.path && p.line == fooComment.line && p.text == fooComment.text {
353 return nil
354 }
355 return fmt.Errorf("unexpected comment at %s:%d: %s", p.path, p.line, p.text)
356 },
357 delete: func(_ context.Context, _ any, e ExistingComment) error {
358 return fmt.Errorf("shouldn't try to delete %s:%d", e.path, e.line)
359 },
360 isEqual: func(e ExistingComment, p PendingComment) bool {
361 return e.path == p.path && e.line == p.line && e.text == p.text
362 },
363 canCreate: func(_ int) bool {
364 return true
365 },
366 },
367 checkErr: func(t *testing.T, err error) {
368 require.NoError(t, err)
369 },
370 },
371 {
372 description: "Create() merges details",
373 reports: []Report{
374 {
375 Path: discovery.Path{
376 SymlinkTarget: "bar.txt",
377 Name: "foo.txt",
378 },
379 ModifiedLines: []int{2, 3, 4, 5},
380 Rule: mockRules[0],
381 Problem: checks.Problem{
382 Reporter: "foo",
383 Text: "foo error 1",
384 Details: "foo details",
385 Lines: parser.LineRange{First: 1, Last: 3},
386 Severity: checks.Bug,
387 Anchor: checks.AnchorAfter,
388 },
389 },
390 {
391 Path: discovery.Path{
392 SymlinkTarget: "bar.txt",
393 Name: "foo.txt",
394 },
395 ModifiedLines: []int{2, 3, 4, 5},
396 Rule: mockRules[0],
397 Problem: checks.Problem{
398 Reporter: "foo",
399 Text: "foo error 2",
400 Details: "foo details",
401 Lines: parser.LineRange{First: 1, Last: 3},
402 Severity: checks.Bug,
403 Anchor: checks.AnchorAfter,
404 },
405 },
406 },
407 commenter: testCommenter{
408 destinations: func(_ context.Context) ([]any, error) {
409 return []any{1}, nil
410 },
411 summary: func(_ context.Context, _ any, _ Summary, errs []error) error {
412 if len(errs) != 0 {
413 return fmt.Errorf("Expected empty errs, got %v", errs)
414 }
415 return nil
416 },
417 list: func(_ context.Context, _ any) ([]ExistingComment, error) {
418 return nil, nil
419 },
420 create: func(_ context.Context, _ any, p PendingComment) error {
421 if p.path != "bar.txt" {
422 return fmt.Errorf("wrong path: %s", p.path)
423 }
424 if p.line != 3 {
425 return fmt.Errorf("wrong line: %d", p.line)
426 }
427 if p.text != `:stop_sign: **Bug** reported by [pint](https://cloudflare.github.io/pint/) **foo** check.
428
429------
430
431foo error 1
432
433:leftwards_arrow_with_hook: This problem was detected on a symlinked file `+"`foo.txt`"+`.
434
435------
436
437foo error 2
438
439:leftwards_arrow_with_hook: This problem was detected on a symlinked file `+"`foo.txt`"+`.
440
441------
442
443foo details
444
445------
446
447:information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/foo.html).
448` {
449 return fmt.Errorf("wrong text: %s", p.text)
450 }
451 return nil
452 },
453 delete: func(_ context.Context, _ any, e ExistingComment) error {
454 return fmt.Errorf("shouldn't try to delete %s:%d", e.path, e.line)
455 },
456 isEqual: func(e ExistingComment, p PendingComment) bool {
457 return e.path == p.path && e.line == p.line && e.text == p.text
458 },
459 canCreate: func(_ int) bool {
460 return true
461 },
462 },
463 checkErr: func(t *testing.T, err error) {
464 require.NoError(t, err)
465 },
466 },
467 {
468 description: "Create() reports symlink",
469 reports: []Report{
470 {
471 Path: discovery.Path{
472 SymlinkTarget: "bar.txt",
473 Name: "foo.txt",
474 },
475 ModifiedLines: []int{2, 3, 4, 5},
476 Rule: mockRules[0],
477 Problem: checks.Problem{
478 Reporter: "foo",
479 Text: "foo error",
480 Details: "foo details",
481 Lines: parser.LineRange{First: 1, Last: 3},
482 Severity: checks.Bug,
483 Anchor: checks.AnchorAfter,
484 },
485 },
486 },
487 commenter: testCommenter{
488 destinations: func(_ context.Context) ([]any, error) {
489 return []any{1}, nil
490 },
491 summary: func(_ context.Context, _ any, _ Summary, errs []error) error {
492 if len(errs) != 0 {
493 return fmt.Errorf("Expected empty errs, got %v", errs)
494 }
495 return nil
496 },
497 list: func(_ context.Context, _ any) ([]ExistingComment, error) {
498 return nil, nil
499 },
500 create: func(_ context.Context, _ any, p PendingComment) error {
501 if p.path != "bar.txt" {
502 return fmt.Errorf("wrong path: %s", p.path)
503 }
504 if p.line != 3 {
505 return fmt.Errorf("wrong line: %d", p.line)
506 }
507 if p.text != `:stop_sign: **Bug** reported by [pint](https://cloudflare.github.io/pint/) **foo** check.
508
509------
510
511foo error
512
513foo details
514
515:leftwards_arrow_with_hook: This problem was detected on a symlinked file `+"`foo.txt`"+`.
516
517------
518
519:information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/foo.html).
520` {
521 return fmt.Errorf("wrong text: %s", p.text)
522 }
523 return nil
524 },
525 delete: func(_ context.Context, _ any, e ExistingComment) error {
526 return fmt.Errorf("shouldn't try to delete %s:%d", e.path, e.line)
527 },
528 isEqual: func(e ExistingComment, p PendingComment) bool {
529 return e.path == p.path && e.line == p.line && e.text == p.text
530 },
531 canCreate: func(_ int) bool {
532 return true
533 },
534 },
535 checkErr: func(t *testing.T, err error) {
536 require.NoError(t, err)
537 },
538 },
539 {
540 description: "Create() doesn't merge details with different line range",
541 reports: []Report{
542 {
543 Path: discovery.Path{
544 SymlinkTarget: "foo.txt",
545 Name: "foo.txt",
546 },
547 ModifiedLines: []int{2, 3, 4, 5},
548 Rule: mockRules[0],
549 Problem: checks.Problem{
550 Reporter: "foo",
551 Text: "foo error 1",
552 Details: "foo details",
553 Lines: parser.LineRange{First: 1, Last: 3},
554 Severity: checks.Bug,
555 Anchor: checks.AnchorAfter,
556 },
557 },
558 {
559 Path: discovery.Path{
560 SymlinkTarget: "foo.txt",
561 Name: "foo.txt",
562 },
563 ModifiedLines: []int{2, 3, 4, 5},
564 Rule: mockRules[0],
565 Problem: checks.Problem{
566 Reporter: "foo",
567 Text: "foo error 2",
568 Details: "foo details",
569 Lines: parser.LineRange{First: 2, Last: 2},
570 Severity: checks.Bug,
571 Anchor: checks.AnchorAfter,
572 },
573 },
574 },
575 commenter: testCommenter{
576 destinations: func(_ context.Context) ([]any, error) {
577 return []any{1}, nil
578 },
579 summary: func(_ context.Context, _ any, _ Summary, errs []error) error {
580 if len(errs) != 0 {
581 return fmt.Errorf("Expected empty errs, got %v", errs)
582 }
583 return nil
584 },
585 list: func(_ context.Context, _ any) ([]ExistingComment, error) {
586 return nil, nil
587 },
588 create: func(_ context.Context, _ any, p PendingComment) error {
589 if p.path != "foo.txt" {
590 return fmt.Errorf("wrong path: %s", p.path)
591 }
592 if p.line != 2 && p.line != 3 {
593 return fmt.Errorf("wrong line: %d", p.line)
594 }
595 if p.line == 3 && p.text != `:stop_sign: **Bug** reported by [pint](https://cloudflare.github.io/pint/) **foo** check.
596
597------
598
599foo error 1
600
601foo details
602
603------
604
605:information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/foo.html).
606` {
607 return fmt.Errorf("wrong text on first report: %s", p.text)
608 }
609 if p.line == 2 && p.text != `:stop_sign: **Bug** reported by [pint](https://cloudflare.github.io/pint/) **foo** check.
610
611------
612
613foo error 2
614
615foo details
616
617------
618
619:information_source: To see documentation covering this check and instructions on how to resolve it [click here](https://cloudflare.github.io/pint/checks/foo.html).
620` {
621 return fmt.Errorf("wrong text on second report: %s", p.text)
622 }
623 return nil
624 },
625 delete: func(_ context.Context, _ any, e ExistingComment) error {
626 return fmt.Errorf("shouldn't try to delete %s:%d", e.path, e.line)
627 },
628 isEqual: func(e ExistingComment, p PendingComment) bool {
629 return e.path == p.path && e.line == p.line && e.text == p.text
630 },
631 canCreate: func(_ int) bool {
632 return true
633 },
634 },
635 checkErr: func(t *testing.T, err error) {
636 require.NoError(t, err)
637 },
638 },
639 {
640 description: "Delete() fails",
641 reports: []Report{barReport},
642 commenter: testCommenter{
643 destinations: func(_ context.Context) ([]any, error) {
644 return []any{1}, nil
645 },
646 summary: func(_ context.Context, _ any, _ Summary, errs []error) error {
647 if len(errs) != 1 {
648 return fmt.Errorf("Expected errDelete in errs, got %v", errs)
649 }
650 if !errors.Is(errs[0], errDelete) {
651 return fmt.Errorf("Expected errDelete in errs, got %w", errs[0])
652 }
653 return nil
654 },
655 list: func(_ context.Context, _ any) ([]ExistingComment, error) {
656 return []ExistingComment{fooComment}, nil
657 },
658 create: func(_ context.Context, _ any, _ PendingComment) error {
659 return nil
660 },
661 delete: func(_ context.Context, _ any, _ ExistingComment) error {
662 return errDelete
663 },
664 isEqual: func(e ExistingComment, p PendingComment) bool {
665 return e.path == p.path && e.line == p.line && e.text == p.text
666 },
667 canCreate: func(_ int) bool {
668 return true
669 },
670 },
671 checkErr: func(t *testing.T, err error) {
672 require.NoError(t, err)
673 },
674 },
675 {
676 description: "Delete() works",
677 reports: []Report{barReport},
678 commenter: testCommenter{
679 destinations: func(_ context.Context) ([]any, error) {
680 return []any{1}, nil
681 },
682 summary: func(_ context.Context, _ any, _ Summary, errs []error) error {
683 if len(errs) != 0 {
684 return fmt.Errorf("Expected empty errs, got %v", errs)
685 }
686 return nil
687 },
688 list: func(_ context.Context, _ any) ([]ExistingComment, error) {
689 return []ExistingComment{fooComment}, nil
690 },
691 create: func(_ context.Context, _ any, _ PendingComment) error {
692 return nil
693 },
694 delete: func(_ context.Context, _ any, _ ExistingComment) error {
695 return nil
696 },
697 isEqual: func(e ExistingComment, p PendingComment) bool {
698 return e.path == p.path && e.line == p.line && e.text == p.text
699 },
700 canCreate: func(_ int) bool {
701 return true
702 },
703 },
704 checkErr: func(t *testing.T, err error) {
705 require.NoError(t, err)
706 },
707 },
708 {
709 description: "Summary() fails",
710 reports: []Report{},
711 commenter: testCommenter{
712 destinations: func(_ context.Context) ([]any, error) {
713 return []any{1}, nil
714 },
715 summary: func(_ context.Context, _ any, _ Summary, _ []error) error {
716 return errSummary
717 },
718 list: func(_ context.Context, _ any) ([]ExistingComment, error) {
719 return nil, nil
720 },
721 },
722 checkErr: func(t *testing.T, err error) {
723 require.ErrorIs(t, err, errSummary)
724 },
725 },
726 }
727
728 for _, tc := range testCases {
729 t.Run(tc.description, func(t *testing.T) {
730 slog.SetDefault(slogt.New(t))
731
732 summary := NewSummary(tc.reports)
733 tc.checkErr(t, Submit(context.Background(), summary, tc.commenter))
734 })
735 }
736}
737
738func TestCommentsCommonPaths(t *testing.T) {
739 type errorCheck func(err error) error
740
741 type testCaseT struct {
742 httpHandler http.Handler
743 errorHandler errorCheck
744
745 description string
746 branch string
747 token string
748
749 reports []Report
750 timeout time.Duration
751 project int
752 maxComments int
753 }
754
755 p := parser.NewParser(false)
756 mockRules, _ := p.Parse([]byte(`
757- record: target is down
758 expr: up == 0
759- record: sum errors
760 expr: sum(errors) by (job)
761`))
762
763 fooReport := Report{
764 Path: discovery.Path{
765 SymlinkTarget: "foo.txt",
766 Name: "foo.txt",
767 },
768 ModifiedLines: []int{2},
769 Rule: mockRules[0],
770 Problem: checks.Problem{
771 Reporter: "foo",
772 Text: "foo error",
773 Details: "foo details",
774 Lines: parser.LineRange{First: 1, Last: 3},
775 Severity: checks.Fatal,
776 Anchor: checks.AnchorAfter,
777 },
778 }
779
780 testCases := []testCaseT{
781 {
782 description: "returns an error on non-200 HTTP response",
783 branch: "fakeBranch",
784 token: "fakeToken",
785 timeout: time.Second,
786 project: 123,
787 maxComments: 50,
788 reports: []Report{fooReport},
789 httpHandler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
790 w.WriteHeader(400)
791 _, _ = w.Write([]byte("Bad Request"))
792 }),
793 errorHandler: func(err error) error {
794 if err != nil {
795 return nil
796 }
797 return fmt.Errorf("wrong error: %w", err)
798 },
799 },
800 {
801 description: "returns an error on HTTP response timeout",
802 branch: "fakeBranch",
803 token: "fakeToken",
804 timeout: time.Second,
805 project: 123,
806 maxComments: 50,
807 reports: []Report{fooReport},
808 httpHandler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
809 time.Sleep(time.Second * 2)
810 w.WriteHeader(400)
811 _, _ = w.Write([]byte("Bad Request"))
812 }),
813 errorHandler: func(err error) error {
814 if err != nil && strings.HasSuffix(err.Error(), "context deadline exceeded") {
815 return nil
816 }
817 return fmt.Errorf("wrong error: %w", err)
818 },
819 },
820 {
821 description: "returns an error on non-json body",
822 branch: "fakeBranch",
823 token: "fakeToken",
824 timeout: time.Second,
825 project: 123,
826 maxComments: 50,
827 reports: []Report{fooReport},
828 httpHandler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
829 w.WriteHeader(200)
830 _, _ = w.Write([]byte("OK"))
831 }),
832 errorHandler: func(err error) error {
833 if err != nil {
834 return nil
835 }
836 return fmt.Errorf("wrong error: %w", err)
837 },
838 },
839 {
840 description: "returns an error on empty JSON body",
841 branch: "fakeBranch",
842 token: "fakeToken",
843 timeout: time.Second,
844 project: 123,
845 maxComments: 50,
846 reports: []Report{fooReport},
847 httpHandler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
848 w.WriteHeader(200)
849 _, _ = w.Write([]byte("{}"))
850 }),
851 errorHandler: func(err error) error {
852 if err != nil {
853 return nil
854 }
855 return fmt.Errorf("wrong error: %w", err)
856 },
857 },
858 }
859
860 for _, tc := range testCases {
861 for _, c := range []func(string) Commenter{
862 func(uri string) Commenter {
863 r, err := NewGitLabReporter(
864 "v0.0.0",
865 tc.branch,
866 uri,
867 tc.timeout,
868 tc.token,
869 tc.project,
870 tc.maxComments,
871 )
872 require.NoError(t, err, "can't create gitlab reporter")
873 return r
874 },
875 func(uri string) Commenter {
876 r, err := NewGithubReporter(
877 "v0.0.0",
878 uri,
879 uri,
880 tc.timeout,
881 tc.token,
882 "owner",
883 "repo",
884 123,
885 tc.maxComments,
886 "fake-commit-id",
887 )
888 require.NoError(t, err, "can't create gitlab reporter")
889 return r
890 },
891 } {
892 t.Run(tc.description, func(t *testing.T) {
893 slog.SetDefault(slogt.New(t))
894
895 srv := httptest.NewServer(tc.httpHandler)
896 defer srv.Close()
897
898 summary := NewSummary(tc.reports)
899 err := Submit(context.Background(), summary, c(srv.URL))
900 require.NoError(t, tc.errorHandler(err))
901 })
902 }
903 }
904}
905