github.com/google/go-github/v42@v42.0.0/github/issues_test.go (about)

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