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