github.com/google/go-github/v64@v64.0.0/github/copilot_test.go (about)

     1  // Copyright 2023 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  // Test invalid JSON responses, vlaid responses are covered in the other tests
    20  func TestCopilotSeatDetails_UnmarshalJSON(t *testing.T) {
    21  	tests := []struct {
    22  		name    string
    23  		data    string
    24  		want    *CopilotSeatDetails
    25  		wantErr bool
    26  	}{
    27  		{
    28  			name: "Invalid JSON",
    29  			data: `{`,
    30  			want: &CopilotSeatDetails{
    31  				Assignee: nil,
    32  			},
    33  			wantErr: true,
    34  		},
    35  		{
    36  			name: "Invalid top level type",
    37  			data: `{
    38  					"assignee": {
    39  						"type": "User",
    40  						"name": "octokittens",
    41  						"id": 1
    42  					},
    43  					"assigning_team": "this should be an object"
    44  				}`,
    45  			want:    &CopilotSeatDetails{},
    46  			wantErr: true,
    47  		},
    48  		{
    49  			name: "No Type Field",
    50  			data: `{
    51  					"assignee": {
    52  						"name": "octokittens",
    53  						"id": 1
    54  					}
    55  				}`,
    56  			want:    &CopilotSeatDetails{},
    57  			wantErr: true,
    58  		},
    59  		{
    60  			name: "Invalid Assignee Field Type",
    61  			data: `{
    62  					"assignee": "test"
    63  				}`,
    64  			want:    &CopilotSeatDetails{},
    65  			wantErr: true,
    66  		},
    67  		{
    68  			name: "Invalid Assignee Type",
    69  			data: `{
    70  					"assignee": {
    71  						"name": "octokittens",
    72  						"id": 1,
    73  						"type": []
    74  					}
    75  				}`,
    76  			want:    &CopilotSeatDetails{},
    77  			wantErr: true,
    78  		},
    79  		{
    80  			name: "Invalid User",
    81  			data: `{
    82  					"assignee": {
    83  						"type": "User",
    84  						"id": "bad"
    85  					}
    86  				}`,
    87  			want:    &CopilotSeatDetails{},
    88  			wantErr: true,
    89  		},
    90  		{
    91  			name: "Invalid Team",
    92  			data: `{
    93  					"assignee": {
    94  						"type": "Team",
    95  						"id": "bad"
    96  					}
    97  				}`,
    98  			want:    &CopilotSeatDetails{},
    99  			wantErr: true,
   100  		},
   101  		{
   102  			name: "Invalid Organization",
   103  			data: `{
   104  					"assignee": {
   105  						"type": "Organization",
   106  						"id": "bad"
   107  					}
   108  				}`,
   109  			want:    &CopilotSeatDetails{},
   110  			wantErr: true,
   111  		},
   112  	}
   113  
   114  	for _, tc := range tests {
   115  		seatDetails := &CopilotSeatDetails{}
   116  
   117  		t.Run(tc.name, func(t *testing.T) {
   118  			err := json.Unmarshal([]byte(tc.data), seatDetails)
   119  			if err == nil && tc.wantErr {
   120  				t.Errorf("CopilotSeatDetails.UnmarshalJSON returned nil instead of an error")
   121  			}
   122  			if err != nil && !tc.wantErr {
   123  				t.Errorf("CopilotSeatDetails.UnmarshalJSON returned an unexpected error: %v", err)
   124  			}
   125  			if !cmp.Equal(tc.want, seatDetails) {
   126  				t.Errorf("CopilotSeatDetails.UnmarshalJSON expected %+v, got %+v", tc.want, seatDetails)
   127  			}
   128  		})
   129  	}
   130  }
   131  
   132  func TestCopilotService_GetSeatDetailsUser(t *testing.T) {
   133  	data := `{
   134  				"assignee": {
   135  					"type": "User",
   136  					"id": 1
   137  				}
   138  			}`
   139  
   140  	seatDetails := &CopilotSeatDetails{}
   141  
   142  	err := json.Unmarshal([]byte(data), seatDetails)
   143  	if err != nil {
   144  		t.Errorf("CopilotSeatDetails.UnmarshalJSON returned an unexpected error: %v", err)
   145  	}
   146  
   147  	want := &User{
   148  		ID:   Int64(1),
   149  		Type: String("User"),
   150  	}
   151  
   152  	if got, ok := seatDetails.GetUser(); ok && !cmp.Equal(got, want) {
   153  		t.Errorf("CopilotSeatDetails.GetTeam returned %+v, want %+v", got, want)
   154  	} else if !ok {
   155  		t.Errorf("CopilotSeatDetails.GetUser returned false, expected true")
   156  	}
   157  
   158  	data = `{
   159  				"assignee": {
   160  					"type": "Organization",
   161  					"id": 1
   162  				}
   163  			}`
   164  
   165  	bad := &Organization{
   166  		ID:   Int64(1),
   167  		Type: String("Organization"),
   168  	}
   169  
   170  	err = json.Unmarshal([]byte(data), seatDetails)
   171  	if err != nil {
   172  		t.Errorf("CopilotSeatDetails.UnmarshalJSON returned an unexpected error: %v", err)
   173  	}
   174  
   175  	if got, ok := seatDetails.GetUser(); ok {
   176  		t.Errorf("CopilotSeatDetails.GetUser returned true, expected false. Returned %v, expected %v", got, bad)
   177  	}
   178  }
   179  
   180  func TestCopilotService_GetSeatDetailsTeam(t *testing.T) {
   181  	data := `{
   182  				"assignee": {
   183  					"type": "Team",
   184  					"id": 1
   185  				}
   186  			}`
   187  
   188  	seatDetails := &CopilotSeatDetails{}
   189  
   190  	err := json.Unmarshal([]byte(data), seatDetails)
   191  	if err != nil {
   192  		t.Errorf("CopilotSeatDetails.UnmarshalJSON returned an unexpected error: %v", err)
   193  	}
   194  
   195  	want := &Team{
   196  		ID: Int64(1),
   197  	}
   198  
   199  	if got, ok := seatDetails.GetTeam(); ok && !cmp.Equal(got, want) {
   200  		t.Errorf("CopilotSeatDetails.GetTeam returned %+v, want %+v", got, want)
   201  	} else if !ok {
   202  		t.Errorf("CopilotSeatDetails.GetTeam returned false, expected true")
   203  	}
   204  
   205  	data = `{
   206  				"assignee": {
   207  					"type": "User",
   208  					"id": 1
   209  				}
   210  			}`
   211  
   212  	bad := &User{
   213  		ID:   Int64(1),
   214  		Type: String("User"),
   215  	}
   216  
   217  	err = json.Unmarshal([]byte(data), seatDetails)
   218  	if err != nil {
   219  		t.Errorf("CopilotSeatDetails.UnmarshalJSON returned an unexpected error: %v", err)
   220  	}
   221  
   222  	if got, ok := seatDetails.GetTeam(); ok {
   223  		t.Errorf("CopilotSeatDetails.GetTeam returned true, expected false. Returned %v, expected %v", got, bad)
   224  	}
   225  }
   226  
   227  func TestCopilotService_GetSeatDetailsOrganization(t *testing.T) {
   228  	data := `{
   229  				"assignee": {
   230  					"type": "Organization",
   231  					"id": 1
   232  				}
   233  			}`
   234  
   235  	seatDetails := &CopilotSeatDetails{}
   236  
   237  	err := json.Unmarshal([]byte(data), seatDetails)
   238  	if err != nil {
   239  		t.Errorf("CopilotSeatDetails.UnmarshalJSON returned an unexpected error: %v", err)
   240  	}
   241  
   242  	want := &Organization{
   243  		ID:   Int64(1),
   244  		Type: String("Organization"),
   245  	}
   246  
   247  	if got, ok := seatDetails.GetOrganization(); ok && !cmp.Equal(got, want) {
   248  		t.Errorf("CopilotSeatDetails.GetOrganization returned %+v, want %+v", got, want)
   249  	} else if !ok {
   250  		t.Errorf("CopilotSeatDetails.GetOrganization returned false, expected true")
   251  	}
   252  
   253  	data = `{
   254  				"assignee": {
   255  					"type": "Team",
   256  					"id": 1
   257  				}
   258  			}`
   259  
   260  	bad := &Team{
   261  		ID: Int64(1),
   262  	}
   263  
   264  	err = json.Unmarshal([]byte(data), seatDetails)
   265  	if err != nil {
   266  		t.Errorf("CopilotSeatDetails.UnmarshalJSON returned an unexpected error: %v", err)
   267  	}
   268  
   269  	if got, ok := seatDetails.GetOrganization(); ok {
   270  		t.Errorf("CopilotSeatDetails.GetOrganization returned true, expected false. Returned %v, expected %v", got, bad)
   271  	}
   272  }
   273  
   274  func TestCopilotService_GetCopilotBilling(t *testing.T) {
   275  	client, mux, _, teardown := setup()
   276  	defer teardown()
   277  
   278  	mux.HandleFunc("/orgs/o/copilot/billing", func(w http.ResponseWriter, r *http.Request) {
   279  		testMethod(t, r, "GET")
   280  		fmt.Fprint(w, `{
   281  			"seat_breakdown": {
   282  				"total": 12,
   283  				"added_this_cycle": 9,
   284  				"pending_invitation": 0,
   285  				"pending_cancellation": 0,
   286  				"active_this_cycle": 12,
   287  				"inactive_this_cycle": 11
   288  			},
   289  			"seat_management_setting": "assign_selected",
   290  			"public_code_suggestions": "block"
   291  			}`)
   292  	})
   293  
   294  	ctx := context.Background()
   295  	got, _, err := client.Copilot.GetCopilotBilling(ctx, "o")
   296  	if err != nil {
   297  		t.Errorf("Copilot.GetCopilotBilling returned error: %v", err)
   298  	}
   299  
   300  	want := &CopilotOrganizationDetails{
   301  		SeatBreakdown: &CopilotSeatBreakdown{
   302  			Total:               12,
   303  			AddedThisCycle:      9,
   304  			PendingInvitation:   0,
   305  			PendingCancellation: 0,
   306  			ActiveThisCycle:     12,
   307  			InactiveThisCycle:   11,
   308  		},
   309  		PublicCodeSuggestions: "block",
   310  		CopilotChat:           "",
   311  		SeatManagementSetting: "assign_selected",
   312  	}
   313  	if !cmp.Equal(got, want) {
   314  		t.Errorf("Copilot.GetCopilotBilling returned %+v, want %+v", got, want)
   315  	}
   316  
   317  	const methodName = "GetCopilotBilling"
   318  
   319  	testBadOptions(t, methodName, func() (err error) {
   320  		_, _, err = client.Copilot.GetCopilotBilling(ctx, "\n")
   321  		return err
   322  	})
   323  
   324  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   325  		got, resp, err := client.Copilot.GetCopilotBilling(ctx, "o")
   326  		if got != nil {
   327  			t.Errorf("Copilot.GetCopilotBilling returned %+v, want nil", got)
   328  		}
   329  		return resp, err
   330  	})
   331  }
   332  
   333  func TestCopilotService_ListCopilotSeats(t *testing.T) {
   334  	client, mux, _, teardown := setup()
   335  	defer teardown()
   336  
   337  	mux.HandleFunc("/orgs/o/copilot/billing/seats", func(w http.ResponseWriter, r *http.Request) {
   338  		testMethod(t, r, "GET")
   339  		testFormValues(t, r, values{
   340  			"per_page": "100",
   341  			"page":     "1",
   342  		})
   343  		fmt.Fprint(w, `{
   344  				"total_seats": 4,
   345  				"seats": [
   346  					{
   347  						"created_at": "2021-08-03T18:00:00-06:00",
   348  						"updated_at": "2021-09-23T15:00:00-06:00",
   349  						"pending_cancellation_date": null,
   350  						"last_activity_at": "2021-10-14T00:53:32-06:00",
   351  						"last_activity_editor": "vscode/1.77.3/copilot/1.86.82",
   352  						"assignee": {
   353  							"login": "octocat",
   354  							"id": 1,
   355  							"node_id": "MDQ6VXNlcjE=",
   356  							"avatar_url": "https://github.com/images/error/octocat_happy.gif",
   357  							"gravatar_id": "",
   358  							"url": "https://api.github.com/users/octocat",
   359  							"html_url": "https://github.com/octocat",
   360  							"followers_url": "https://api.github.com/users/octocat/followers",
   361  							"following_url": "https://api.github.com/users/octocat/following{/other_user}",
   362  							"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
   363  							"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
   364  							"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
   365  							"organizations_url": "https://api.github.com/users/octocat/orgs",
   366  							"repos_url": "https://api.github.com/users/octocat/repos",
   367  							"events_url": "https://api.github.com/users/octocat/events{/privacy}",
   368  							"received_events_url": "https://api.github.com/users/octocat/received_events",
   369  							"type": "User",
   370  							"site_admin": false
   371  						},
   372  						"assigning_team": {
   373  							"id": 1,
   374  							"node_id": "MDQ6VGVhbTE=",
   375  							"url": "https://api.github.com/teams/1",
   376  							"html_url": "https://github.com/orgs/github/teams/justice-league",
   377  							"name": "Justice League",
   378  							"slug": "justice-league",
   379  							"description": "A great team.",
   380  							"privacy": "closed",
   381  							"notification_setting": "notifications_enabled",
   382  							"permission": "admin",
   383  							"members_url": "https://api.github.com/teams/1/members{/member}",
   384  							"repositories_url": "https://api.github.com/teams/1/repos",
   385  							"parent": null
   386  						}
   387  					},
   388  					{
   389  						"created_at": "2021-09-23T18:00:00-06:00",
   390  						"updated_at": "2021-09-23T15:00:00-06:00",
   391  						"pending_cancellation_date": "2021-11-01",
   392  						"last_activity_at": "2021-10-13T00:53:32-06:00",
   393  						"last_activity_editor": "vscode/1.77.3/copilot/1.86.82",
   394  						"assignee": {
   395  							"login": "octokitten",
   396  							"id": 1,
   397  							"node_id": "MDQ76VNlcjE=",
   398  							"avatar_url": "https://github.com/images/error/octokitten_happy.gif",
   399  							"gravatar_id": "",
   400  							"url": "https://api.github.com/users/octokitten",
   401  							"html_url": "https://github.com/octokitten",
   402  							"followers_url": "https://api.github.com/users/octokitten/followers",
   403  							"following_url": "https://api.github.com/users/octokitten/following{/other_user}",
   404  							"gists_url": "https://api.github.com/users/octokitten/gists{/gist_id}",
   405  							"starred_url": "https://api.github.com/users/octokitten/starred{/owner}{/repo}",
   406  							"subscriptions_url": "https://api.github.com/users/octokitten/subscriptions",
   407  							"organizations_url": "https://api.github.com/users/octokitten/orgs",
   408  							"repos_url": "https://api.github.com/users/octokitten/repos",
   409  							"events_url": "https://api.github.com/users/octokitten/events{/privacy}",
   410  							"received_events_url": "https://api.github.com/users/octokitten/received_events",
   411  							"type": "User",
   412  							"site_admin": false
   413  						}
   414  					},
   415  					{
   416  						"created_at": "2021-09-23T18:00:00-06:00",
   417  						"updated_at": "2021-09-23T15:00:00-06:00",
   418  						"pending_cancellation_date": "2021-11-01",
   419  						"last_activity_at": "2021-10-13T00:53:32-06:00",
   420  						"last_activity_editor": "vscode/1.77.3/copilot/1.86.82",
   421  						"assignee": {
   422  							"name": "octokittens",
   423  							"id": 1,
   424  							"type": "Team"
   425  						}
   426  					},
   427  					{
   428  						"created_at": "2021-09-23T18:00:00-06:00",
   429  						"updated_at": "2021-09-23T15:00:00-06:00",
   430  						"pending_cancellation_date": "2021-11-01",
   431  						"last_activity_at": "2021-10-13T00:53:32-06:00",
   432  						"last_activity_editor": "vscode/1.77.3/copilot/1.86.82",
   433  						"assignee": {
   434  							"name": "octocats",
   435  							"id": 1,
   436  							"type": "Organization"
   437  						}
   438  					}
   439  				]
   440  			}`)
   441  	})
   442  
   443  	tmp, err := time.Parse(time.RFC3339, "2021-08-03T18:00:00-06:00")
   444  	if err != nil {
   445  		panic(err)
   446  	}
   447  	createdAt1 := Timestamp{tmp}
   448  
   449  	tmp, err = time.Parse(time.RFC3339, "2021-09-23T15:00:00-06:00")
   450  	if err != nil {
   451  		panic(err)
   452  	}
   453  	updatedAt1 := Timestamp{tmp}
   454  
   455  	tmp, err = time.Parse(time.RFC3339, "2021-10-14T00:53:32-06:00")
   456  	if err != nil {
   457  		panic(err)
   458  	}
   459  	lastActivityAt1 := Timestamp{tmp}
   460  
   461  	tmp, err = time.Parse(time.RFC3339, "2021-09-23T18:00:00-06:00")
   462  	if err != nil {
   463  		panic(err)
   464  	}
   465  	createdAt2 := Timestamp{tmp}
   466  
   467  	tmp, err = time.Parse(time.RFC3339, "2021-09-23T15:00:00-06:00")
   468  	if err != nil {
   469  		panic(err)
   470  	}
   471  	updatedAt2 := Timestamp{tmp}
   472  
   473  	tmp, err = time.Parse(time.RFC3339, "2021-10-13T00:53:32-06:00")
   474  	if err != nil {
   475  		panic(err)
   476  	}
   477  	lastActivityAt2 := Timestamp{tmp}
   478  
   479  	ctx := context.Background()
   480  	opts := &ListOptions{Page: 1, PerPage: 100}
   481  	got, _, err := client.Copilot.ListCopilotSeats(ctx, "o", opts)
   482  	if err != nil {
   483  		t.Errorf("Copilot.ListCopilotSeats returned error: %v", err)
   484  	}
   485  
   486  	want := &ListCopilotSeatsResponse{
   487  		TotalSeats: 4,
   488  		Seats: []*CopilotSeatDetails{
   489  			{
   490  				Assignee: &User{
   491  					Login:             String("octocat"),
   492  					ID:                Int64(1),
   493  					NodeID:            String("MDQ6VXNlcjE="),
   494  					AvatarURL:         String("https://github.com/images/error/octocat_happy.gif"),
   495  					GravatarID:        String(""),
   496  					URL:               String("https://api.github.com/users/octocat"),
   497  					HTMLURL:           String("https://github.com/octocat"),
   498  					FollowersURL:      String("https://api.github.com/users/octocat/followers"),
   499  					FollowingURL:      String("https://api.github.com/users/octocat/following{/other_user}"),
   500  					GistsURL:          String("https://api.github.com/users/octocat/gists{/gist_id}"),
   501  					StarredURL:        String("https://api.github.com/users/octocat/starred{/owner}{/repo}"),
   502  					SubscriptionsURL:  String("https://api.github.com/users/octocat/subscriptions"),
   503  					OrganizationsURL:  String("https://api.github.com/users/octocat/orgs"),
   504  					ReposURL:          String("https://api.github.com/users/octocat/repos"),
   505  					EventsURL:         String("https://api.github.com/users/octocat/events{/privacy}"),
   506  					ReceivedEventsURL: String("https://api.github.com/users/octocat/received_events"),
   507  					Type:              String("User"),
   508  					SiteAdmin:         Bool(false),
   509  				},
   510  				AssigningTeam: &Team{
   511  					ID:              Int64(1),
   512  					NodeID:          String("MDQ6VGVhbTE="),
   513  					URL:             String("https://api.github.com/teams/1"),
   514  					HTMLURL:         String("https://github.com/orgs/github/teams/justice-league"),
   515  					Name:            String("Justice League"),
   516  					Slug:            String("justice-league"),
   517  					Description:     String("A great team."),
   518  					Privacy:         String("closed"),
   519  					Permission:      String("admin"),
   520  					MembersURL:      String("https://api.github.com/teams/1/members{/member}"),
   521  					RepositoriesURL: String("https://api.github.com/teams/1/repos"),
   522  					Parent:          nil,
   523  				},
   524  				CreatedAt:               &createdAt1,
   525  				UpdatedAt:               &updatedAt1,
   526  				PendingCancellationDate: nil,
   527  				LastActivityAt:          &lastActivityAt1,
   528  				LastActivityEditor:      String("vscode/1.77.3/copilot/1.86.82"),
   529  			},
   530  			{
   531  				Assignee: &User{
   532  					Login:             String("octokitten"),
   533  					ID:                Int64(1),
   534  					NodeID:            String("MDQ76VNlcjE="),
   535  					AvatarURL:         String("https://github.com/images/error/octokitten_happy.gif"),
   536  					GravatarID:        String(""),
   537  					URL:               String("https://api.github.com/users/octokitten"),
   538  					HTMLURL:           String("https://github.com/octokitten"),
   539  					FollowersURL:      String("https://api.github.com/users/octokitten/followers"),
   540  					FollowingURL:      String("https://api.github.com/users/octokitten/following{/other_user}"),
   541  					GistsURL:          String("https://api.github.com/users/octokitten/gists{/gist_id}"),
   542  					StarredURL:        String("https://api.github.com/users/octokitten/starred{/owner}{/repo}"),
   543  					SubscriptionsURL:  String("https://api.github.com/users/octokitten/subscriptions"),
   544  					OrganizationsURL:  String("https://api.github.com/users/octokitten/orgs"),
   545  					ReposURL:          String("https://api.github.com/users/octokitten/repos"),
   546  					EventsURL:         String("https://api.github.com/users/octokitten/events{/privacy}"),
   547  					ReceivedEventsURL: String("https://api.github.com/users/octokitten/received_events"),
   548  					Type:              String("User"),
   549  					SiteAdmin:         Bool(false),
   550  				},
   551  				AssigningTeam:           nil,
   552  				CreatedAt:               &createdAt2,
   553  				UpdatedAt:               &updatedAt2,
   554  				PendingCancellationDate: String("2021-11-01"),
   555  				LastActivityAt:          &lastActivityAt2,
   556  				LastActivityEditor:      String("vscode/1.77.3/copilot/1.86.82"),
   557  			},
   558  			{
   559  				Assignee: &Team{
   560  					ID:   Int64(1),
   561  					Name: String("octokittens"),
   562  				},
   563  				AssigningTeam:           nil,
   564  				CreatedAt:               &createdAt2,
   565  				UpdatedAt:               &updatedAt2,
   566  				PendingCancellationDate: String("2021-11-01"),
   567  				LastActivityAt:          &lastActivityAt2,
   568  				LastActivityEditor:      String("vscode/1.77.3/copilot/1.86.82"),
   569  			},
   570  			{
   571  				Assignee: &Organization{
   572  					ID:   Int64(1),
   573  					Name: String("octocats"),
   574  					Type: String("Organization"),
   575  				},
   576  				AssigningTeam:           nil,
   577  				CreatedAt:               &createdAt2,
   578  				UpdatedAt:               &updatedAt2,
   579  				PendingCancellationDate: String("2021-11-01"),
   580  				LastActivityAt:          &lastActivityAt2,
   581  				LastActivityEditor:      String("vscode/1.77.3/copilot/1.86.82"),
   582  			},
   583  		},
   584  	}
   585  
   586  	if !cmp.Equal(got, want) {
   587  		t.Errorf("Copilot.ListCopilotSeats returned %+v, want %+v", got, want)
   588  	}
   589  
   590  	const methodName = "ListCopilotSeats"
   591  
   592  	testBadOptions(t, methodName, func() (err error) {
   593  		_, _, err = client.Copilot.ListCopilotSeats(ctx, "\n", opts)
   594  		return err
   595  	})
   596  
   597  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   598  		got, resp, err := client.Copilot.ListCopilotSeats(ctx, "o", opts)
   599  		if got != nil {
   600  			t.Errorf("Copilot.ListCopilotSeats returned %+v, want nil", got)
   601  		}
   602  		return resp, err
   603  	})
   604  }
   605  
   606  func TestCopilotService_AddCopilotTeams(t *testing.T) {
   607  	client, mux, _, teardown := setup()
   608  	defer teardown()
   609  
   610  	mux.HandleFunc("/orgs/o/copilot/billing/selected_teams", func(w http.ResponseWriter, r *http.Request) {
   611  		testMethod(t, r, "POST")
   612  		testBody(t, r, `{"selected_teams":["team1","team2"]}`+"\n")
   613  		fmt.Fprint(w, `{"seats_created": 2}`)
   614  	})
   615  
   616  	ctx := context.Background()
   617  	got, _, err := client.Copilot.AddCopilotTeams(ctx, "o", []string{"team1", "team2"})
   618  	if err != nil {
   619  		t.Errorf("Copilot.AddCopilotTeams returned error: %v", err)
   620  	}
   621  
   622  	want := &SeatAssignments{SeatsCreated: 2}
   623  
   624  	if !cmp.Equal(got, want) {
   625  		t.Errorf("Copilot.AddCopilotTeams returned %+v, want %+v", got, want)
   626  	}
   627  
   628  	const methodName = "AddCopilotTeams"
   629  
   630  	testBadOptions(t, methodName, func() (err error) {
   631  		_, _, err = client.Copilot.AddCopilotTeams(ctx, "\n", []string{"team1", "team2"})
   632  		return err
   633  	})
   634  
   635  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   636  		got, resp, err := client.Copilot.AddCopilotTeams(ctx, "o", []string{"team1", "team2"})
   637  		if got != nil {
   638  			t.Errorf("Copilot.AddCopilotTeams returned %+v, want nil", got)
   639  		}
   640  		return resp, err
   641  	})
   642  }
   643  
   644  func TestCopilotService_RemoveCopilotTeams(t *testing.T) {
   645  	client, mux, _, teardown := setup()
   646  	defer teardown()
   647  
   648  	mux.HandleFunc("/orgs/o/copilot/billing/selected_teams", func(w http.ResponseWriter, r *http.Request) {
   649  		testMethod(t, r, "DELETE")
   650  		testBody(t, r, `{"selected_teams":["team1","team2"]}`+"\n")
   651  		fmt.Fprint(w, `{"seats_cancelled": 2}`)
   652  	})
   653  
   654  	ctx := context.Background()
   655  	got, _, err := client.Copilot.RemoveCopilotTeams(ctx, "o", []string{"team1", "team2"})
   656  	if err != nil {
   657  		t.Errorf("Copilot.RemoveCopilotTeams returned error: %v", err)
   658  	}
   659  
   660  	want := &SeatCancellations{SeatsCancelled: 2}
   661  
   662  	if !cmp.Equal(got, want) {
   663  		t.Errorf("Copilot.RemoveCopilotTeams returned %+v, want %+v", got, want)
   664  	}
   665  
   666  	const methodName = "RemoveCopilotTeams"
   667  
   668  	testBadOptions(t, methodName, func() (err error) {
   669  		_, _, err = client.Copilot.RemoveCopilotTeams(ctx, "\n", []string{"team1", "team2"})
   670  		return err
   671  	})
   672  
   673  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   674  		got, resp, err := client.Copilot.RemoveCopilotTeams(ctx, "o", []string{"team1", "team2"})
   675  		if got != nil {
   676  			t.Errorf("Copilot.RemoveCopilotTeams returned %+v, want nil", got)
   677  		}
   678  		return resp, err
   679  	})
   680  }
   681  
   682  func TestCopilotService_AddCopilotUsers(t *testing.T) {
   683  	client, mux, _, teardown := setup()
   684  	defer teardown()
   685  
   686  	mux.HandleFunc("/orgs/o/copilot/billing/selected_users", func(w http.ResponseWriter, r *http.Request) {
   687  		testMethod(t, r, "POST")
   688  		testBody(t, r, `{"selected_usernames":["user1","user2"]}`+"\n")
   689  		fmt.Fprint(w, `{"seats_created": 2}`)
   690  	})
   691  
   692  	ctx := context.Background()
   693  	got, _, err := client.Copilot.AddCopilotUsers(ctx, "o", []string{"user1", "user2"})
   694  	if err != nil {
   695  		t.Errorf("Copilot.AddCopilotUsers returned error: %v", err)
   696  	}
   697  
   698  	want := &SeatAssignments{SeatsCreated: 2}
   699  
   700  	if !cmp.Equal(got, want) {
   701  		t.Errorf("Copilot.AddCopilotUsers returned %+v, want %+v", got, want)
   702  	}
   703  
   704  	const methodName = "AddCopilotUsers"
   705  
   706  	testBadOptions(t, methodName, func() (err error) {
   707  		_, _, err = client.Copilot.AddCopilotUsers(ctx, "\n", []string{"user1", "user2"})
   708  		return err
   709  	})
   710  
   711  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   712  		got, resp, err := client.Copilot.AddCopilotUsers(ctx, "o", []string{"user1", "user2"})
   713  		if got != nil {
   714  			t.Errorf("Copilot.AddCopilotUsers returned %+v, want nil", got)
   715  		}
   716  		return resp, err
   717  	})
   718  }
   719  
   720  func TestCopilotService_RemoveCopilotUsers(t *testing.T) {
   721  	client, mux, _, teardown := setup()
   722  	defer teardown()
   723  
   724  	mux.HandleFunc("/orgs/o/copilot/billing/selected_users", func(w http.ResponseWriter, r *http.Request) {
   725  		testMethod(t, r, "DELETE")
   726  		testBody(t, r, `{"selected_usernames":["user1","user2"]}`+"\n")
   727  		fmt.Fprint(w, `{"seats_cancelled": 2}`)
   728  	})
   729  
   730  	ctx := context.Background()
   731  	got, _, err := client.Copilot.RemoveCopilotUsers(ctx, "o", []string{"user1", "user2"})
   732  	if err != nil {
   733  		t.Errorf("Copilot.RemoveCopilotUsers returned error: %v", err)
   734  	}
   735  
   736  	want := &SeatCancellations{SeatsCancelled: 2}
   737  
   738  	if !cmp.Equal(got, want) {
   739  		t.Errorf("Copilot.RemoveCopilotUsers returned %+v, want %+v", got, want)
   740  	}
   741  
   742  	const methodName = "RemoveCopilotUsers"
   743  
   744  	testBadOptions(t, methodName, func() (err error) {
   745  		_, _, err = client.Copilot.RemoveCopilotUsers(ctx, "\n", []string{"user1", "user2"})
   746  		return err
   747  	})
   748  
   749  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   750  		got, resp, err := client.Copilot.RemoveCopilotUsers(ctx, "o", []string{"user1", "user2"})
   751  		if got != nil {
   752  			t.Errorf("Copilot.RemoveCopilotUsers returned %+v, want nil", got)
   753  		}
   754  		return resp, err
   755  	})
   756  }
   757  
   758  func TestCopilotService_GetSeatDetails(t *testing.T) {
   759  	client, mux, _, teardown := setup()
   760  	defer teardown()
   761  
   762  	mux.HandleFunc("/orgs/o/members/u/copilot", func(w http.ResponseWriter, r *http.Request) {
   763  		testMethod(t, r, "GET")
   764  		fmt.Fprint(w, `{
   765  				"created_at": "2021-08-03T18:00:00-06:00",
   766  				"updated_at": "2021-09-23T15:00:00-06:00",
   767  				"pending_cancellation_date": null,
   768  				"last_activity_at": "2021-10-14T00:53:32-06:00",
   769  				"last_activity_editor": "vscode/1.77.3/copilot/1.86.82",
   770  				"assignee": {
   771  					"login": "octocat",
   772  					"id": 1,
   773  					"node_id": "MDQ6VXNlcjE=",
   774  					"avatar_url": "https://github.com/images/error/octocat_happy.gif",
   775  					"gravatar_id": "",
   776  					"url": "https://api.github.com/users/octocat",
   777  					"html_url": "https://github.com/octocat",
   778  					"followers_url": "https://api.github.com/users/octocat/followers",
   779  					"following_url": "https://api.github.com/users/octocat/following{/other_user}",
   780  					"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
   781  					"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
   782  					"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
   783  					"organizations_url": "https://api.github.com/users/octocat/orgs",
   784  					"repos_url": "https://api.github.com/users/octocat/repos",
   785  					"events_url": "https://api.github.com/users/octocat/events{/privacy}",
   786  					"received_events_url": "https://api.github.com/users/octocat/received_events",
   787  					"type": "User",
   788  					"site_admin": false
   789  				},
   790  				"assigning_team": {
   791  					"id": 1,
   792  					"node_id": "MDQ6VGVhbTE=",
   793  					"url": "https://api.github.com/teams/1",
   794  					"html_url": "https://github.com/orgs/github/teams/justice-league",
   795  					"name": "Justice League",
   796  					"slug": "justice-league",
   797  					"description": "A great team.",
   798  					"privacy": "closed",
   799  					"notification_setting": "notifications_enabled",
   800  					"permission": "admin",
   801  					"members_url": "https://api.github.com/teams/1/members{/member}",
   802  					"repositories_url": "https://api.github.com/teams/1/repos",
   803  					"parent": null
   804  				}
   805  			}`)
   806  	})
   807  
   808  	tmp, err := time.Parse(time.RFC3339, "2021-08-03T18:00:00-06:00")
   809  	if err != nil {
   810  		panic(err)
   811  	}
   812  	createdAt := Timestamp{tmp}
   813  
   814  	tmp, err = time.Parse(time.RFC3339, "2021-09-23T15:00:00-06:00")
   815  	if err != nil {
   816  		panic(err)
   817  	}
   818  	updatedAt := Timestamp{tmp}
   819  
   820  	tmp, err = time.Parse(time.RFC3339, "2021-10-14T00:53:32-06:00")
   821  	if err != nil {
   822  		panic(err)
   823  	}
   824  	lastActivityAt := Timestamp{tmp}
   825  
   826  	ctx := context.Background()
   827  	got, _, err := client.Copilot.GetSeatDetails(ctx, "o", "u")
   828  	if err != nil {
   829  		t.Errorf("Copilot.GetSeatDetails returned error: %v", err)
   830  	}
   831  
   832  	want := &CopilotSeatDetails{
   833  		Assignee: &User{
   834  			Login:             String("octocat"),
   835  			ID:                Int64(1),
   836  			NodeID:            String("MDQ6VXNlcjE="),
   837  			AvatarURL:         String("https://github.com/images/error/octocat_happy.gif"),
   838  			GravatarID:        String(""),
   839  			URL:               String("https://api.github.com/users/octocat"),
   840  			HTMLURL:           String("https://github.com/octocat"),
   841  			FollowersURL:      String("https://api.github.com/users/octocat/followers"),
   842  			FollowingURL:      String("https://api.github.com/users/octocat/following{/other_user}"),
   843  			GistsURL:          String("https://api.github.com/users/octocat/gists{/gist_id}"),
   844  			StarredURL:        String("https://api.github.com/users/octocat/starred{/owner}{/repo}"),
   845  			SubscriptionsURL:  String("https://api.github.com/users/octocat/subscriptions"),
   846  			OrganizationsURL:  String("https://api.github.com/users/octocat/orgs"),
   847  			ReposURL:          String("https://api.github.com/users/octocat/repos"),
   848  			EventsURL:         String("https://api.github.com/users/octocat/events{/privacy}"),
   849  			ReceivedEventsURL: String("https://api.github.com/users/octocat/received_events"),
   850  			Type:              String("User"),
   851  			SiteAdmin:         Bool(false),
   852  		},
   853  		AssigningTeam: &Team{
   854  			ID:              Int64(1),
   855  			NodeID:          String("MDQ6VGVhbTE="),
   856  			URL:             String("https://api.github.com/teams/1"),
   857  			HTMLURL:         String("https://github.com/orgs/github/teams/justice-league"),
   858  			Name:            String("Justice League"),
   859  			Slug:            String("justice-league"),
   860  			Description:     String("A great team."),
   861  			Privacy:         String("closed"),
   862  			Permission:      String("admin"),
   863  			MembersURL:      String("https://api.github.com/teams/1/members{/member}"),
   864  			RepositoriesURL: String("https://api.github.com/teams/1/repos"),
   865  			Parent:          nil,
   866  		},
   867  		CreatedAt:               &createdAt,
   868  		UpdatedAt:               &updatedAt,
   869  		PendingCancellationDate: nil,
   870  		LastActivityAt:          &lastActivityAt,
   871  		LastActivityEditor:      String("vscode/1.77.3/copilot/1.86.82"),
   872  	}
   873  
   874  	if !cmp.Equal(got, want) {
   875  		t.Errorf("Copilot.GetSeatDetails returned %+v, want %+v", got, want)
   876  	}
   877  
   878  	const methodName = "GetSeatDetails"
   879  
   880  	testBadOptions(t, methodName, func() (err error) {
   881  		_, _, err = client.Copilot.GetSeatDetails(ctx, "\n", "u")
   882  		return err
   883  	})
   884  
   885  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   886  		got, resp, err := client.Copilot.GetSeatDetails(ctx, "o", "u")
   887  		if got != nil {
   888  			t.Errorf("Copilot.GetSeatDetails returned %+v, want nil", got)
   889  		}
   890  		return resp, err
   891  	})
   892  }