github.com/google/go-github/v60@v60.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 fmt.Fprint(w, `{ 340 "total_seats": 4, 341 "seats": [ 342 { 343 "created_at": "2021-08-03T18:00:00-06:00", 344 "updated_at": "2021-09-23T15:00:00-06:00", 345 "pending_cancellation_date": null, 346 "last_activity_at": "2021-10-14T00:53:32-06:00", 347 "last_activity_editor": "vscode/1.77.3/copilot/1.86.82", 348 "assignee": { 349 "login": "octocat", 350 "id": 1, 351 "node_id": "MDQ6VXNlcjE=", 352 "avatar_url": "https://github.com/images/error/octocat_happy.gif", 353 "gravatar_id": "", 354 "url": "https://api.github.com/users/octocat", 355 "html_url": "https://github.com/octocat", 356 "followers_url": "https://api.github.com/users/octocat/followers", 357 "following_url": "https://api.github.com/users/octocat/following{/other_user}", 358 "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 359 "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 360 "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 361 "organizations_url": "https://api.github.com/users/octocat/orgs", 362 "repos_url": "https://api.github.com/users/octocat/repos", 363 "events_url": "https://api.github.com/users/octocat/events{/privacy}", 364 "received_events_url": "https://api.github.com/users/octocat/received_events", 365 "type": "User", 366 "site_admin": false 367 }, 368 "assigning_team": { 369 "id": 1, 370 "node_id": "MDQ6VGVhbTE=", 371 "url": "https://api.github.com/teams/1", 372 "html_url": "https://github.com/orgs/github/teams/justice-league", 373 "name": "Justice League", 374 "slug": "justice-league", 375 "description": "A great team.", 376 "privacy": "closed", 377 "notification_setting": "notifications_enabled", 378 "permission": "admin", 379 "members_url": "https://api.github.com/teams/1/members{/member}", 380 "repositories_url": "https://api.github.com/teams/1/repos", 381 "parent": null 382 } 383 }, 384 { 385 "created_at": "2021-09-23T18:00:00-06:00", 386 "updated_at": "2021-09-23T15:00:00-06:00", 387 "pending_cancellation_date": "2021-11-01", 388 "last_activity_at": "2021-10-13T00:53:32-06:00", 389 "last_activity_editor": "vscode/1.77.3/copilot/1.86.82", 390 "assignee": { 391 "login": "octokitten", 392 "id": 1, 393 "node_id": "MDQ76VNlcjE=", 394 "avatar_url": "https://github.com/images/error/octokitten_happy.gif", 395 "gravatar_id": "", 396 "url": "https://api.github.com/users/octokitten", 397 "html_url": "https://github.com/octokitten", 398 "followers_url": "https://api.github.com/users/octokitten/followers", 399 "following_url": "https://api.github.com/users/octokitten/following{/other_user}", 400 "gists_url": "https://api.github.com/users/octokitten/gists{/gist_id}", 401 "starred_url": "https://api.github.com/users/octokitten/starred{/owner}{/repo}", 402 "subscriptions_url": "https://api.github.com/users/octokitten/subscriptions", 403 "organizations_url": "https://api.github.com/users/octokitten/orgs", 404 "repos_url": "https://api.github.com/users/octokitten/repos", 405 "events_url": "https://api.github.com/users/octokitten/events{/privacy}", 406 "received_events_url": "https://api.github.com/users/octokitten/received_events", 407 "type": "User", 408 "site_admin": false 409 } 410 }, 411 { 412 "created_at": "2021-09-23T18:00:00-06:00", 413 "updated_at": "2021-09-23T15:00:00-06:00", 414 "pending_cancellation_date": "2021-11-01", 415 "last_activity_at": "2021-10-13T00:53:32-06:00", 416 "last_activity_editor": "vscode/1.77.3/copilot/1.86.82", 417 "assignee": { 418 "name": "octokittens", 419 "id": 1, 420 "type": "Team" 421 } 422 }, 423 { 424 "created_at": "2021-09-23T18:00:00-06:00", 425 "updated_at": "2021-09-23T15:00:00-06:00", 426 "pending_cancellation_date": "2021-11-01", 427 "last_activity_at": "2021-10-13T00:53:32-06:00", 428 "last_activity_editor": "vscode/1.77.3/copilot/1.86.82", 429 "assignee": { 430 "name": "octocats", 431 "id": 1, 432 "type": "Organization" 433 } 434 } 435 ] 436 }`) 437 }) 438 439 tmp, err := time.Parse(time.RFC3339, "2021-08-03T18:00:00-06:00") 440 if err != nil { 441 panic(err) 442 } 443 createdAt1 := Timestamp{tmp} 444 445 tmp, err = time.Parse(time.RFC3339, "2021-09-23T15:00:00-06:00") 446 if err != nil { 447 panic(err) 448 } 449 updatedAt1 := Timestamp{tmp} 450 451 tmp, err = time.Parse(time.RFC3339, "2021-10-14T00:53:32-06:00") 452 if err != nil { 453 panic(err) 454 } 455 lastActivityAt1 := Timestamp{tmp} 456 457 tmp, err = time.Parse(time.RFC3339, "2021-09-23T18:00:00-06:00") 458 if err != nil { 459 panic(err) 460 } 461 createdAt2 := Timestamp{tmp} 462 463 tmp, err = time.Parse(time.RFC3339, "2021-09-23T15:00:00-06:00") 464 if err != nil { 465 panic(err) 466 } 467 updatedAt2 := Timestamp{tmp} 468 469 tmp, err = time.Parse(time.RFC3339, "2021-10-13T00:53:32-06:00") 470 if err != nil { 471 panic(err) 472 } 473 lastActivityAt2 := Timestamp{tmp} 474 475 ctx := context.Background() 476 got, _, err := client.Copilot.ListCopilotSeats(ctx, "o", nil) 477 if err != nil { 478 t.Errorf("Copilot.ListCopilotSeats returned error: %v", err) 479 } 480 481 want := &ListCopilotSeatsResponse{ 482 TotalSeats: 4, 483 Seats: []*CopilotSeatDetails{ 484 { 485 Assignee: &User{ 486 Login: String("octocat"), 487 ID: Int64(1), 488 NodeID: String("MDQ6VXNlcjE="), 489 AvatarURL: String("https://github.com/images/error/octocat_happy.gif"), 490 GravatarID: String(""), 491 URL: String("https://api.github.com/users/octocat"), 492 HTMLURL: String("https://github.com/octocat"), 493 FollowersURL: String("https://api.github.com/users/octocat/followers"), 494 FollowingURL: String("https://api.github.com/users/octocat/following{/other_user}"), 495 GistsURL: String("https://api.github.com/users/octocat/gists{/gist_id}"), 496 StarredURL: String("https://api.github.com/users/octocat/starred{/owner}{/repo}"), 497 SubscriptionsURL: String("https://api.github.com/users/octocat/subscriptions"), 498 OrganizationsURL: String("https://api.github.com/users/octocat/orgs"), 499 ReposURL: String("https://api.github.com/users/octocat/repos"), 500 EventsURL: String("https://api.github.com/users/octocat/events{/privacy}"), 501 ReceivedEventsURL: String("https://api.github.com/users/octocat/received_events"), 502 Type: String("User"), 503 SiteAdmin: Bool(false), 504 }, 505 AssigningTeam: &Team{ 506 ID: Int64(1), 507 NodeID: String("MDQ6VGVhbTE="), 508 URL: String("https://api.github.com/teams/1"), 509 HTMLURL: String("https://github.com/orgs/github/teams/justice-league"), 510 Name: String("Justice League"), 511 Slug: String("justice-league"), 512 Description: String("A great team."), 513 Privacy: String("closed"), 514 Permission: String("admin"), 515 MembersURL: String("https://api.github.com/teams/1/members{/member}"), 516 RepositoriesURL: String("https://api.github.com/teams/1/repos"), 517 Parent: nil, 518 }, 519 CreatedAt: &createdAt1, 520 UpdatedAt: &updatedAt1, 521 PendingCancellationDate: nil, 522 LastActivityAt: &lastActivityAt1, 523 LastActivityEditor: String("vscode/1.77.3/copilot/1.86.82"), 524 }, 525 { 526 Assignee: &User{ 527 Login: String("octokitten"), 528 ID: Int64(1), 529 NodeID: String("MDQ76VNlcjE="), 530 AvatarURL: String("https://github.com/images/error/octokitten_happy.gif"), 531 GravatarID: String(""), 532 URL: String("https://api.github.com/users/octokitten"), 533 HTMLURL: String("https://github.com/octokitten"), 534 FollowersURL: String("https://api.github.com/users/octokitten/followers"), 535 FollowingURL: String("https://api.github.com/users/octokitten/following{/other_user}"), 536 GistsURL: String("https://api.github.com/users/octokitten/gists{/gist_id}"), 537 StarredURL: String("https://api.github.com/users/octokitten/starred{/owner}{/repo}"), 538 SubscriptionsURL: String("https://api.github.com/users/octokitten/subscriptions"), 539 OrganizationsURL: String("https://api.github.com/users/octokitten/orgs"), 540 ReposURL: String("https://api.github.com/users/octokitten/repos"), 541 EventsURL: String("https://api.github.com/users/octokitten/events{/privacy}"), 542 ReceivedEventsURL: String("https://api.github.com/users/octokitten/received_events"), 543 Type: String("User"), 544 SiteAdmin: Bool(false), 545 }, 546 AssigningTeam: nil, 547 CreatedAt: &createdAt2, 548 UpdatedAt: &updatedAt2, 549 PendingCancellationDate: String("2021-11-01"), 550 LastActivityAt: &lastActivityAt2, 551 LastActivityEditor: String("vscode/1.77.3/copilot/1.86.82"), 552 }, 553 { 554 Assignee: &Team{ 555 ID: Int64(1), 556 Name: String("octokittens"), 557 }, 558 AssigningTeam: nil, 559 CreatedAt: &createdAt2, 560 UpdatedAt: &updatedAt2, 561 PendingCancellationDate: String("2021-11-01"), 562 LastActivityAt: &lastActivityAt2, 563 LastActivityEditor: String("vscode/1.77.3/copilot/1.86.82"), 564 }, 565 { 566 Assignee: &Organization{ 567 ID: Int64(1), 568 Name: String("octocats"), 569 Type: String("Organization"), 570 }, 571 AssigningTeam: nil, 572 CreatedAt: &createdAt2, 573 UpdatedAt: &updatedAt2, 574 PendingCancellationDate: String("2021-11-01"), 575 LastActivityAt: &lastActivityAt2, 576 LastActivityEditor: String("vscode/1.77.3/copilot/1.86.82"), 577 }, 578 }, 579 } 580 581 if !cmp.Equal(got, want) { 582 t.Errorf("Copilot.ListCopilotSeats returned %+v, want %+v", got, want) 583 } 584 585 const methodName = "ListCopilotSeats" 586 587 testBadOptions(t, methodName, func() (err error) { 588 _, _, err = client.Copilot.ListCopilotSeats(ctx, "\n", nil) 589 return err 590 }) 591 592 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 593 got, resp, err := client.Copilot.ListCopilotSeats(ctx, "", nil) 594 if got != nil { 595 t.Errorf("Copilot.ListCopilotSeats returned %+v, want nil", got) 596 } 597 return resp, err 598 }) 599 } 600 601 func TestCopilotService_AddCopilotTeams(t *testing.T) { 602 client, mux, _, teardown := setup() 603 defer teardown() 604 605 mux.HandleFunc("/orgs/o/copilot/billing/selected_teams", func(w http.ResponseWriter, r *http.Request) { 606 testMethod(t, r, "POST") 607 testBody(t, r, `{"selected_teams":["team1","team2"]}`+"\n") 608 fmt.Fprint(w, `{"seats_created": 2}`) 609 }) 610 611 ctx := context.Background() 612 got, _, err := client.Copilot.AddCopilotTeams(ctx, "o", []string{"team1", "team2"}) 613 if err != nil { 614 t.Errorf("Copilot.AddCopilotTeams returned error: %v", err) 615 } 616 617 want := &SeatAssignments{SeatsCreated: 2} 618 619 if !cmp.Equal(got, want) { 620 t.Errorf("Copilot.AddCopilotTeams returned %+v, want %+v", got, want) 621 } 622 623 const methodName = "AddCopilotTeams" 624 625 testBadOptions(t, methodName, func() (err error) { 626 _, _, err = client.Copilot.AddCopilotTeams(ctx, "\n", []string{"team1", "team2"}) 627 return err 628 }) 629 630 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 631 got, resp, err := client.Copilot.AddCopilotTeams(ctx, "o", []string{"team1", "team2"}) 632 if got != nil { 633 t.Errorf("Copilot.AddCopilotTeams returned %+v, want nil", got) 634 } 635 return resp, err 636 }) 637 } 638 639 func TestCopilotService_RemoveCopilotTeams(t *testing.T) { 640 client, mux, _, teardown := setup() 641 defer teardown() 642 643 mux.HandleFunc("/orgs/o/copilot/billing/selected_teams", func(w http.ResponseWriter, r *http.Request) { 644 testMethod(t, r, "DELETE") 645 testBody(t, r, `{"selected_teams":["team1","team2"]}`+"\n") 646 fmt.Fprint(w, `{"seats_cancelled": 2}`) 647 }) 648 649 ctx := context.Background() 650 got, _, err := client.Copilot.RemoveCopilotTeams(ctx, "o", []string{"team1", "team2"}) 651 if err != nil { 652 t.Errorf("Copilot.RemoveCopilotTeams returned error: %v", err) 653 } 654 655 want := &SeatCancellations{SeatsCancelled: 2} 656 657 if !cmp.Equal(got, want) { 658 t.Errorf("Copilot.RemoveCopilotTeams returned %+v, want %+v", got, want) 659 } 660 661 const methodName = "RemoveCopilotTeams" 662 663 testBadOptions(t, methodName, func() (err error) { 664 _, _, err = client.Copilot.RemoveCopilotTeams(ctx, "\n", []string{"team1", "team2"}) 665 return err 666 }) 667 668 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 669 got, resp, err := client.Copilot.RemoveCopilotTeams(ctx, "o", []string{"team1", "team2"}) 670 if got != nil { 671 t.Errorf("Copilot.RemoveCopilotTeams returned %+v, want nil", got) 672 } 673 return resp, err 674 }) 675 } 676 677 func TestCopilotService_AddCopilotUsers(t *testing.T) { 678 client, mux, _, teardown := setup() 679 defer teardown() 680 681 mux.HandleFunc("/orgs/o/copilot/billing/selected_users", func(w http.ResponseWriter, r *http.Request) { 682 testMethod(t, r, "POST") 683 testBody(t, r, `{"selected_usernames":["user1","user2"]}`+"\n") 684 fmt.Fprint(w, `{"seats_created": 2}`) 685 }) 686 687 ctx := context.Background() 688 got, _, err := client.Copilot.AddCopilotUsers(ctx, "o", []string{"user1", "user2"}) 689 if err != nil { 690 t.Errorf("Copilot.AddCopilotUsers returned error: %v", err) 691 } 692 693 want := &SeatAssignments{SeatsCreated: 2} 694 695 if !cmp.Equal(got, want) { 696 t.Errorf("Copilot.AddCopilotUsers returned %+v, want %+v", got, want) 697 } 698 699 const methodName = "AddCopilotUsers" 700 701 testBadOptions(t, methodName, func() (err error) { 702 _, _, err = client.Copilot.AddCopilotUsers(ctx, "\n", []string{"user1", "user2"}) 703 return err 704 }) 705 706 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 707 got, resp, err := client.Copilot.AddCopilotUsers(ctx, "o", []string{"user1", "user2"}) 708 if got != nil { 709 t.Errorf("Copilot.AddCopilotUsers returned %+v, want nil", got) 710 } 711 return resp, err 712 }) 713 } 714 715 func TestCopilotService_RemoveCopilotUsers(t *testing.T) { 716 client, mux, _, teardown := setup() 717 defer teardown() 718 719 mux.HandleFunc("/orgs/o/copilot/billing/selected_users", func(w http.ResponseWriter, r *http.Request) { 720 testMethod(t, r, "DELETE") 721 testBody(t, r, `{"selected_usernames":["user1","user2"]}`+"\n") 722 fmt.Fprint(w, `{"seats_cancelled": 2}`) 723 }) 724 725 ctx := context.Background() 726 got, _, err := client.Copilot.RemoveCopilotUsers(ctx, "o", []string{"user1", "user2"}) 727 if err != nil { 728 t.Errorf("Copilot.RemoveCopilotUsers returned error: %v", err) 729 } 730 731 want := &SeatCancellations{SeatsCancelled: 2} 732 733 if !cmp.Equal(got, want) { 734 t.Errorf("Copilot.RemoveCopilotUsers returned %+v, want %+v", got, want) 735 } 736 737 const methodName = "RemoveCopilotUsers" 738 739 testBadOptions(t, methodName, func() (err error) { 740 _, _, err = client.Copilot.RemoveCopilotUsers(ctx, "\n", []string{"user1", "user2"}) 741 return err 742 }) 743 744 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 745 got, resp, err := client.Copilot.RemoveCopilotUsers(ctx, "o", []string{"user1", "user2"}) 746 if got != nil { 747 t.Errorf("Copilot.RemoveCopilotUsers returned %+v, want nil", got) 748 } 749 return resp, err 750 }) 751 } 752 753 func TestCopilotService_GetSeatDetails(t *testing.T) { 754 client, mux, _, teardown := setup() 755 defer teardown() 756 757 mux.HandleFunc("/orgs/o/members/u/copilot", func(w http.ResponseWriter, r *http.Request) { 758 testMethod(t, r, "GET") 759 fmt.Fprint(w, `{ 760 "created_at": "2021-08-03T18:00:00-06:00", 761 "updated_at": "2021-09-23T15:00:00-06:00", 762 "pending_cancellation_date": null, 763 "last_activity_at": "2021-10-14T00:53:32-06:00", 764 "last_activity_editor": "vscode/1.77.3/copilot/1.86.82", 765 "assignee": { 766 "login": "octocat", 767 "id": 1, 768 "node_id": "MDQ6VXNlcjE=", 769 "avatar_url": "https://github.com/images/error/octocat_happy.gif", 770 "gravatar_id": "", 771 "url": "https://api.github.com/users/octocat", 772 "html_url": "https://github.com/octocat", 773 "followers_url": "https://api.github.com/users/octocat/followers", 774 "following_url": "https://api.github.com/users/octocat/following{/other_user}", 775 "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 776 "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 777 "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 778 "organizations_url": "https://api.github.com/users/octocat/orgs", 779 "repos_url": "https://api.github.com/users/octocat/repos", 780 "events_url": "https://api.github.com/users/octocat/events{/privacy}", 781 "received_events_url": "https://api.github.com/users/octocat/received_events", 782 "type": "User", 783 "site_admin": false 784 }, 785 "assigning_team": { 786 "id": 1, 787 "node_id": "MDQ6VGVhbTE=", 788 "url": "https://api.github.com/teams/1", 789 "html_url": "https://github.com/orgs/github/teams/justice-league", 790 "name": "Justice League", 791 "slug": "justice-league", 792 "description": "A great team.", 793 "privacy": "closed", 794 "notification_setting": "notifications_enabled", 795 "permission": "admin", 796 "members_url": "https://api.github.com/teams/1/members{/member}", 797 "repositories_url": "https://api.github.com/teams/1/repos", 798 "parent": null 799 } 800 }`) 801 }) 802 803 tmp, err := time.Parse(time.RFC3339, "2021-08-03T18:00:00-06:00") 804 if err != nil { 805 panic(err) 806 } 807 createdAt := Timestamp{tmp} 808 809 tmp, err = time.Parse(time.RFC3339, "2021-09-23T15:00:00-06:00") 810 if err != nil { 811 panic(err) 812 } 813 updatedAt := Timestamp{tmp} 814 815 tmp, err = time.Parse(time.RFC3339, "2021-10-14T00:53:32-06:00") 816 if err != nil { 817 panic(err) 818 } 819 lastActivityAt := Timestamp{tmp} 820 821 ctx := context.Background() 822 got, _, err := client.Copilot.GetSeatDetails(ctx, "o", "u") 823 if err != nil { 824 t.Errorf("Copilot.GetSeatDetails returned error: %v", err) 825 } 826 827 want := &CopilotSeatDetails{ 828 Assignee: &User{ 829 Login: String("octocat"), 830 ID: Int64(1), 831 NodeID: String("MDQ6VXNlcjE="), 832 AvatarURL: String("https://github.com/images/error/octocat_happy.gif"), 833 GravatarID: String(""), 834 URL: String("https://api.github.com/users/octocat"), 835 HTMLURL: String("https://github.com/octocat"), 836 FollowersURL: String("https://api.github.com/users/octocat/followers"), 837 FollowingURL: String("https://api.github.com/users/octocat/following{/other_user}"), 838 GistsURL: String("https://api.github.com/users/octocat/gists{/gist_id}"), 839 StarredURL: String("https://api.github.com/users/octocat/starred{/owner}{/repo}"), 840 SubscriptionsURL: String("https://api.github.com/users/octocat/subscriptions"), 841 OrganizationsURL: String("https://api.github.com/users/octocat/orgs"), 842 ReposURL: String("https://api.github.com/users/octocat/repos"), 843 EventsURL: String("https://api.github.com/users/octocat/events{/privacy}"), 844 ReceivedEventsURL: String("https://api.github.com/users/octocat/received_events"), 845 Type: String("User"), 846 SiteAdmin: Bool(false), 847 }, 848 AssigningTeam: &Team{ 849 ID: Int64(1), 850 NodeID: String("MDQ6VGVhbTE="), 851 URL: String("https://api.github.com/teams/1"), 852 HTMLURL: String("https://github.com/orgs/github/teams/justice-league"), 853 Name: String("Justice League"), 854 Slug: String("justice-league"), 855 Description: String("A great team."), 856 Privacy: String("closed"), 857 Permission: String("admin"), 858 MembersURL: String("https://api.github.com/teams/1/members{/member}"), 859 RepositoriesURL: String("https://api.github.com/teams/1/repos"), 860 Parent: nil, 861 }, 862 CreatedAt: &createdAt, 863 UpdatedAt: &updatedAt, 864 PendingCancellationDate: nil, 865 LastActivityAt: &lastActivityAt, 866 LastActivityEditor: String("vscode/1.77.3/copilot/1.86.82"), 867 } 868 869 if !cmp.Equal(got, want) { 870 t.Errorf("Copilot.GetSeatDetails returned %+v, want %+v", got, want) 871 } 872 873 const methodName = "GetSeatDetails" 874 875 testBadOptions(t, methodName, func() (err error) { 876 _, _, err = client.Copilot.GetSeatDetails(ctx, "\n", "u") 877 return err 878 }) 879 880 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 881 got, resp, err := client.Copilot.GetSeatDetails(ctx, "o", "u") 882 if got != nil { 883 t.Errorf("Copilot.GetSeatDetails returned %+v, want nil", got) 884 } 885 return resp, err 886 }) 887 }