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