1// Copyright 2016 The go-github AUTHORS. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file.
5
6package github
7
8import (
9	"context"
10	"encoding/json"
11	"fmt"
12	"net/http"
13	"reflect"
14	"testing"
15)
16
17func TestPullRequestsService_ListReviews(t *testing.T) {
18	client, mux, _, teardown := setup()
19	defer teardown()
20
21	mux.HandleFunc("/repos/o/r/pulls/1/reviews", func(w http.ResponseWriter, r *http.Request) {
22		testMethod(t, r, "GET")
23		testFormValues(t, r, values{
24			"page": "2",
25		})
26		fmt.Fprint(w, `[{"id":1},{"id":2}]`)
27	})
28
29	opt := &ListOptions{Page: 2}
30	ctx := context.Background()
31	reviews, _, err := client.PullRequests.ListReviews(ctx, "o", "r", 1, opt)
32	if err != nil {
33		t.Errorf("PullRequests.ListReviews returned error: %v", err)
34	}
35
36	want := []*PullRequestReview{
37		{ID: Int64(1)},
38		{ID: Int64(2)},
39	}
40	if !reflect.DeepEqual(reviews, want) {
41		t.Errorf("PullRequests.ListReviews returned %+v, want %+v", reviews, want)
42	}
43
44	const methodName = "ListReviews"
45	testBadOptions(t, methodName, func() (err error) {
46		_, _, err = client.PullRequests.ListReviews(ctx, "\n", "\n", -1, opt)
47		return err
48	})
49
50	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
51		got, resp, err := client.PullRequests.ListReviews(ctx, "o", "r", 1, opt)
52		if got != nil {
53			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
54		}
55		return resp, err
56	})
57}
58
59func TestPullRequestsService_ListReviews_invalidOwner(t *testing.T) {
60	client, _, _, teardown := setup()
61	defer teardown()
62
63	ctx := context.Background()
64	_, _, err := client.PullRequests.ListReviews(ctx, "%", "r", 1, nil)
65	testURLParseError(t, err)
66}
67
68func TestPullRequestsService_GetReview(t *testing.T) {
69	client, mux, _, teardown := setup()
70	defer teardown()
71
72	mux.HandleFunc("/repos/o/r/pulls/1/reviews/1", func(w http.ResponseWriter, r *http.Request) {
73		testMethod(t, r, "GET")
74		fmt.Fprint(w, `{"id":1}`)
75	})
76
77	ctx := context.Background()
78	review, _, err := client.PullRequests.GetReview(ctx, "o", "r", 1, 1)
79	if err != nil {
80		t.Errorf("PullRequests.GetReview returned error: %v", err)
81	}
82
83	want := &PullRequestReview{ID: Int64(1)}
84	if !reflect.DeepEqual(review, want) {
85		t.Errorf("PullRequests.GetReview returned %+v, want %+v", review, want)
86	}
87
88	const methodName = "GetReview"
89	testBadOptions(t, methodName, func() (err error) {
90		_, _, err = client.PullRequests.GetReview(ctx, "\n", "\n", -1, -1)
91		return err
92	})
93
94	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
95		got, resp, err := client.PullRequests.GetReview(ctx, "o", "r", 1, 1)
96		if got != nil {
97			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
98		}
99		return resp, err
100	})
101}
102
103func TestPullRequestsService_GetReview_invalidOwner(t *testing.T) {
104	client, _, _, teardown := setup()
105	defer teardown()
106
107	ctx := context.Background()
108	_, _, err := client.PullRequests.GetReview(ctx, "%", "r", 1, 1)
109	testURLParseError(t, err)
110}
111
112func TestPullRequestsService_DeletePendingReview(t *testing.T) {
113	client, mux, _, teardown := setup()
114	defer teardown()
115
116	mux.HandleFunc("/repos/o/r/pulls/1/reviews/1", func(w http.ResponseWriter, r *http.Request) {
117		testMethod(t, r, "DELETE")
118		fmt.Fprint(w, `{"id":1}`)
119	})
120
121	ctx := context.Background()
122	review, _, err := client.PullRequests.DeletePendingReview(ctx, "o", "r", 1, 1)
123	if err != nil {
124		t.Errorf("PullRequests.DeletePendingReview returned error: %v", err)
125	}
126
127	want := &PullRequestReview{ID: Int64(1)}
128	if !reflect.DeepEqual(review, want) {
129		t.Errorf("PullRequests.DeletePendingReview returned %+v, want %+v", review, want)
130	}
131
132	const methodName = "DeletePendingReview"
133	testBadOptions(t, methodName, func() (err error) {
134		_, _, err = client.PullRequests.DeletePendingReview(ctx, "\n", "\n", -1, -1)
135		return err
136	})
137
138	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
139		got, resp, err := client.PullRequests.DeletePendingReview(ctx, "o", "r", 1, 1)
140		if got != nil {
141			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
142		}
143		return resp, err
144	})
145}
146
147func TestPullRequestsService_DeletePendingReview_invalidOwner(t *testing.T) {
148	client, _, _, teardown := setup()
149	defer teardown()
150
151	ctx := context.Background()
152	_, _, err := client.PullRequests.DeletePendingReview(ctx, "%", "r", 1, 1)
153	testURLParseError(t, err)
154}
155
156func TestPullRequestsService_ListReviewComments(t *testing.T) {
157	client, mux, _, teardown := setup()
158	defer teardown()
159
160	mux.HandleFunc("/repos/o/r/pulls/1/reviews/1/comments", func(w http.ResponseWriter, r *http.Request) {
161		testMethod(t, r, "GET")
162		fmt.Fprint(w, `[{"id":1},{"id":2}]`)
163	})
164
165	ctx := context.Background()
166	comments, _, err := client.PullRequests.ListReviewComments(ctx, "o", "r", 1, 1, nil)
167	if err != nil {
168		t.Errorf("PullRequests.ListReviewComments returned error: %v", err)
169	}
170
171	want := []*PullRequestComment{
172		{ID: Int64(1)},
173		{ID: Int64(2)},
174	}
175	if !reflect.DeepEqual(comments, want) {
176		t.Errorf("PullRequests.ListReviewComments returned %+v, want %+v", comments, want)
177	}
178
179	const methodName = "ListReviewComments"
180	testBadOptions(t, methodName, func() (err error) {
181		_, _, err = client.PullRequests.ListReviewComments(ctx, "\n", "\n", -1, -1, nil)
182		return err
183	})
184
185	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
186		got, resp, err := client.PullRequests.ListReviewComments(ctx, "o", "r", 1, 1, nil)
187		if got != nil {
188			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
189		}
190		return resp, err
191	})
192}
193
194func TestPullRequestsService_ListReviewComments_withOptions(t *testing.T) {
195	client, mux, _, teardown := setup()
196	defer teardown()
197
198	mux.HandleFunc("/repos/o/r/pulls/1/reviews/1/comments", func(w http.ResponseWriter, r *http.Request) {
199		testMethod(t, r, "GET")
200		testFormValues(t, r, values{
201			"page": "2",
202		})
203		fmt.Fprint(w, `[]`)
204	})
205
206	ctx := context.Background()
207	_, _, err := client.PullRequests.ListReviewComments(ctx, "o", "r", 1, 1, &ListOptions{Page: 2})
208	if err != nil {
209		t.Errorf("PullRequests.ListReviewComments returned error: %v", err)
210	}
211
212	const methodName = "ListReviewComments"
213	testBadOptions(t, methodName, func() (err error) {
214		_, _, err = client.PullRequests.ListReviewComments(ctx, "\n", "\n", -1, -1, &ListOptions{Page: 2})
215		return err
216	})
217
218	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
219		got, resp, err := client.PullRequests.ListReviewComments(ctx, "o", "r", 1, 1, &ListOptions{Page: 2})
220		if got != nil {
221			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
222		}
223		return resp, err
224	})
225}
226
227func TestPullRequestReviewRequest_isComfortFadePreview(t *testing.T) {
228	path := "path/to/file.go"
229	body := "this is a comment body"
230	left, right := "LEFT", "RIGHT"
231	pos1, pos2, pos3 := 1, 2, 3
232	line1, line2, line3 := 11, 22, 33
233
234	tests := []struct {
235		name     string
236		review   *PullRequestReviewRequest
237		wantErr  error
238		wantBool bool
239	}{{
240		name:     "empty review",
241		review:   &PullRequestReviewRequest{},
242		wantBool: false,
243	}, {
244		name:     "nil comment",
245		review:   &PullRequestReviewRequest{Comments: []*DraftReviewComment{nil}},
246		wantBool: false,
247	}, {
248		name: "old-style review",
249		review: &PullRequestReviewRequest{
250			Comments: []*DraftReviewComment{{
251				Path:     &path,
252				Body:     &body,
253				Position: &pos1,
254			}, {
255				Path:     &path,
256				Body:     &body,
257				Position: &pos2,
258			}, {
259				Path:     &path,
260				Body:     &body,
261				Position: &pos3,
262			}},
263		},
264		wantBool: false,
265	}, {
266		name: "new-style review",
267		review: &PullRequestReviewRequest{
268			Comments: []*DraftReviewComment{{
269				Path: &path,
270				Body: &body,
271				Side: &right,
272				Line: &line1,
273			}, {
274				Path: &path,
275				Body: &body,
276				Side: &left,
277				Line: &line2,
278			}, {
279				Path: &path,
280				Body: &body,
281				Side: &right,
282				Line: &line3,
283			}},
284		},
285		wantBool: true,
286	}, {
287		name: "blended comment",
288		review: &PullRequestReviewRequest{
289			Comments: []*DraftReviewComment{{
290				Path:     &path,
291				Body:     &body,
292				Position: &pos1, // can't have both styles.
293				Side:     &right,
294				Line:     &line1,
295			}},
296		},
297		wantErr: ErrMixedCommentStyles,
298	}, {
299		name: "position then line",
300		review: &PullRequestReviewRequest{
301			Comments: []*DraftReviewComment{{
302				Path:     &path,
303				Body:     &body,
304				Position: &pos1,
305			}, {
306				Path: &path,
307				Body: &body,
308				Side: &right,
309				Line: &line1,
310			}},
311		},
312		wantErr: ErrMixedCommentStyles,
313	}, {
314		name: "line then position",
315		review: &PullRequestReviewRequest{
316			Comments: []*DraftReviewComment{{
317				Path: &path,
318				Body: &body,
319				Side: &right,
320				Line: &line1,
321			}, {
322				Path:     &path,
323				Body:     &body,
324				Position: &pos1,
325			}},
326		},
327		wantErr: ErrMixedCommentStyles,
328	}}
329
330	for _, tc := range tests {
331		t.Run(tc.name, func(t *testing.T) {
332			gotBool, gotErr := tc.review.isComfortFadePreview()
333			if tc.wantErr != nil {
334				if gotErr != tc.wantErr {
335					t.Errorf("isComfortFadePreview() = %v, wanted %v", gotErr, tc.wantErr)
336				}
337			} else {
338				if gotBool != tc.wantBool {
339					t.Errorf("isComfortFadePreview() = %v, wanted %v", gotBool, tc.wantBool)
340				}
341			}
342		})
343	}
344}
345
346func TestPullRequestsService_ListReviewComments_invalidOwner(t *testing.T) {
347	client, _, _, teardown := setup()
348	defer teardown()
349
350	ctx := context.Background()
351	_, _, err := client.PullRequests.ListReviewComments(ctx, "%", "r", 1, 1, nil)
352	testURLParseError(t, err)
353}
354
355func TestPullRequestsService_CreateReview(t *testing.T) {
356	client, mux, _, teardown := setup()
357	defer teardown()
358
359	input := &PullRequestReviewRequest{
360		CommitID: String("commit_id"),
361		Body:     String("b"),
362		Event:    String("APPROVE"),
363	}
364
365	mux.HandleFunc("/repos/o/r/pulls/1/reviews", func(w http.ResponseWriter, r *http.Request) {
366		v := new(PullRequestReviewRequest)
367		json.NewDecoder(r.Body).Decode(v)
368
369		testMethod(t, r, "POST")
370		if !reflect.DeepEqual(v, input) {
371			t.Errorf("Request body = %+v, want %+v", v, input)
372		}
373
374		fmt.Fprint(w, `{"id":1}`)
375	})
376
377	ctx := context.Background()
378	review, _, err := client.PullRequests.CreateReview(ctx, "o", "r", 1, input)
379	if err != nil {
380		t.Errorf("PullRequests.CreateReview returned error: %v", err)
381	}
382
383	want := &PullRequestReview{ID: Int64(1)}
384	if !reflect.DeepEqual(review, want) {
385		t.Errorf("PullRequests.CreateReview returned %+v, want %+v", review, want)
386	}
387
388	const methodName = "CreateReview"
389	testBadOptions(t, methodName, func() (err error) {
390		_, _, err = client.PullRequests.CreateReview(ctx, "\n", "\n", -1, input)
391		return err
392	})
393
394	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
395		got, resp, err := client.PullRequests.CreateReview(ctx, "o", "r", 1, input)
396		if got != nil {
397			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
398		}
399		return resp, err
400	})
401}
402
403func TestPullRequestsService_CreateReview_invalidOwner(t *testing.T) {
404	client, _, _, teardown := setup()
405	defer teardown()
406
407	ctx := context.Background()
408	_, _, err := client.PullRequests.CreateReview(ctx, "%", "r", 1, &PullRequestReviewRequest{})
409	testURLParseError(t, err)
410}
411
412func TestPullRequestsService_CreateReview_badReview(t *testing.T) {
413	client, _, _, teardown := setup()
414	defer teardown()
415
416	ctx := context.Background()
417
418	path := "path/to/file.go"
419	body := "this is a comment body"
420	right := "RIGHT"
421	pos1 := 1
422	line1 := 11
423	badReview := &PullRequestReviewRequest{
424		Comments: []*DraftReviewComment{{
425			Path: &path,
426			Body: &body,
427			Side: &right,
428			Line: &line1,
429		}, {
430			Path:     &path,
431			Body:     &body,
432			Position: &pos1,
433		}}}
434
435	_, _, err := client.PullRequests.CreateReview(ctx, "o", "r", 1, badReview)
436	if err == nil {
437		t.Errorf("CreateReview badReview err = nil, want err")
438	}
439}
440
441func TestPullRequestsService_CreateReview_addHeader(t *testing.T) {
442	client, mux, _, teardown := setup()
443	defer teardown()
444
445	path := "path/to/file.go"
446	body := "this is a comment body"
447	left, right := "LEFT", "RIGHT"
448	line1, line2, line3 := 11, 22, 33
449	input := &PullRequestReviewRequest{
450		Comments: []*DraftReviewComment{{
451			Path: &path,
452			Body: &body,
453			Side: &right,
454			Line: &line1,
455		}, {
456			Path: &path,
457			Body: &body,
458			Side: &left,
459			Line: &line2,
460		}, {
461			Path: &path,
462			Body: &body,
463			Side: &right,
464			Line: &line3,
465		}},
466	}
467
468	mux.HandleFunc("/repos/o/r/pulls/1/reviews", func(w http.ResponseWriter, r *http.Request) {
469		v := new(PullRequestReviewRequest)
470		json.NewDecoder(r.Body).Decode(v)
471
472		testMethod(t, r, "POST")
473		if !reflect.DeepEqual(v, input) {
474			t.Errorf("Request body = %+v, want %+v", v, input)
475		}
476
477		fmt.Fprint(w, `{"id":1}`)
478	})
479
480	ctx := context.Background()
481
482	_, _, err := client.PullRequests.CreateReview(ctx, "o", "r", 1, input)
483	if err != nil {
484		t.Errorf("CreateReview addHeader err = %v, want nil", err)
485	}
486}
487
488func TestPullRequestsService_UpdateReview(t *testing.T) {
489	client, mux, _, teardown := setup()
490	defer teardown()
491
492	mux.HandleFunc("/repos/o/r/pulls/1/reviews/1", func(w http.ResponseWriter, r *http.Request) {
493		testMethod(t, r, "PUT")
494		fmt.Fprintf(w, `{"id":1}`)
495	})
496
497	ctx := context.Background()
498	got, _, err := client.PullRequests.UpdateReview(ctx, "o", "r", 1, 1, "updated_body")
499	if err != nil {
500		t.Errorf("PullRequests.UpdateReview returned error: %v", err)
501	}
502
503	want := &PullRequestReview{ID: Int64(1)}
504	if !reflect.DeepEqual(got, want) {
505		t.Errorf("PullRequests.UpdateReview = %+v, want %+v", got, want)
506	}
507
508	const methodName = "UpdateReview"
509	testBadOptions(t, methodName, func() (err error) {
510		_, _, err = client.PullRequests.UpdateReview(ctx, "\n", "\n", -1, -1, "updated_body")
511		return err
512	})
513
514	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
515		got, resp, err := client.PullRequests.UpdateReview(ctx, "o", "r", 1, 1, "updated_body")
516		if got != nil {
517			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
518		}
519		return resp, err
520	})
521}
522
523func TestPullRequestsService_SubmitReview(t *testing.T) {
524	client, mux, _, teardown := setup()
525	defer teardown()
526
527	input := &PullRequestReviewRequest{
528		Body:  String("b"),
529		Event: String("APPROVE"),
530	}
531
532	mux.HandleFunc("/repos/o/r/pulls/1/reviews/1/events", func(w http.ResponseWriter, r *http.Request) {
533		v := new(PullRequestReviewRequest)
534		json.NewDecoder(r.Body).Decode(v)
535
536		testMethod(t, r, "POST")
537		if !reflect.DeepEqual(v, input) {
538			t.Errorf("Request body = %+v, want %+v", v, input)
539		}
540
541		fmt.Fprint(w, `{"id":1}`)
542	})
543
544	ctx := context.Background()
545	review, _, err := client.PullRequests.SubmitReview(ctx, "o", "r", 1, 1, input)
546	if err != nil {
547		t.Errorf("PullRequests.SubmitReview returned error: %v", err)
548	}
549
550	want := &PullRequestReview{ID: Int64(1)}
551	if !reflect.DeepEqual(review, want) {
552		t.Errorf("PullRequests.SubmitReview returned %+v, want %+v", review, want)
553	}
554
555	const methodName = "SubmitReview"
556	testBadOptions(t, methodName, func() (err error) {
557		_, _, err = client.PullRequests.SubmitReview(ctx, "\n", "\n", -1, -1, input)
558		return err
559	})
560
561	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
562		got, resp, err := client.PullRequests.SubmitReview(ctx, "o", "r", 1, 1, input)
563		if got != nil {
564			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
565		}
566		return resp, err
567	})
568}
569
570func TestPullRequestsService_SubmitReview_invalidOwner(t *testing.T) {
571	client, _, _, teardown := setup()
572	defer teardown()
573
574	ctx := context.Background()
575	_, _, err := client.PullRequests.SubmitReview(ctx, "%", "r", 1, 1, &PullRequestReviewRequest{})
576	testURLParseError(t, err)
577}
578
579func TestPullRequestsService_DismissReview(t *testing.T) {
580	client, mux, _, teardown := setup()
581	defer teardown()
582
583	input := &PullRequestReviewDismissalRequest{Message: String("m")}
584
585	mux.HandleFunc("/repos/o/r/pulls/1/reviews/1/dismissals", func(w http.ResponseWriter, r *http.Request) {
586		v := new(PullRequestReviewDismissalRequest)
587		json.NewDecoder(r.Body).Decode(v)
588
589		testMethod(t, r, "PUT")
590		if !reflect.DeepEqual(v, input) {
591			t.Errorf("Request body = %+v, want %+v", v, input)
592		}
593
594		fmt.Fprint(w, `{"id":1}`)
595	})
596
597	ctx := context.Background()
598	review, _, err := client.PullRequests.DismissReview(ctx, "o", "r", 1, 1, input)
599	if err != nil {
600		t.Errorf("PullRequests.DismissReview returned error: %v", err)
601	}
602
603	want := &PullRequestReview{ID: Int64(1)}
604	if !reflect.DeepEqual(review, want) {
605		t.Errorf("PullRequests.DismissReview returned %+v, want %+v", review, want)
606	}
607
608	const methodName = "ListReviews"
609	testBadOptions(t, methodName, func() (err error) {
610		_, _, err = client.PullRequests.DismissReview(ctx, "\n", "\n", -1, -1, input)
611		return err
612	})
613
614	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
615		got, resp, err := client.PullRequests.DismissReview(ctx, "o", "r", 1, 1, input)
616		if got != nil {
617			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
618		}
619		return resp, err
620	})
621}
622
623func TestPullRequestsService_DismissReview_invalidOwner(t *testing.T) {
624	client, _, _, teardown := setup()
625	defer teardown()
626
627	ctx := context.Background()
628	_, _, err := client.PullRequests.DismissReview(ctx, "%", "r", 1, 1, &PullRequestReviewDismissalRequest{})
629	testURLParseError(t, err)
630}
631