github.com/google/go-github/v65@v65.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 }