1
2
3
4
5
6 package github
7
8 import (
9 "context"
10 "encoding/json"
11 "fmt"
12 "net/http"
13 "testing"
14 "time"
15
16 "github.com/google/go-cmp/cmp"
17 )
18
19 func TestIssuesService_List_all(t *testing.T) {
20 client, mux, _, teardown := setup()
21 defer teardown()
22
23 mux.HandleFunc("/issues", func(w http.ResponseWriter, r *http.Request) {
24 testMethod(t, r, "GET")
25 testHeader(t, r, "Accept", mediaTypeReactionsPreview)
26 testFormValues(t, r, values{
27 "filter": "all",
28 "state": "closed",
29 "labels": "a,b",
30 "sort": "updated",
31 "direction": "asc",
32 "since": "2002-02-10T15:30:00Z",
33 "page": "1",
34 "per_page": "2",
35 })
36 fmt.Fprint(w, `[{"number":1}]`)
37 })
38
39 opt := &IssueListOptions{
40 "all", "closed", []string{"a", "b"}, "updated", "asc",
41 time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC),
42 ListOptions{Page: 1, PerPage: 2},
43 }
44 ctx := context.Background()
45 issues, _, err := client.Issues.List(ctx, true, opt)
46 if err != nil {
47 t.Errorf("Issues.List returned error: %v", err)
48 }
49
50 want := []*Issue{{Number: Int(1)}}
51 if !cmp.Equal(issues, want) {
52 t.Errorf("Issues.List returned %+v, want %+v", issues, want)
53 }
54
55 const methodName = "List"
56 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
57 got, resp, err := client.Issues.List(ctx, true, opt)
58 if got != nil {
59 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
60 }
61 return resp, err
62 })
63 }
64
65 func TestIssuesService_List_owned(t *testing.T) {
66 client, mux, _, teardown := setup()
67 defer teardown()
68
69 mux.HandleFunc("/user/issues", func(w http.ResponseWriter, r *http.Request) {
70 testMethod(t, r, "GET")
71 testHeader(t, r, "Accept", mediaTypeReactionsPreview)
72 fmt.Fprint(w, `[{"number":1}]`)
73 })
74
75 ctx := context.Background()
76 issues, _, err := client.Issues.List(ctx, false, nil)
77 if err != nil {
78 t.Errorf("Issues.List returned error: %v", err)
79 }
80
81 want := []*Issue{{Number: Int(1)}}
82 if !cmp.Equal(issues, want) {
83 t.Errorf("Issues.List returned %+v, want %+v", issues, want)
84 }
85 }
86
87 func TestIssuesService_ListByOrg(t *testing.T) {
88 client, mux, _, teardown := setup()
89 defer teardown()
90
91 mux.HandleFunc("/orgs/o/issues", func(w http.ResponseWriter, r *http.Request) {
92 testMethod(t, r, "GET")
93 testHeader(t, r, "Accept", mediaTypeReactionsPreview)
94 fmt.Fprint(w, `[{"number":1}]`)
95 })
96
97 ctx := context.Background()
98 issues, _, err := client.Issues.ListByOrg(ctx, "o", nil)
99 if err != nil {
100 t.Errorf("Issues.ListByOrg returned error: %v", err)
101 }
102
103 want := []*Issue{{Number: Int(1)}}
104 if !cmp.Equal(issues, want) {
105 t.Errorf("Issues.List returned %+v, want %+v", issues, want)
106 }
107
108 const methodName = "ListByOrg"
109 testBadOptions(t, methodName, func() (err error) {
110 _, _, err = client.Issues.ListByOrg(ctx, "\n", nil)
111 return err
112 })
113
114 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
115 got, resp, err := client.Issues.ListByOrg(ctx, "o", nil)
116 if got != nil {
117 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
118 }
119 return resp, err
120 })
121 }
122
123 func TestIssuesService_ListByOrg_invalidOrg(t *testing.T) {
124 client, _, _, teardown := setup()
125 defer teardown()
126
127 ctx := context.Background()
128 _, _, err := client.Issues.ListByOrg(ctx, "%", nil)
129 testURLParseError(t, err)
130 }
131
132 func TestIssuesService_ListByOrg_badOrg(t *testing.T) {
133 client, _, _, teardown := setup()
134 defer teardown()
135
136 ctx := context.Background()
137 _, _, err := client.Issues.ListByOrg(ctx, "\n", nil)
138 testURLParseError(t, err)
139 }
140
141 func TestIssuesService_ListByRepo(t *testing.T) {
142 client, mux, _, teardown := setup()
143 defer teardown()
144
145 mux.HandleFunc("/repos/o/r/issues", func(w http.ResponseWriter, r *http.Request) {
146 testMethod(t, r, "GET")
147 testHeader(t, r, "Accept", mediaTypeReactionsPreview)
148 testFormValues(t, r, values{
149 "milestone": "*",
150 "state": "closed",
151 "assignee": "a",
152 "creator": "c",
153 "mentioned": "m",
154 "labels": "a,b",
155 "sort": "updated",
156 "direction": "asc",
157 "since": "2002-02-10T15:30:00Z",
158 })
159 fmt.Fprint(w, `[{"number":1}]`)
160 })
161
162 opt := &IssueListByRepoOptions{
163 "*", "closed", "a", "c", "m", []string{"a", "b"}, "updated", "asc",
164 time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC),
165 ListOptions{0, 0},
166 }
167 ctx := context.Background()
168 issues, _, err := client.Issues.ListByRepo(ctx, "o", "r", opt)
169 if err != nil {
170 t.Errorf("Issues.ListByOrg returned error: %v", err)
171 }
172
173 want := []*Issue{{Number: Int(1)}}
174 if !cmp.Equal(issues, want) {
175 t.Errorf("Issues.List returned %+v, want %+v", issues, want)
176 }
177
178 const methodName = "ListByRepo"
179 testBadOptions(t, methodName, func() (err error) {
180 _, _, err = client.Issues.ListByRepo(ctx, "\n", "\n", opt)
181 return err
182 })
183
184 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
185 got, resp, err := client.Issues.ListByRepo(ctx, "o", "r", opt)
186 if got != nil {
187 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
188 }
189 return resp, err
190 })
191 }
192
193 func TestIssuesService_ListByRepo_invalidOwner(t *testing.T) {
194 client, _, _, teardown := setup()
195 defer teardown()
196
197 ctx := context.Background()
198 _, _, err := client.Issues.ListByRepo(ctx, "%", "r", nil)
199 testURLParseError(t, err)
200 }
201
202 func TestIssuesService_Get(t *testing.T) {
203 client, mux, _, teardown := setup()
204 defer teardown()
205
206 mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) {
207 testMethod(t, r, "GET")
208 testHeader(t, r, "Accept", mediaTypeReactionsPreview)
209 fmt.Fprint(w, `{"number":1, "author_association": "MEMBER","labels": [{"url": "u", "name": "n", "color": "c"}]}`)
210 })
211
212 ctx := context.Background()
213 issue, _, err := client.Issues.Get(ctx, "o", "r", 1)
214 if err != nil {
215 t.Errorf("Issues.Get returned error: %v", err)
216 }
217
218 want := &Issue{
219 Number: Int(1),
220 AuthorAssociation: String("MEMBER"),
221 Labels: []*Label{{
222 URL: String("u"),
223 Name: String("n"),
224 Color: String("c"),
225 }},
226 }
227 if !cmp.Equal(issue, want) {
228 t.Errorf("Issues.Get returned %+v, want %+v", issue, want)
229 }
230
231 const methodName = "Get"
232 testBadOptions(t, methodName, func() (err error) {
233 _, _, err = client.Issues.Get(ctx, "\n", "\n", 1)
234 return err
235 })
236
237 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
238 got, resp, err := client.Issues.Get(ctx, "o", "r", 1)
239 if got != nil {
240 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
241 }
242 return resp, err
243 })
244 }
245
246 func TestIssuesService_Get_invalidOwner(t *testing.T) {
247 client, _, _, teardown := setup()
248 defer teardown()
249
250 ctx := context.Background()
251 _, _, err := client.Issues.Get(ctx, "%", "r", 1)
252 testURLParseError(t, err)
253 }
254
255 func TestIssuesService_Create(t *testing.T) {
256 client, mux, _, teardown := setup()
257 defer teardown()
258
259 input := &IssueRequest{
260 Title: String("t"),
261 Body: String("b"),
262 Assignee: String("a"),
263 Labels: &[]string{"l1", "l2"},
264 }
265
266 mux.HandleFunc("/repos/o/r/issues", func(w http.ResponseWriter, r *http.Request) {
267 v := new(IssueRequest)
268 json.NewDecoder(r.Body).Decode(v)
269
270 testMethod(t, r, "POST")
271 if !cmp.Equal(v, input) {
272 t.Errorf("Request body = %+v, want %+v", v, input)
273 }
274
275 fmt.Fprint(w, `{"number":1}`)
276 })
277
278 ctx := context.Background()
279 issue, _, err := client.Issues.Create(ctx, "o", "r", input)
280 if err != nil {
281 t.Errorf("Issues.Create returned error: %v", err)
282 }
283
284 want := &Issue{Number: Int(1)}
285 if !cmp.Equal(issue, want) {
286 t.Errorf("Issues.Create returned %+v, want %+v", issue, want)
287 }
288
289 const methodName = "Create"
290 testBadOptions(t, methodName, func() (err error) {
291 _, _, err = client.Issues.Create(ctx, "\n", "\n", input)
292 return err
293 })
294
295 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
296 got, resp, err := client.Issues.Create(ctx, "o", "r", input)
297 if got != nil {
298 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
299 }
300 return resp, err
301 })
302 }
303
304 func TestIssuesService_Create_invalidOwner(t *testing.T) {
305 client, _, _, teardown := setup()
306 defer teardown()
307
308 ctx := context.Background()
309 _, _, err := client.Issues.Create(ctx, "%", "r", nil)
310 testURLParseError(t, err)
311 }
312
313 func TestIssuesService_Edit(t *testing.T) {
314 client, mux, _, teardown := setup()
315 defer teardown()
316
317 input := &IssueRequest{Title: String("t")}
318
319 mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) {
320 v := new(IssueRequest)
321 json.NewDecoder(r.Body).Decode(v)
322
323 testMethod(t, r, "PATCH")
324 if !cmp.Equal(v, input) {
325 t.Errorf("Request body = %+v, want %+v", v, input)
326 }
327
328 fmt.Fprint(w, `{"number":1}`)
329 })
330
331 ctx := context.Background()
332 issue, _, err := client.Issues.Edit(ctx, "o", "r", 1, input)
333 if err != nil {
334 t.Errorf("Issues.Edit returned error: %v", err)
335 }
336
337 want := &Issue{Number: Int(1)}
338 if !cmp.Equal(issue, want) {
339 t.Errorf("Issues.Edit returned %+v, want %+v", issue, want)
340 }
341
342 const methodName = "Edit"
343 testBadOptions(t, methodName, func() (err error) {
344 _, _, err = client.Issues.Edit(ctx, "\n", "\n", -1, input)
345 return err
346 })
347
348 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
349 got, resp, err := client.Issues.Edit(ctx, "o", "r", 1, input)
350 if got != nil {
351 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
352 }
353 return resp, err
354 })
355 }
356
357 func TestIssuesService_RemoveMilestone(t *testing.T) {
358 client, mux, _, teardown := setup()
359 defer teardown()
360 mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) {
361 testMethod(t, r, "PATCH")
362 fmt.Fprint(w, `{"number":1}`)
363 })
364
365 ctx := context.Background()
366 issue, _, err := client.Issues.RemoveMilestone(ctx, "o", "r", 1)
367 if err != nil {
368 t.Errorf("Issues.RemoveMilestone returned error: %v", err)
369 }
370
371 want := &Issue{Number: Int(1)}
372 if !cmp.Equal(issue, want) {
373 t.Errorf("Issues.RemoveMilestone returned %+v, want %+v", issue, want)
374 }
375
376 const methodName = "RemoveMilestone"
377 testBadOptions(t, methodName, func() (err error) {
378 _, _, err = client.Issues.RemoveMilestone(ctx, "\n", "\n", -1)
379 return err
380 })
381
382 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
383 got, resp, err := client.Issues.RemoveMilestone(ctx, "o", "r", 1)
384 if got != nil {
385 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
386 }
387 return resp, err
388 })
389 }
390
391 func TestIssuesService_Edit_invalidOwner(t *testing.T) {
392 client, _, _, teardown := setup()
393 defer teardown()
394
395 ctx := context.Background()
396 _, _, err := client.Issues.Edit(ctx, "%", "r", 1, nil)
397 testURLParseError(t, err)
398 }
399
400 func TestIssuesService_Lock(t *testing.T) {
401 client, mux, _, teardown := setup()
402 defer teardown()
403
404 mux.HandleFunc("/repos/o/r/issues/1/lock", func(w http.ResponseWriter, r *http.Request) {
405 testMethod(t, r, "PUT")
406
407 w.WriteHeader(http.StatusNoContent)
408 })
409
410 ctx := context.Background()
411 if _, err := client.Issues.Lock(ctx, "o", "r", 1, nil); err != nil {
412 t.Errorf("Issues.Lock returned error: %v", err)
413 }
414
415 const methodName = "Lock"
416 testBadOptions(t, methodName, func() (err error) {
417 _, err = client.Issues.Lock(ctx, "\n", "\n", -1, nil)
418 return err
419 })
420
421 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
422 return client.Issues.Lock(ctx, "o", "r", 1, nil)
423 })
424 }
425
426 func TestIssuesService_LockWithReason(t *testing.T) {
427 client, mux, _, teardown := setup()
428 defer teardown()
429
430 mux.HandleFunc("/repos/o/r/issues/1/lock", func(w http.ResponseWriter, r *http.Request) {
431 testMethod(t, r, "PUT")
432 w.WriteHeader(http.StatusNoContent)
433 })
434
435 opt := &LockIssueOptions{LockReason: "off-topic"}
436
437 ctx := context.Background()
438 if _, err := client.Issues.Lock(ctx, "o", "r", 1, opt); err != nil {
439 t.Errorf("Issues.Lock returned error: %v", err)
440 }
441 }
442
443 func TestIssuesService_Unlock(t *testing.T) {
444 client, mux, _, teardown := setup()
445 defer teardown()
446
447 mux.HandleFunc("/repos/o/r/issues/1/lock", func(w http.ResponseWriter, r *http.Request) {
448 testMethod(t, r, "DELETE")
449
450 w.WriteHeader(http.StatusNoContent)
451 })
452
453 ctx := context.Background()
454 if _, err := client.Issues.Unlock(ctx, "o", "r", 1); err != nil {
455 t.Errorf("Issues.Unlock returned error: %v", err)
456 }
457
458 const methodName = "Unlock"
459 testBadOptions(t, methodName, func() (err error) {
460 _, err = client.Issues.Unlock(ctx, "\n", "\n", 1)
461 return err
462 })
463
464 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
465 return client.Issues.Unlock(ctx, "o", "r", 1)
466 })
467 }
468
469 func TestIsPullRequest(t *testing.T) {
470 i := new(Issue)
471 if i.IsPullRequest() == true {
472 t.Errorf("expected i.IsPullRequest (%v) to return false, got true", i)
473 }
474 i.PullRequestLinks = &PullRequestLinks{URL: String("http://example.com")}
475 if i.IsPullRequest() == false {
476 t.Errorf("expected i.IsPullRequest (%v) to return true, got false", i)
477 }
478 }
479
480 func TestLockIssueOptions_Marshal(t *testing.T) {
481 testJSONMarshal(t, &LockIssueOptions{}, "{}")
482
483 u := &LockIssueOptions{
484 LockReason: "lr",
485 }
486
487 want := `{
488 "lock_reason": "lr"
489 }`
490
491 testJSONMarshal(t, u, want)
492 }
493
494 func TestPullRequestLinks_Marshal(t *testing.T) {
495 testJSONMarshal(t, &PullRequestLinks{}, "{}")
496
497 u := &PullRequestLinks{
498 URL: String("url"),
499 HTMLURL: String("hurl"),
500 DiffURL: String("durl"),
501 PatchURL: String("purl"),
502 }
503
504 want := `{
505 "url": "url",
506 "html_url": "hurl",
507 "diff_url": "durl",
508 "patch_url": "purl"
509 }`
510
511 testJSONMarshal(t, u, want)
512 }
513
514 func TestIssueRequest_Marshal(t *testing.T) {
515 testJSONMarshal(t, &IssueRequest{}, "{}")
516
517 u := &IssueRequest{
518 Title: String("url"),
519 Body: String("url"),
520 Labels: &[]string{"l"},
521 Assignee: String("url"),
522 State: String("url"),
523 Milestone: Int(1),
524 Assignees: &[]string{"a"},
525 }
526
527 want := `{
528 "title": "url",
529 "body": "url",
530 "labels": [
531 "l"
532 ],
533 "assignee": "url",
534 "state": "url",
535 "milestone": 1,
536 "assignees": [
537 "a"
538 ]
539 }`
540
541 testJSONMarshal(t, u, want)
542 }
543
544 func TestIssue_Marshal(t *testing.T) {
545 testJSONMarshal(t, &Issue{}, "{}")
546
547 u := &Issue{
548 ID: Int64(1),
549 Number: Int(1),
550 State: String("s"),
551 Locked: Bool(false),
552 Title: String("title"),
553 Body: String("body"),
554 AuthorAssociation: String("aa"),
555 User: &User{ID: Int64(1)},
556 Labels: []*Label{{ID: Int64(1)}},
557 Assignee: &User{ID: Int64(1)},
558 Comments: Int(1),
559 ClosedAt: &referenceTime,
560 CreatedAt: &referenceTime,
561 UpdatedAt: &referenceTime,
562 ClosedBy: &User{ID: Int64(1)},
563 URL: String("url"),
564 HTMLURL: String("hurl"),
565 CommentsURL: String("curl"),
566 EventsURL: String("eurl"),
567 LabelsURL: String("lurl"),
568 RepositoryURL: String("rurl"),
569 Milestone: &Milestone{ID: Int64(1)},
570 PullRequestLinks: &PullRequestLinks{URL: String("url")},
571 Repository: &Repository{ID: Int64(1)},
572 Reactions: &Reactions{TotalCount: Int(1)},
573 Assignees: []*User{{ID: Int64(1)}},
574 NodeID: String("nid"),
575 TextMatches: []*TextMatch{{ObjectURL: String("ourl")}},
576 ActiveLockReason: String("alr"),
577 }
578
579 want := `{
580 "id": 1,
581 "number": 1,
582 "state": "s",
583 "locked": false,
584 "title": "title",
585 "body": "body",
586 "author_association": "aa",
587 "user": {
588 "id": 1
589 },
590 "labels": [
591 {
592 "id": 1
593 }
594 ],
595 "assignee": {
596 "id": 1
597 },
598 "comments": 1,
599 "closed_at": ` + referenceTimeStr + `,
600 "created_at": ` + referenceTimeStr + `,
601 "updated_at": ` + referenceTimeStr + `,
602 "closed_by": {
603 "id": 1
604 },
605 "url": "url",
606 "html_url": "hurl",
607 "comments_url": "curl",
608 "events_url": "eurl",
609 "labels_url": "lurl",
610 "repository_url": "rurl",
611 "milestone": {
612 "id": 1
613 },
614 "pull_request": {
615 "url": "url"
616 },
617 "repository": {
618 "id": 1
619 },
620 "reactions": {
621 "total_count": 1
622 },
623 "assignees": [
624 {
625 "id": 1
626 }
627 ],
628 "node_id": "nid",
629 "text_matches": [
630 {
631 "object_url": "ourl"
632 }
633 ],
634 "active_lock_reason": "alr"
635 }`
636
637 testJSONMarshal(t, u, want)
638 }
639
View as plain text