github.com/google/go-github/v66@v66.0.0/github/projects_test.go (about)

     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  
     6  package github
     7  
     8  import (
     9  	"context"
    10  	"encoding/json"
    11  	"fmt"
    12  	"net/http"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/google/go-cmp/cmp"
    17  )
    18  
    19  func TestProject_Marshal(t *testing.T) {
    20  	t.Parallel()
    21  	testJSONMarshal(t, &Project{}, "{}")
    22  
    23  	u := &Project{
    24  		ID:         Int64(1),
    25  		URL:        String("u"),
    26  		HTMLURL:    String("h"),
    27  		ColumnsURL: String("c"),
    28  		OwnerURL:   String("o"),
    29  		Name:       String("n"),
    30  		Body:       String("b"),
    31  		Number:     Int(1),
    32  		State:      String("s"),
    33  		CreatedAt:  &Timestamp{referenceTime},
    34  		UpdatedAt:  &Timestamp{referenceTime},
    35  		NodeID:     String("n"),
    36  		Creator: &User{
    37  			Login:       String("l"),
    38  			ID:          Int64(1),
    39  			AvatarURL:   String("a"),
    40  			GravatarID:  String("g"),
    41  			Name:        String("n"),
    42  			Company:     String("c"),
    43  			Blog:        String("b"),
    44  			Location:    String("l"),
    45  			Email:       String("e"),
    46  			Hireable:    Bool(true),
    47  			PublicRepos: Int(1),
    48  			Followers:   Int(1),
    49  			Following:   Int(1),
    50  			CreatedAt:   &Timestamp{referenceTime},
    51  			URL:         String("u"),
    52  		},
    53  	}
    54  	want := `{
    55  		"id": 1,
    56  		"url": "u",
    57  		"html_url": "h",
    58  		"columns_url": "c",
    59  		"owner_url": "o",
    60  		"name": "n",
    61  		"body": "b",
    62  		"number": 1,
    63  		"state": "s",
    64  		"created_at": ` + referenceTimeStr + `,
    65  		"updated_at": ` + referenceTimeStr + `,
    66  		"node_id": "n",
    67  		"creator": {
    68  			"login": "l",
    69  			"id": 1,
    70  			"avatar_url": "a",
    71  			"gravatar_id": "g",
    72  			"name": "n",
    73  			"company": "c",
    74  			"blog": "b",
    75  			"location": "l",
    76  			"email": "e",
    77  			"hireable": true,
    78  			"public_repos": 1,
    79  			"followers": 1,
    80  			"following": 1,
    81  			"created_at": ` + referenceTimeStr + `,
    82  			"url": "u"
    83  		}
    84  	}`
    85  	testJSONMarshal(t, u, want)
    86  }
    87  
    88  func TestProjectsService_UpdateProject(t *testing.T) {
    89  	t.Parallel()
    90  	client, mux, _ := setup(t)
    91  
    92  	input := &ProjectOptions{
    93  		Name:    String("Project Name"),
    94  		Body:    String("Project body."),
    95  		State:   String("open"),
    96  		Private: Bool(false),
    97  
    98  		OrganizationPermission: String("read"),
    99  	}
   100  
   101  	mux.HandleFunc("/projects/1", func(w http.ResponseWriter, r *http.Request) {
   102  		testMethod(t, r, "PATCH")
   103  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   104  
   105  		v := &ProjectOptions{}
   106  		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
   107  		if !cmp.Equal(v, input) {
   108  			t.Errorf("Request body = %+v, want %+v", v, input)
   109  		}
   110  
   111  		fmt.Fprint(w, `{"id":1}`)
   112  	})
   113  
   114  	ctx := context.Background()
   115  	project, _, err := client.Projects.UpdateProject(ctx, 1, input)
   116  	if err != nil {
   117  		t.Errorf("Projects.UpdateProject returned error: %v", err)
   118  	}
   119  
   120  	want := &Project{ID: Int64(1)}
   121  	if !cmp.Equal(project, want) {
   122  		t.Errorf("Projects.UpdateProject returned %+v, want %+v", project, want)
   123  	}
   124  
   125  	const methodName = "UpdateProject"
   126  	testBadOptions(t, methodName, func() (err error) {
   127  		_, _, err = client.Projects.UpdateProject(ctx, -1, input)
   128  		return err
   129  	})
   130  
   131  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   132  		got, resp, err := client.Projects.UpdateProject(ctx, 1, input)
   133  		if got != nil {
   134  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   135  		}
   136  		return resp, err
   137  	})
   138  }
   139  
   140  func TestProjectsService_GetProject(t *testing.T) {
   141  	t.Parallel()
   142  	client, mux, _ := setup(t)
   143  
   144  	mux.HandleFunc("/projects/1", func(w http.ResponseWriter, r *http.Request) {
   145  		testMethod(t, r, "GET")
   146  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   147  		fmt.Fprint(w, `{"id":1}`)
   148  	})
   149  
   150  	ctx := context.Background()
   151  	project, _, err := client.Projects.GetProject(ctx, 1)
   152  	if err != nil {
   153  		t.Errorf("Projects.GetProject returned error: %v", err)
   154  	}
   155  
   156  	want := &Project{ID: Int64(1)}
   157  	if !cmp.Equal(project, want) {
   158  		t.Errorf("Projects.GetProject returned %+v, want %+v", project, want)
   159  	}
   160  
   161  	const methodName = "GetProject"
   162  	testBadOptions(t, methodName, func() (err error) {
   163  		_, _, err = client.Projects.GetProject(ctx, -1)
   164  		return err
   165  	})
   166  
   167  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   168  		got, resp, err := client.Projects.GetProject(ctx, 1)
   169  		if got != nil {
   170  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   171  		}
   172  		return resp, err
   173  	})
   174  }
   175  
   176  func TestProjectsService_DeleteProject(t *testing.T) {
   177  	t.Parallel()
   178  	client, mux, _ := setup(t)
   179  
   180  	mux.HandleFunc("/projects/1", func(w http.ResponseWriter, r *http.Request) {
   181  		testMethod(t, r, "DELETE")
   182  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   183  	})
   184  
   185  	ctx := context.Background()
   186  	_, err := client.Projects.DeleteProject(ctx, 1)
   187  	if err != nil {
   188  		t.Errorf("Projects.DeleteProject returned error: %v", err)
   189  	}
   190  
   191  	const methodName = "DeleteProject"
   192  	testBadOptions(t, methodName, func() (err error) {
   193  		_, err = client.Projects.DeleteProject(ctx, -1)
   194  		return err
   195  	})
   196  
   197  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   198  		return client.Projects.DeleteProject(ctx, 1)
   199  	})
   200  }
   201  
   202  func TestProjectsService_ListProjectColumns(t *testing.T) {
   203  	t.Parallel()
   204  	client, mux, _ := setup(t)
   205  
   206  	wantAcceptHeaders := []string{mediaTypeProjectsPreview}
   207  	mux.HandleFunc("/projects/1/columns", func(w http.ResponseWriter, r *http.Request) {
   208  		testMethod(t, r, "GET")
   209  		testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", "))
   210  		testFormValues(t, r, values{"page": "2"})
   211  		fmt.Fprint(w, `[{"id":1}]`)
   212  	})
   213  
   214  	opt := &ListOptions{Page: 2}
   215  	ctx := context.Background()
   216  	columns, _, err := client.Projects.ListProjectColumns(ctx, 1, opt)
   217  	if err != nil {
   218  		t.Errorf("Projects.ListProjectColumns returned error: %v", err)
   219  	}
   220  
   221  	want := []*ProjectColumn{{ID: Int64(1)}}
   222  	if !cmp.Equal(columns, want) {
   223  		t.Errorf("Projects.ListProjectColumns returned %+v, want %+v", columns, want)
   224  	}
   225  
   226  	const methodName = "ListProjectColumns"
   227  	testBadOptions(t, methodName, func() (err error) {
   228  		_, _, err = client.Projects.ListProjectColumns(ctx, -1, opt)
   229  		return err
   230  	})
   231  
   232  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   233  		got, resp, err := client.Projects.ListProjectColumns(ctx, 1, opt)
   234  		if got != nil {
   235  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   236  		}
   237  		return resp, err
   238  	})
   239  }
   240  
   241  func TestProjectsService_GetProjectColumn(t *testing.T) {
   242  	t.Parallel()
   243  	client, mux, _ := setup(t)
   244  
   245  	mux.HandleFunc("/projects/columns/1", func(w http.ResponseWriter, r *http.Request) {
   246  		testMethod(t, r, "GET")
   247  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   248  		fmt.Fprint(w, `{"id":1}`)
   249  	})
   250  
   251  	ctx := context.Background()
   252  	column, _, err := client.Projects.GetProjectColumn(ctx, 1)
   253  	if err != nil {
   254  		t.Errorf("Projects.GetProjectColumn returned error: %v", err)
   255  	}
   256  
   257  	want := &ProjectColumn{ID: Int64(1)}
   258  	if !cmp.Equal(column, want) {
   259  		t.Errorf("Projects.GetProjectColumn returned %+v, want %+v", column, want)
   260  	}
   261  
   262  	const methodName = "GetProjectColumn"
   263  	testBadOptions(t, methodName, func() (err error) {
   264  		_, _, err = client.Projects.GetProjectColumn(ctx, -1)
   265  		return err
   266  	})
   267  
   268  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   269  		got, resp, err := client.Projects.GetProjectColumn(ctx, 1)
   270  		if got != nil {
   271  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   272  		}
   273  		return resp, err
   274  	})
   275  }
   276  
   277  func TestProjectsService_CreateProjectColumn(t *testing.T) {
   278  	t.Parallel()
   279  	client, mux, _ := setup(t)
   280  
   281  	input := &ProjectColumnOptions{Name: "Column Name"}
   282  
   283  	mux.HandleFunc("/projects/1/columns", func(w http.ResponseWriter, r *http.Request) {
   284  		testMethod(t, r, "POST")
   285  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   286  
   287  		v := &ProjectColumnOptions{}
   288  		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
   289  		if !cmp.Equal(v, input) {
   290  			t.Errorf("Request body = %+v, want %+v", v, input)
   291  		}
   292  
   293  		fmt.Fprint(w, `{"id":1}`)
   294  	})
   295  
   296  	ctx := context.Background()
   297  	column, _, err := client.Projects.CreateProjectColumn(ctx, 1, input)
   298  	if err != nil {
   299  		t.Errorf("Projects.CreateProjectColumn returned error: %v", err)
   300  	}
   301  
   302  	want := &ProjectColumn{ID: Int64(1)}
   303  	if !cmp.Equal(column, want) {
   304  		t.Errorf("Projects.CreateProjectColumn returned %+v, want %+v", column, want)
   305  	}
   306  
   307  	const methodName = "CreateProjectColumn"
   308  	testBadOptions(t, methodName, func() (err error) {
   309  		_, _, err = client.Projects.CreateProjectColumn(ctx, -1, input)
   310  		return err
   311  	})
   312  
   313  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   314  		got, resp, err := client.Projects.CreateProjectColumn(ctx, 1, input)
   315  		if got != nil {
   316  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   317  		}
   318  		return resp, err
   319  	})
   320  }
   321  
   322  func TestProjectsService_UpdateProjectColumn(t *testing.T) {
   323  	t.Parallel()
   324  	client, mux, _ := setup(t)
   325  
   326  	input := &ProjectColumnOptions{Name: "Column Name"}
   327  
   328  	mux.HandleFunc("/projects/columns/1", func(w http.ResponseWriter, r *http.Request) {
   329  		testMethod(t, r, "PATCH")
   330  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   331  
   332  		v := &ProjectColumnOptions{}
   333  		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
   334  		if !cmp.Equal(v, input) {
   335  			t.Errorf("Request body = %+v, want %+v", v, input)
   336  		}
   337  
   338  		fmt.Fprint(w, `{"id":1}`)
   339  	})
   340  
   341  	ctx := context.Background()
   342  	column, _, err := client.Projects.UpdateProjectColumn(ctx, 1, input)
   343  	if err != nil {
   344  		t.Errorf("Projects.UpdateProjectColumn returned error: %v", err)
   345  	}
   346  
   347  	want := &ProjectColumn{ID: Int64(1)}
   348  	if !cmp.Equal(column, want) {
   349  		t.Errorf("Projects.UpdateProjectColumn returned %+v, want %+v", column, want)
   350  	}
   351  
   352  	const methodName = "UpdateProjectColumn"
   353  	testBadOptions(t, methodName, func() (err error) {
   354  		_, _, err = client.Projects.UpdateProjectColumn(ctx, -1, input)
   355  		return err
   356  	})
   357  
   358  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   359  		got, resp, err := client.Projects.UpdateProjectColumn(ctx, 1, input)
   360  		if got != nil {
   361  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   362  		}
   363  		return resp, err
   364  	})
   365  }
   366  
   367  func TestProjectsService_DeleteProjectColumn(t *testing.T) {
   368  	t.Parallel()
   369  	client, mux, _ := setup(t)
   370  
   371  	mux.HandleFunc("/projects/columns/1", func(w http.ResponseWriter, r *http.Request) {
   372  		testMethod(t, r, "DELETE")
   373  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   374  	})
   375  
   376  	ctx := context.Background()
   377  	_, err := client.Projects.DeleteProjectColumn(ctx, 1)
   378  	if err != nil {
   379  		t.Errorf("Projects.DeleteProjectColumn returned error: %v", err)
   380  	}
   381  
   382  	const methodName = "DeleteProjectColumn"
   383  	testBadOptions(t, methodName, func() (err error) {
   384  		_, err = client.Projects.DeleteProjectColumn(ctx, -1)
   385  		return err
   386  	})
   387  
   388  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   389  		return client.Projects.DeleteProjectColumn(ctx, 1)
   390  	})
   391  }
   392  
   393  func TestProjectsService_MoveProjectColumn(t *testing.T) {
   394  	t.Parallel()
   395  	client, mux, _ := setup(t)
   396  
   397  	input := &ProjectColumnMoveOptions{Position: "after:12345"}
   398  
   399  	mux.HandleFunc("/projects/columns/1/moves", func(w http.ResponseWriter, r *http.Request) {
   400  		testMethod(t, r, "POST")
   401  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   402  
   403  		v := &ProjectColumnMoveOptions{}
   404  		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
   405  		if !cmp.Equal(v, input) {
   406  			t.Errorf("Request body = %+v, want %+v", v, input)
   407  		}
   408  	})
   409  
   410  	ctx := context.Background()
   411  	_, err := client.Projects.MoveProjectColumn(ctx, 1, input)
   412  	if err != nil {
   413  		t.Errorf("Projects.MoveProjectColumn returned error: %v", err)
   414  	}
   415  
   416  	const methodName = "MoveProjectColumn"
   417  	testBadOptions(t, methodName, func() (err error) {
   418  		_, err = client.Projects.MoveProjectColumn(ctx, -1, input)
   419  		return err
   420  	})
   421  
   422  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   423  		return client.Projects.MoveProjectColumn(ctx, 1, input)
   424  	})
   425  }
   426  
   427  func TestProjectsService_ListProjectCards(t *testing.T) {
   428  	t.Parallel()
   429  	client, mux, _ := setup(t)
   430  
   431  	mux.HandleFunc("/projects/columns/1/cards", func(w http.ResponseWriter, r *http.Request) {
   432  		testMethod(t, r, "GET")
   433  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   434  		testFormValues(t, r, values{
   435  			"archived_state": "all",
   436  			"page":           "2"})
   437  		fmt.Fprint(w, `[{"id":1}]`)
   438  	})
   439  
   440  	opt := &ProjectCardListOptions{
   441  		ArchivedState: String("all"),
   442  		ListOptions:   ListOptions{Page: 2}}
   443  	ctx := context.Background()
   444  	cards, _, err := client.Projects.ListProjectCards(ctx, 1, opt)
   445  	if err != nil {
   446  		t.Errorf("Projects.ListProjectCards returned error: %v", err)
   447  	}
   448  
   449  	want := []*ProjectCard{{ID: Int64(1)}}
   450  	if !cmp.Equal(cards, want) {
   451  		t.Errorf("Projects.ListProjectCards returned %+v, want %+v", cards, want)
   452  	}
   453  
   454  	const methodName = "ListProjectCards"
   455  	testBadOptions(t, methodName, func() (err error) {
   456  		_, _, err = client.Projects.ListProjectCards(ctx, -1, opt)
   457  		return err
   458  	})
   459  
   460  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   461  		got, resp, err := client.Projects.ListProjectCards(ctx, 1, opt)
   462  		if got != nil {
   463  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   464  		}
   465  		return resp, err
   466  	})
   467  }
   468  
   469  func TestProjectsService_GetProjectCard(t *testing.T) {
   470  	t.Parallel()
   471  	client, mux, _ := setup(t)
   472  
   473  	mux.HandleFunc("/projects/columns/cards/1", func(w http.ResponseWriter, r *http.Request) {
   474  		testMethod(t, r, "GET")
   475  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   476  		fmt.Fprint(w, `{"id":1}`)
   477  	})
   478  
   479  	ctx := context.Background()
   480  	card, _, err := client.Projects.GetProjectCard(ctx, 1)
   481  	if err != nil {
   482  		t.Errorf("Projects.GetProjectCard returned error: %v", err)
   483  	}
   484  
   485  	want := &ProjectCard{ID: Int64(1)}
   486  	if !cmp.Equal(card, want) {
   487  		t.Errorf("Projects.GetProjectCard returned %+v, want %+v", card, want)
   488  	}
   489  
   490  	const methodName = "GetProjectCard"
   491  	testBadOptions(t, methodName, func() (err error) {
   492  		_, _, err = client.Projects.GetProjectCard(ctx, -1)
   493  		return err
   494  	})
   495  
   496  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   497  		got, resp, err := client.Projects.GetProjectCard(ctx, 1)
   498  		if got != nil {
   499  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   500  		}
   501  		return resp, err
   502  	})
   503  }
   504  
   505  func TestProjectsService_CreateProjectCard(t *testing.T) {
   506  	t.Parallel()
   507  	client, mux, _ := setup(t)
   508  
   509  	input := &ProjectCardOptions{
   510  		ContentID:   12345,
   511  		ContentType: "Issue",
   512  	}
   513  
   514  	mux.HandleFunc("/projects/columns/1/cards", func(w http.ResponseWriter, r *http.Request) {
   515  		testMethod(t, r, "POST")
   516  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   517  
   518  		v := &ProjectCardOptions{}
   519  		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
   520  		if !cmp.Equal(v, input) {
   521  			t.Errorf("Request body = %+v, want %+v", v, input)
   522  		}
   523  
   524  		fmt.Fprint(w, `{"id":1}`)
   525  	})
   526  
   527  	ctx := context.Background()
   528  	card, _, err := client.Projects.CreateProjectCard(ctx, 1, input)
   529  	if err != nil {
   530  		t.Errorf("Projects.CreateProjectCard returned error: %v", err)
   531  	}
   532  
   533  	want := &ProjectCard{ID: Int64(1)}
   534  	if !cmp.Equal(card, want) {
   535  		t.Errorf("Projects.CreateProjectCard returned %+v, want %+v", card, want)
   536  	}
   537  
   538  	const methodName = "CreateProjectCard"
   539  	testBadOptions(t, methodName, func() (err error) {
   540  		_, _, err = client.Projects.CreateProjectCard(ctx, -1, input)
   541  		return err
   542  	})
   543  
   544  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   545  		got, resp, err := client.Projects.CreateProjectCard(ctx, 1, input)
   546  		if got != nil {
   547  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   548  		}
   549  		return resp, err
   550  	})
   551  }
   552  
   553  func TestProjectsService_UpdateProjectCard(t *testing.T) {
   554  	t.Parallel()
   555  	client, mux, _ := setup(t)
   556  
   557  	input := &ProjectCardOptions{
   558  		ContentID:   12345,
   559  		ContentType: "Issue",
   560  	}
   561  
   562  	mux.HandleFunc("/projects/columns/cards/1", func(w http.ResponseWriter, r *http.Request) {
   563  		testMethod(t, r, "PATCH")
   564  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   565  
   566  		v := &ProjectCardOptions{}
   567  		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
   568  		if !cmp.Equal(v, input) {
   569  			t.Errorf("Request body = %+v, want %+v", v, input)
   570  		}
   571  
   572  		fmt.Fprint(w, `{"id":1, "archived":false}`)
   573  	})
   574  
   575  	ctx := context.Background()
   576  	card, _, err := client.Projects.UpdateProjectCard(ctx, 1, input)
   577  	if err != nil {
   578  		t.Errorf("Projects.UpdateProjectCard returned error: %v", err)
   579  	}
   580  
   581  	want := &ProjectCard{ID: Int64(1), Archived: Bool(false)}
   582  	if !cmp.Equal(card, want) {
   583  		t.Errorf("Projects.UpdateProjectCard returned %+v, want %+v", card, want)
   584  	}
   585  
   586  	const methodName = "UpdateProjectCard"
   587  	testBadOptions(t, methodName, func() (err error) {
   588  		_, _, err = client.Projects.UpdateProjectCard(ctx, -1, input)
   589  		return err
   590  	})
   591  
   592  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   593  		got, resp, err := client.Projects.UpdateProjectCard(ctx, 1, input)
   594  		if got != nil {
   595  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   596  		}
   597  		return resp, err
   598  	})
   599  }
   600  
   601  func TestProjectsService_DeleteProjectCard(t *testing.T) {
   602  	t.Parallel()
   603  	client, mux, _ := setup(t)
   604  
   605  	mux.HandleFunc("/projects/columns/cards/1", func(w http.ResponseWriter, r *http.Request) {
   606  		testMethod(t, r, "DELETE")
   607  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   608  	})
   609  
   610  	ctx := context.Background()
   611  	_, err := client.Projects.DeleteProjectCard(ctx, 1)
   612  	if err != nil {
   613  		t.Errorf("Projects.DeleteProjectCard returned error: %v", err)
   614  	}
   615  
   616  	const methodName = "DeleteProjectCard"
   617  	testBadOptions(t, methodName, func() (err error) {
   618  		_, err = client.Projects.DeleteProjectCard(ctx, -1)
   619  		return err
   620  	})
   621  
   622  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   623  		return client.Projects.DeleteProjectCard(ctx, 1)
   624  	})
   625  }
   626  
   627  func TestProjectsService_MoveProjectCard(t *testing.T) {
   628  	t.Parallel()
   629  	client, mux, _ := setup(t)
   630  
   631  	input := &ProjectCardMoveOptions{Position: "after:12345"}
   632  
   633  	mux.HandleFunc("/projects/columns/cards/1/moves", func(w http.ResponseWriter, r *http.Request) {
   634  		testMethod(t, r, "POST")
   635  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   636  
   637  		v := &ProjectCardMoveOptions{}
   638  		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
   639  		if !cmp.Equal(v, input) {
   640  			t.Errorf("Request body = %+v, want %+v", v, input)
   641  		}
   642  	})
   643  
   644  	ctx := context.Background()
   645  	_, err := client.Projects.MoveProjectCard(ctx, 1, input)
   646  	if err != nil {
   647  		t.Errorf("Projects.MoveProjectCard returned error: %v", err)
   648  	}
   649  
   650  	const methodName = "MoveProjectCard"
   651  	testBadOptions(t, methodName, func() (err error) {
   652  		_, err = client.Projects.MoveProjectCard(ctx, -1, input)
   653  		return err
   654  	})
   655  
   656  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   657  		return client.Projects.MoveProjectCard(ctx, 1, input)
   658  	})
   659  }
   660  
   661  func TestProjectsService_AddProjectCollaborator(t *testing.T) {
   662  	t.Parallel()
   663  	client, mux, _ := setup(t)
   664  
   665  	opt := &ProjectCollaboratorOptions{
   666  		Permission: String("admin"),
   667  	}
   668  
   669  	mux.HandleFunc("/projects/1/collaborators/u", func(w http.ResponseWriter, r *http.Request) {
   670  		testMethod(t, r, "PUT")
   671  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   672  
   673  		v := &ProjectCollaboratorOptions{}
   674  		assertNilError(t, json.NewDecoder(r.Body).Decode(v))
   675  		if !cmp.Equal(v, opt) {
   676  			t.Errorf("Request body = %+v, want %+v", v, opt)
   677  		}
   678  
   679  		w.WriteHeader(http.StatusNoContent)
   680  	})
   681  
   682  	ctx := context.Background()
   683  	_, err := client.Projects.AddProjectCollaborator(ctx, 1, "u", opt)
   684  	if err != nil {
   685  		t.Errorf("Projects.AddProjectCollaborator returned error: %v", err)
   686  	}
   687  
   688  	const methodName = "AddProjectCollaborator"
   689  	testBadOptions(t, methodName, func() (err error) {
   690  		_, err = client.Projects.AddProjectCollaborator(ctx, -1, "\n", opt)
   691  		return err
   692  	})
   693  
   694  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   695  		return client.Projects.AddProjectCollaborator(ctx, 1, "u", opt)
   696  	})
   697  }
   698  
   699  func TestProjectsService_AddCollaborator_invalidUser(t *testing.T) {
   700  	t.Parallel()
   701  	client, _, _ := setup(t)
   702  
   703  	ctx := context.Background()
   704  	_, err := client.Projects.AddProjectCollaborator(ctx, 1, "%", nil)
   705  	testURLParseError(t, err)
   706  }
   707  
   708  func TestProjectsService_RemoveCollaborator(t *testing.T) {
   709  	t.Parallel()
   710  	client, mux, _ := setup(t)
   711  
   712  	mux.HandleFunc("/projects/1/collaborators/u", func(w http.ResponseWriter, r *http.Request) {
   713  		testMethod(t, r, "DELETE")
   714  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   715  		w.WriteHeader(http.StatusNoContent)
   716  	})
   717  
   718  	ctx := context.Background()
   719  	_, err := client.Projects.RemoveProjectCollaborator(ctx, 1, "u")
   720  	if err != nil {
   721  		t.Errorf("Projects.RemoveProjectCollaborator returned error: %v", err)
   722  	}
   723  
   724  	const methodName = "RemoveProjectCollaborator"
   725  	testBadOptions(t, methodName, func() (err error) {
   726  		_, err = client.Projects.RemoveProjectCollaborator(ctx, -1, "\n")
   727  		return err
   728  	})
   729  
   730  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   731  		return client.Projects.RemoveProjectCollaborator(ctx, 1, "u")
   732  	})
   733  }
   734  
   735  func TestProjectsService_RemoveCollaborator_invalidUser(t *testing.T) {
   736  	t.Parallel()
   737  	client, _, _ := setup(t)
   738  
   739  	ctx := context.Background()
   740  	_, err := client.Projects.RemoveProjectCollaborator(ctx, 1, "%")
   741  	testURLParseError(t, err)
   742  }
   743  
   744  func TestProjectsService_ListCollaborators(t *testing.T) {
   745  	t.Parallel()
   746  	client, mux, _ := setup(t)
   747  
   748  	mux.HandleFunc("/projects/1/collaborators", func(w http.ResponseWriter, r *http.Request) {
   749  		testMethod(t, r, "GET")
   750  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   751  		testFormValues(t, r, values{"page": "2"})
   752  		fmt.Fprintf(w, `[{"id":1}, {"id":2}]`)
   753  	})
   754  
   755  	opt := &ListCollaboratorOptions{
   756  		ListOptions: ListOptions{Page: 2},
   757  	}
   758  	ctx := context.Background()
   759  	users, _, err := client.Projects.ListProjectCollaborators(ctx, 1, opt)
   760  	if err != nil {
   761  		t.Errorf("Projects.ListProjectCollaborators returned error: %v", err)
   762  	}
   763  
   764  	want := []*User{{ID: Int64(1)}, {ID: Int64(2)}}
   765  	if !cmp.Equal(users, want) {
   766  		t.Errorf("Projects.ListProjectCollaborators returned %+v, want %+v", users, want)
   767  	}
   768  
   769  	const methodName = "ListProjectCollaborators"
   770  	testBadOptions(t, methodName, func() (err error) {
   771  		_, _, err = client.Projects.ListProjectCollaborators(ctx, -1, opt)
   772  		return err
   773  	})
   774  
   775  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   776  		got, resp, err := client.Projects.ListProjectCollaborators(ctx, 1, opt)
   777  		if got != nil {
   778  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   779  		}
   780  		return resp, err
   781  	})
   782  }
   783  
   784  func TestProjectsService_ListCollaborators_withAffiliation(t *testing.T) {
   785  	t.Parallel()
   786  	client, mux, _ := setup(t)
   787  
   788  	mux.HandleFunc("/projects/1/collaborators", func(w http.ResponseWriter, r *http.Request) {
   789  		testMethod(t, r, "GET")
   790  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   791  		testFormValues(t, r, values{"affiliation": "all", "page": "2"})
   792  		fmt.Fprintf(w, `[{"id":1}, {"id":2}]`)
   793  	})
   794  
   795  	opt := &ListCollaboratorOptions{
   796  		ListOptions: ListOptions{Page: 2},
   797  		Affiliation: String("all"),
   798  	}
   799  	ctx := context.Background()
   800  	users, _, err := client.Projects.ListProjectCollaborators(ctx, 1, opt)
   801  	if err != nil {
   802  		t.Errorf("Projects.ListProjectCollaborators returned error: %v", err)
   803  	}
   804  
   805  	want := []*User{{ID: Int64(1)}, {ID: Int64(2)}}
   806  	if !cmp.Equal(users, want) {
   807  		t.Errorf("Projects.ListProjectCollaborators returned %+v, want %+v", users, want)
   808  	}
   809  }
   810  
   811  func TestProjectsService_ReviewProjectCollaboratorPermission(t *testing.T) {
   812  	t.Parallel()
   813  	client, mux, _ := setup(t)
   814  
   815  	mux.HandleFunc("/projects/1/collaborators/u/permission", func(w http.ResponseWriter, r *http.Request) {
   816  		testMethod(t, r, "GET")
   817  		testHeader(t, r, "Accept", mediaTypeProjectsPreview)
   818  		fmt.Fprintf(w, `{"permission":"admin","user":{"login":"u"}}`)
   819  	})
   820  
   821  	ctx := context.Background()
   822  	ppl, _, err := client.Projects.ReviewProjectCollaboratorPermission(ctx, 1, "u")
   823  	if err != nil {
   824  		t.Errorf("Projects.ReviewProjectCollaboratorPermission returned error: %v", err)
   825  	}
   826  
   827  	want := &ProjectPermissionLevel{
   828  		Permission: String("admin"),
   829  		User: &User{
   830  			Login: String("u"),
   831  		},
   832  	}
   833  
   834  	if !cmp.Equal(ppl, want) {
   835  		t.Errorf("Projects.ReviewProjectCollaboratorPermission returned %+v, want %+v", ppl, want)
   836  	}
   837  
   838  	const methodName = "ReviewProjectCollaboratorPermission"
   839  	testBadOptions(t, methodName, func() (err error) {
   840  		_, _, err = client.Projects.ReviewProjectCollaboratorPermission(ctx, -1, "\n")
   841  		return err
   842  	})
   843  
   844  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   845  		got, resp, err := client.Projects.ReviewProjectCollaboratorPermission(ctx, 1, "u")
   846  		if got != nil {
   847  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   848  		}
   849  		return resp, err
   850  	})
   851  }
   852  
   853  func TestProjectOptions_Marshal(t *testing.T) {
   854  	t.Parallel()
   855  	testJSONMarshal(t, &ProjectOptions{}, "{}")
   856  
   857  	u := &ProjectOptions{
   858  		Name:                   String("name"),
   859  		Body:                   String("body"),
   860  		State:                  String("state"),
   861  		OrganizationPermission: String("op"),
   862  		Private:                Bool(false),
   863  	}
   864  
   865  	want := `{
   866  		"name": "name",
   867  		"body": "body",
   868  		"state": "state",
   869  		"organization_permission": "op",
   870  		"private": false
   871  	}`
   872  
   873  	testJSONMarshal(t, u, want)
   874  }
   875  
   876  func TestProjectColumn_Marshal(t *testing.T) {
   877  	t.Parallel()
   878  	testJSONMarshal(t, &ProjectColumn{}, "{}")
   879  
   880  	u := &ProjectColumn{
   881  		ID:         Int64(1),
   882  		Name:       String("name"),
   883  		URL:        String("url"),
   884  		ProjectURL: String("purl"),
   885  		CardsURL:   String("curl"),
   886  		CreatedAt:  &Timestamp{referenceTime},
   887  		UpdatedAt:  &Timestamp{referenceTime},
   888  		NodeID:     String("onidp"),
   889  	}
   890  
   891  	want := `{
   892  		"id": 1,
   893  		"name": "name",
   894  		"url": "url",
   895  		"project_url": "purl",
   896  		"cards_url": "curl",
   897  		"created_at": ` + referenceTimeStr + `,
   898  		"updated_at": ` + referenceTimeStr + `,
   899  		"node_id": "onidp"
   900  	}`
   901  
   902  	testJSONMarshal(t, u, want)
   903  }
   904  
   905  func TestProjectColumnOptions_Marshal(t *testing.T) {
   906  	t.Parallel()
   907  	testJSONMarshal(t, &ProjectColumnOptions{}, "{}")
   908  
   909  	u := &ProjectColumnOptions{
   910  		Name: "name",
   911  	}
   912  
   913  	want := `{
   914  		"name": "name"
   915  	}`
   916  
   917  	testJSONMarshal(t, u, want)
   918  }
   919  
   920  func TestProjectColumnMoveOptions_Marshal(t *testing.T) {
   921  	t.Parallel()
   922  	testJSONMarshal(t, &ProjectColumnMoveOptions{}, "{}")
   923  
   924  	u := &ProjectColumnMoveOptions{
   925  		Position: "pos",
   926  	}
   927  
   928  	want := `{
   929  		"position": "pos"
   930  	}`
   931  
   932  	testJSONMarshal(t, u, want)
   933  }
   934  
   935  func TestProjectCard_Marshal(t *testing.T) {
   936  	t.Parallel()
   937  	testJSONMarshal(t, &ProjectCard{}, "{}")
   938  
   939  	u := &ProjectCard{
   940  		URL:        String("url"),
   941  		ColumnURL:  String("curl"),
   942  		ContentURL: String("conurl"),
   943  		ID:         Int64(1),
   944  		Note:       String("note"),
   945  		Creator: &User{
   946  			Login:           String("l"),
   947  			ID:              Int64(1),
   948  			URL:             String("u"),
   949  			AvatarURL:       String("a"),
   950  			GravatarID:      String("g"),
   951  			Name:            String("n"),
   952  			Company:         String("c"),
   953  			Blog:            String("b"),
   954  			Location:        String("l"),
   955  			Email:           String("e"),
   956  			Hireable:        Bool(true),
   957  			Bio:             String("b"),
   958  			TwitterUsername: String("t"),
   959  			PublicRepos:     Int(1),
   960  			Followers:       Int(1),
   961  			Following:       Int(1),
   962  			CreatedAt:       &Timestamp{referenceTime},
   963  			SuspendedAt:     &Timestamp{referenceTime},
   964  		},
   965  		CreatedAt:          &Timestamp{referenceTime},
   966  		UpdatedAt:          &Timestamp{referenceTime},
   967  		NodeID:             String("nid"),
   968  		Archived:           Bool(true),
   969  		ColumnID:           Int64(1),
   970  		ProjectID:          Int64(1),
   971  		ProjectURL:         String("purl"),
   972  		ColumnName:         String("cn"),
   973  		PreviousColumnName: String("pcn"),
   974  	}
   975  
   976  	want := `{
   977  		"url": "url",
   978  		"column_url": "curl",
   979  		"content_url": "conurl",
   980  		"id": 1,
   981  		"note": "note",
   982  		"creator": {
   983  			"login": "l",
   984  			"id": 1,
   985  			"avatar_url": "a",
   986  			"gravatar_id": "g",
   987  			"name": "n",
   988  			"company": "c",
   989  			"blog": "b",
   990  			"location": "l",
   991  			"email": "e",
   992  			"hireable": true,
   993  			"bio": "b",
   994  			"twitter_username": "t",
   995  			"public_repos": 1,
   996  			"followers": 1,
   997  			"following": 1,
   998  			"created_at": ` + referenceTimeStr + `,
   999  			"suspended_at": ` + referenceTimeStr + `,
  1000  			"url": "u"
  1001  		},
  1002  		"created_at": ` + referenceTimeStr + `,
  1003  		"updated_at": ` + referenceTimeStr + `,
  1004  		"node_id": "nid",
  1005  		"archived": true,
  1006  		"column_id": 1,
  1007  		"project_id": 1,
  1008  		"project_url": "purl",
  1009  		"column_name": "cn",
  1010  		"previous_column_name": "pcn"
  1011  	}`
  1012  
  1013  	testJSONMarshal(t, u, want)
  1014  }
  1015  
  1016  func TestProjectCardOptions_Marshal(t *testing.T) {
  1017  	t.Parallel()
  1018  	testJSONMarshal(t, &ProjectCardOptions{}, "{}")
  1019  
  1020  	u := &ProjectCardOptions{
  1021  		Note:        "note",
  1022  		ContentID:   1,
  1023  		ContentType: "ct",
  1024  		Archived:    Bool(false),
  1025  	}
  1026  
  1027  	want := `{
  1028  		"note": "note",
  1029  		"content_id": 1,
  1030  		"content_type": "ct",
  1031  		"archived": false
  1032  	}`
  1033  
  1034  	testJSONMarshal(t, u, want)
  1035  }
  1036  
  1037  func TestProjectCardMoveOptions_Marshal(t *testing.T) {
  1038  	t.Parallel()
  1039  	testJSONMarshal(t, &ProjectCardMoveOptions{}, "{}")
  1040  
  1041  	u := &ProjectCardMoveOptions{
  1042  		Position: "pos",
  1043  		ColumnID: 1,
  1044  	}
  1045  
  1046  	want := `{
  1047  		"position": "pos",
  1048  		"column_id": 1
  1049  	}`
  1050  
  1051  	testJSONMarshal(t, u, want)
  1052  }
  1053  
  1054  func TestProjectCollaboratorOptions_Marshal(t *testing.T) {
  1055  	t.Parallel()
  1056  	testJSONMarshal(t, &ProjectCollaboratorOptions{}, "{}")
  1057  
  1058  	u := &ProjectCollaboratorOptions{
  1059  		Permission: String("per"),
  1060  	}
  1061  
  1062  	want := `{
  1063  		"permission": "per"
  1064  	}`
  1065  
  1066  	testJSONMarshal(t, u, want)
  1067  }
  1068  
  1069  func TestProjectPermissionLevel_Marshal(t *testing.T) {
  1070  	t.Parallel()
  1071  	testJSONMarshal(t, &ProjectPermissionLevel{}, "{}")
  1072  
  1073  	u := &ProjectPermissionLevel{
  1074  		Permission: String("per"),
  1075  		User: &User{
  1076  			Login:           String("l"),
  1077  			ID:              Int64(1),
  1078  			URL:             String("u"),
  1079  			AvatarURL:       String("a"),
  1080  			GravatarID:      String("g"),
  1081  			Name:            String("n"),
  1082  			Company:         String("c"),
  1083  			Blog:            String("b"),
  1084  			Location:        String("l"),
  1085  			Email:           String("e"),
  1086  			Hireable:        Bool(true),
  1087  			Bio:             String("b"),
  1088  			TwitterUsername: String("t"),
  1089  			PublicRepos:     Int(1),
  1090  			Followers:       Int(1),
  1091  			Following:       Int(1),
  1092  			CreatedAt:       &Timestamp{referenceTime},
  1093  			SuspendedAt:     &Timestamp{referenceTime},
  1094  		},
  1095  	}
  1096  
  1097  	want := `{
  1098  		"permission": "per",
  1099  		"user": {
  1100  			"login": "l",
  1101  			"id": 1,
  1102  			"avatar_url": "a",
  1103  			"gravatar_id": "g",
  1104  			"name": "n",
  1105  			"company": "c",
  1106  			"blog": "b",
  1107  			"location": "l",
  1108  			"email": "e",
  1109  			"hireable": true,
  1110  			"bio": "b",
  1111  			"twitter_username": "t",
  1112  			"public_repos": 1,
  1113  			"followers": 1,
  1114  			"following": 1,
  1115  			"created_at": ` + referenceTimeStr + `,
  1116  			"suspended_at": ` + referenceTimeStr + `,
  1117  			"url": "u"
  1118  		}
  1119  	}`
  1120  
  1121  	testJSONMarshal(t, u, want)
  1122  }