github.com/google/go-github/v74@v74.0.0/github/repos_contents_test.go (about) 1 // Copyright 2014 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 "errors" 11 "fmt" 12 "io" 13 "net/http" 14 "net/url" 15 "testing" 16 17 "github.com/google/go-cmp/cmp" 18 ) 19 20 func TestRepositoryContent_GetContent(t *testing.T) { 21 t.Parallel() 22 tests := []struct { 23 encoding, content *string // input encoding and content 24 want string // desired output 25 wantErr bool // whether an error is expected 26 }{ 27 { 28 encoding: Ptr(""), 29 content: Ptr("hello"), 30 want: "hello", 31 wantErr: false, 32 }, 33 { 34 encoding: nil, 35 content: Ptr("hello"), 36 want: "hello", 37 wantErr: false, 38 }, 39 { 40 encoding: nil, 41 content: nil, 42 want: "", 43 wantErr: false, 44 }, 45 { 46 encoding: Ptr("base64"), 47 content: Ptr("aGVsbG8="), 48 want: "hello", 49 wantErr: false, 50 }, 51 { 52 encoding: Ptr("bad"), 53 content: Ptr("aGVsbG8="), 54 want: "", 55 wantErr: true, 56 }, 57 { 58 encoding: Ptr("none"), 59 content: nil, 60 want: "", 61 wantErr: true, 62 }, 63 } 64 65 for _, tt := range tests { 66 r := RepositoryContent{Encoding: tt.encoding, Content: tt.content} 67 got, err := r.GetContent() 68 if err != nil && !tt.wantErr { 69 t.Errorf("RepositoryContent(%s, %s) returned unexpected error: %v", 70 stringOrNil(tt.encoding), stringOrNil(tt.content), err) 71 } 72 if err == nil && tt.wantErr { 73 t.Errorf("RepositoryContent(%s, %s) did not return unexpected error", 74 stringOrNil(tt.encoding), stringOrNil(tt.content)) 75 } 76 if want := tt.want; got != want { 77 t.Errorf("RepositoryContent.GetContent returned %+v, want %+v", got, want) 78 } 79 } 80 } 81 82 // stringOrNil converts a potentially null string pointer to string. 83 // For non-nil input pointer, the returned string is enclosed in double-quotes. 84 func stringOrNil(s *string) string { 85 if s == nil { 86 return "<nil>" 87 } 88 return fmt.Sprintf("%q", *s) 89 } 90 91 func TestRepositoriesService_GetReadme(t *testing.T) { 92 t.Parallel() 93 client, mux, _ := setup(t) 94 95 mux.HandleFunc("/repos/o/r/readme", func(w http.ResponseWriter, r *http.Request) { 96 testMethod(t, r, "GET") 97 fmt.Fprint(w, `{ 98 "type": "file", 99 "encoding": "base64", 100 "size": 5362, 101 "name": "README.md", 102 "path": "README.md" 103 }`) 104 }) 105 ctx := context.Background() 106 readme, _, err := client.Repositories.GetReadme(ctx, "o", "r", &RepositoryContentGetOptions{}) 107 if err != nil { 108 t.Errorf("Repositories.GetReadme returned error: %v", err) 109 } 110 want := &RepositoryContent{Type: Ptr("file"), Name: Ptr("README.md"), Size: Ptr(5362), Encoding: Ptr("base64"), Path: Ptr("README.md")} 111 if !cmp.Equal(readme, want) { 112 t.Errorf("Repositories.GetReadme returned %+v, want %+v", readme, want) 113 } 114 115 const methodName = "GetReadme" 116 testBadOptions(t, methodName, func() (err error) { 117 _, _, err = client.Repositories.GetReadme(ctx, "\n", "\n", &RepositoryContentGetOptions{}) 118 return err 119 }) 120 121 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 122 got, resp, err := client.Repositories.GetReadme(ctx, "o", "r", &RepositoryContentGetOptions{}) 123 if got != nil { 124 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 125 } 126 return resp, err 127 }) 128 } 129 130 func TestRepositoriesService_DownloadContents_SuccessForFile(t *testing.T) { 131 t.Parallel() 132 client, mux, serverURL := setup(t) 133 134 mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { 135 testMethod(t, r, "GET") 136 fmt.Fprint(w, `{ 137 "type": "file", 138 "name": "f", 139 "content": "foo", 140 "download_url": "`+serverURL+baseURLPath+`/download/f" 141 }`) 142 }) 143 144 ctx := context.Background() 145 r, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil) 146 if err != nil { 147 t.Errorf("Repositories.DownloadContents returned error: %v", err) 148 } 149 150 if got, want := resp.Response.StatusCode, http.StatusOK; got != want { 151 t.Errorf("Repositories.DownloadContents returned status code %v, want %v", got, want) 152 } 153 154 bytes, err := io.ReadAll(r) 155 if err != nil { 156 t.Errorf("Error reading response body: %v", err) 157 } 158 r.Close() 159 160 if got, want := string(bytes), "foo"; got != want { 161 t.Errorf("Repositories.DownloadContents returned %v, want %v", got, want) 162 } 163 164 const methodName = "DownloadContents" 165 testBadOptions(t, methodName, func() (err error) { 166 _, _, err = client.Repositories.DownloadContents(ctx, "\n", "\n", "\n", nil) 167 return err 168 }) 169 170 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 171 got, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil) 172 if got != nil { 173 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 174 } 175 return resp, err 176 }) 177 } 178 179 func TestRepositoriesService_DownloadContents_SuccessForDirectory(t *testing.T) { 180 t.Parallel() 181 client, mux, serverURL := setup(t) 182 183 mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { 184 testMethod(t, r, "GET") 185 fmt.Fprint(w, `{ 186 "type": "file", 187 "name": "f" 188 }`) 189 }) 190 mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { 191 testMethod(t, r, "GET") 192 fmt.Fprint(w, `[{ 193 "type": "file", 194 "name": "f", 195 "download_url": "`+serverURL+baseURLPath+`/download/f" 196 }]`) 197 }) 198 mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) { 199 testMethod(t, r, "GET") 200 fmt.Fprint(w, "foo") 201 }) 202 203 ctx := context.Background() 204 r, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil) 205 if err != nil { 206 t.Errorf("Repositories.DownloadContents returned error: %v", err) 207 } 208 209 if got, want := resp.Response.StatusCode, http.StatusOK; got != want { 210 t.Errorf("Repositories.DownloadContents returned status code %v, want %v", got, want) 211 } 212 213 bytes, err := io.ReadAll(r) 214 if err != nil { 215 t.Errorf("Error reading response body: %v", err) 216 } 217 r.Close() 218 219 if got, want := string(bytes), "foo"; got != want { 220 t.Errorf("Repositories.DownloadContents returned %v, want %v", got, want) 221 } 222 223 const methodName = "DownloadContents" 224 testBadOptions(t, methodName, func() (err error) { 225 _, _, err = client.Repositories.DownloadContents(ctx, "\n", "\n", "\n", nil) 226 return err 227 }) 228 229 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 230 got, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil) 231 if got != nil { 232 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 233 } 234 return resp, err 235 }) 236 } 237 238 func TestRepositoriesService_DownloadContents_FailedResponse(t *testing.T) { 239 t.Parallel() 240 client, mux, serverURL := setup(t) 241 242 mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { 243 testMethod(t, r, "GET") 244 fmt.Fprint(w, `{ 245 "type": "file", 246 "name": "f" 247 }`) 248 }) 249 mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { 250 testMethod(t, r, "GET") 251 fmt.Fprint(w, `[{ 252 "type": "file", 253 "name": "f", 254 "download_url": "`+serverURL+baseURLPath+`/download/f" 255 }]`) 256 }) 257 mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) { 258 testMethod(t, r, "GET") 259 w.WriteHeader(http.StatusInternalServerError) 260 fmt.Fprint(w, "foo error") 261 }) 262 263 ctx := context.Background() 264 r, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil) 265 if err != nil { 266 t.Errorf("Repositories.DownloadContents returned error: %v", err) 267 } 268 269 if got, want := resp.Response.StatusCode, http.StatusInternalServerError; got != want { 270 t.Errorf("Repositories.DownloadContents returned status code %v, want %v", got, want) 271 } 272 273 bytes, err := io.ReadAll(r) 274 if err != nil { 275 t.Errorf("Error reading response body: %v", err) 276 } 277 r.Close() 278 279 if got, want := string(bytes), "foo error"; got != want { 280 t.Errorf("Repositories.DownloadContents returned %v, want %v", got, want) 281 } 282 } 283 284 func TestRepositoriesService_DownloadContents_NoDownloadURL(t *testing.T) { 285 t.Parallel() 286 client, mux, _ := setup(t) 287 288 mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { 289 testMethod(t, r, "GET") 290 fmt.Fprint(w, `{ 291 "type": "file", 292 "name": "f", 293 "content": "" 294 }`) 295 }) 296 mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { 297 testMethod(t, r, "GET") 298 fmt.Fprint(w, `[{ 299 "type": "file", 300 "name": "f", 301 "content": "" 302 }]`) 303 }) 304 305 ctx := context.Background() 306 reader, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil) 307 if err == nil { 308 t.Error("Repositories.DownloadContents did not return expected error") 309 } 310 311 if resp == nil { 312 t.Error("Repositories.DownloadContents did not return expected response") 313 } 314 315 if reader != nil { 316 t.Error("Repositories.DownloadContents did not return expected reader") 317 } 318 } 319 320 func TestRepositoriesService_DownloadContents_NoFile(t *testing.T) { 321 t.Parallel() 322 client, mux, _ := setup(t) 323 324 mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { 325 testMethod(t, r, "GET") 326 fmt.Fprint(w, `{ 327 "type": "file", 328 "name": "f", 329 "content": "" 330 }`) 331 }) 332 333 mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { 334 testMethod(t, r, "GET") 335 fmt.Fprint(w, `[]`) 336 }) 337 338 ctx := context.Background() 339 reader, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil) 340 if err == nil { 341 t.Error("Repositories.DownloadContents did not return expected error") 342 } 343 344 if resp == nil { 345 t.Error("Repositories.DownloadContents did not return expected response") 346 } 347 348 if reader != nil { 349 t.Error("Repositories.DownloadContents did not return expected reader") 350 } 351 } 352 353 func TestRepositoriesService_DownloadContentsWithMeta_SuccessForFile(t *testing.T) { 354 t.Parallel() 355 client, mux, serverURL := setup(t) 356 357 mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { 358 testMethod(t, r, "GET") 359 fmt.Fprint(w, `{ 360 "type": "file", 361 "name": "f", 362 "download_url": "`+serverURL+baseURLPath+`/download/f", 363 "content": "foo" 364 }`) 365 }) 366 367 ctx := context.Background() 368 r, c, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil) 369 if err != nil { 370 t.Errorf("Repositories.DownloadContentsWithMeta returned error: %v", err) 371 } 372 373 if got, want := resp.Response.StatusCode, http.StatusOK; got != want { 374 t.Errorf("Repositories.DownloadContentsWithMeta returned status code %v, want %v", got, want) 375 } 376 377 bytes, err := io.ReadAll(r) 378 if err != nil { 379 t.Errorf("Error reading response body: %v", err) 380 } 381 r.Close() 382 383 if got, want := string(bytes), "foo"; got != want { 384 t.Errorf("Repositories.DownloadContentsWithMeta returned %v, want %v", got, want) 385 } 386 387 if c != nil && c.Name != nil { 388 if got, want := *c.Name, "f"; got != want { 389 t.Errorf("Repositories.DownloadContentsWithMeta returned content name %v, want %v", got, want) 390 } 391 } else { 392 t.Error("Returned RepositoryContent is null") 393 } 394 395 const methodName = "DownloadContentsWithMeta" 396 testBadOptions(t, methodName, func() (err error) { 397 _, _, _, err = client.Repositories.DownloadContentsWithMeta(ctx, "\n", "\n", "\n", nil) 398 return err 399 }) 400 401 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 402 got, cot, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil) 403 if got != nil { 404 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 405 } 406 if cot != nil { 407 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, cot) 408 } 409 return resp, err 410 }) 411 } 412 413 func TestRepositoriesService_DownloadContentsWithMeta_SuccessForDirectory(t *testing.T) { 414 t.Parallel() 415 client, mux, serverURL := setup(t) 416 417 mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { 418 testMethod(t, r, "GET") 419 fmt.Fprint(w, `[{ 420 "type": "file", 421 "name": "f", 422 "download_url": "`+serverURL+baseURLPath+`/download/f" 423 }]`) 424 }) 425 mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) { 426 testMethod(t, r, "GET") 427 fmt.Fprint(w, "foo") 428 }) 429 430 ctx := context.Background() 431 r, c, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil) 432 if err != nil { 433 t.Errorf("Repositories.DownloadContentsWithMeta returned error: %v", err) 434 } 435 436 if got, want := resp.Response.StatusCode, http.StatusOK; got != want { 437 t.Errorf("Repositories.DownloadContentsWithMeta returned status code %v, want %v", got, want) 438 } 439 440 bytes, err := io.ReadAll(r) 441 if err != nil { 442 t.Errorf("Error reading response body: %v", err) 443 } 444 r.Close() 445 446 if got, want := string(bytes), "foo"; got != want { 447 t.Errorf("Repositories.DownloadContentsWithMeta returned %v, want %v", got, want) 448 } 449 450 if c != nil && c.Name != nil { 451 if got, want := *c.Name, "f"; got != want { 452 t.Errorf("Repositories.DownloadContentsWithMeta returned content name %v, want %v", got, want) 453 } 454 } else { 455 t.Error("Returned RepositoryContent is null") 456 } 457 } 458 459 func TestRepositoriesService_DownloadContentsWithMeta_FailedResponse(t *testing.T) { 460 t.Parallel() 461 client, mux, serverURL := setup(t) 462 463 downloadURL := fmt.Sprintf("%s%s/download/f", serverURL, baseURLPath) 464 465 mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { 466 testMethod(t, r, "GET") 467 fmt.Fprint(w, `{ 468 "type": "file", 469 "name": "f", 470 "download_url": "`+downloadURL+`" 471 }`) 472 }) 473 mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) { 474 testMethod(t, r, "GET") 475 w.WriteHeader(http.StatusInternalServerError) 476 fmt.Fprint(w, "foo error") 477 }) 478 mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { 479 testMethod(t, r, "GET") 480 fmt.Fprint(w, `[{ 481 "type": "file", 482 "name": "f", 483 "download_url": "`+downloadURL+`" 484 }]`) 485 }) 486 487 ctx := context.Background() 488 r, c, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil) 489 if err != nil { 490 t.Errorf("Repositories.DownloadContentsWithMeta returned error: %v", err) 491 } 492 493 if got, want := resp.Response.StatusCode, http.StatusInternalServerError; got != want { 494 t.Errorf("Repositories.DownloadContentsWithMeta returned status code %v, want %v", got, want) 495 } 496 497 bytes, err := io.ReadAll(r) 498 if err != nil { 499 t.Errorf("Error reading response body: %v", err) 500 } 501 r.Close() 502 503 if got, want := string(bytes), "foo error"; got != want { 504 t.Errorf("Repositories.DownloadContentsWithMeta returned %v, want %v", got, want) 505 } 506 507 if c != nil && c.Name != nil { 508 if got, want := *c.Name, "f"; got != want { 509 t.Errorf("Repositories.DownloadContentsWithMeta returned content name %v, want %v", got, want) 510 } 511 } else { 512 t.Error("Returned RepositoryContent is null") 513 } 514 } 515 516 func TestRepositoriesService_DownloadContentsWithMeta_NoDownloadURL(t *testing.T) { 517 t.Parallel() 518 client, mux, _ := setup(t) 519 520 mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { 521 testMethod(t, r, "GET") 522 fmt.Fprint(w, `{ 523 "type": "file", 524 "name": "f", 525 }`) 526 }) 527 mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { 528 testMethod(t, r, "GET") 529 fmt.Fprint(w, `[{ 530 "type": "file", 531 "name": "f", 532 "content": "" 533 }]`) 534 }) 535 536 ctx := context.Background() 537 reader, contents, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil) 538 if err == nil { 539 t.Error("Repositories.DownloadContentsWithMeta did not return expected error") 540 } 541 542 if reader != nil { 543 t.Error("Repositories.DownloadContentsWithMeta did not return expected reader") 544 } 545 546 if resp == nil { 547 t.Error("Repositories.DownloadContentsWithMeta did not return expected response") 548 } 549 550 if contents == nil { 551 t.Error("Repositories.DownloadContentsWithMeta did not return expected content") 552 } 553 } 554 555 func TestRepositoriesService_DownloadContentsWithMeta_NoFile(t *testing.T) { 556 t.Parallel() 557 client, mux, _ := setup(t) 558 559 mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { 560 testMethod(t, r, "GET") 561 fmt.Fprint(w, `[]`) 562 }) 563 564 ctx := context.Background() 565 _, _, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil) 566 if err == nil { 567 t.Error("Repositories.DownloadContentsWithMeta did not return expected error") 568 } 569 570 if resp == nil { 571 t.Error("Repositories.DownloadContentsWithMeta did not return expected response") 572 } 573 } 574 575 func TestRepositoriesService_GetContents_File(t *testing.T) { 576 t.Parallel() 577 client, mux, _ := setup(t) 578 579 mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { 580 testMethod(t, r, "GET") 581 fmt.Fprint(w, `{ 582 "type": "file", 583 "encoding": "base64", 584 "size": 20678, 585 "name": "LICENSE", 586 "path": "LICENSE" 587 }`) 588 }) 589 ctx := context.Background() 590 fileContents, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "p", &RepositoryContentGetOptions{}) 591 if err != nil { 592 t.Errorf("Repositories.GetContents returned error: %v", err) 593 } 594 want := &RepositoryContent{Type: Ptr("file"), Name: Ptr("LICENSE"), Size: Ptr(20678), Encoding: Ptr("base64"), Path: Ptr("LICENSE")} 595 if !cmp.Equal(fileContents, want) { 596 t.Errorf("Repositories.GetContents returned %+v, want %+v", fileContents, want) 597 } 598 599 const methodName = "GetContents" 600 testBadOptions(t, methodName, func() (err error) { 601 _, _, _, err = client.Repositories.GetContents(ctx, "\n", "\n", "\n", &RepositoryContentGetOptions{}) 602 return err 603 }) 604 605 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 606 got, _, resp, err := client.Repositories.GetContents(ctx, "o", "r", "p", &RepositoryContentGetOptions{}) 607 if got != nil { 608 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 609 } 610 return resp, err 611 }) 612 } 613 614 func TestRepositoriesService_GetContents_FilenameNeedsEscape(t *testing.T) { 615 t.Parallel() 616 client, mux, _ := setup(t) 617 618 mux.HandleFunc("/repos/o/r/contents/p#?%/中.go", func(w http.ResponseWriter, r *http.Request) { 619 testMethod(t, r, "GET") 620 fmt.Fprint(w, `{}`) 621 }) 622 ctx := context.Background() 623 _, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "p#?%/中.go", &RepositoryContentGetOptions{}) 624 if err != nil { 625 t.Fatalf("Repositories.GetContents returned error: %v", err) 626 } 627 } 628 629 func TestRepositoriesService_GetContents_DirectoryWithSpaces(t *testing.T) { 630 t.Parallel() 631 client, mux, _ := setup(t) 632 633 mux.HandleFunc("/repos/o/r/contents/some%20directory/file.go", func(w http.ResponseWriter, r *http.Request) { 634 testMethod(t, r, "GET") 635 fmt.Fprint(w, `{}`) 636 }) 637 ctx := context.Background() 638 _, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "some directory/file.go", &RepositoryContentGetOptions{}) 639 if err != nil { 640 t.Fatalf("Repositories.GetContents returned error: %v", err) 641 } 642 } 643 644 func TestRepositoriesService_GetContents_PathWithParent(t *testing.T) { 645 t.Parallel() 646 client, mux, _ := setup(t) 647 648 mux.HandleFunc("/repos/o/r/contents/some/../directory/file.go", func(w http.ResponseWriter, r *http.Request) { 649 testMethod(t, r, "GET") 650 fmt.Fprint(w, `{}`) 651 }) 652 ctx := context.Background() 653 _, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "some/../directory/file.go", &RepositoryContentGetOptions{}) 654 if err == nil { 655 t.Fatal("Repositories.GetContents expected error but got none") 656 } 657 } 658 659 func TestRepositoriesService_GetContents_DirectoryWithPlusChars(t *testing.T) { 660 t.Parallel() 661 client, mux, _ := setup(t) 662 663 mux.HandleFunc("/repos/o/r/contents/some%20directory%2Bname/file.go", func(w http.ResponseWriter, r *http.Request) { 664 testMethod(t, r, "GET") 665 fmt.Fprint(w, `{}`) 666 }) 667 ctx := context.Background() 668 _, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "some directory+name/file.go", &RepositoryContentGetOptions{}) 669 if err != nil { 670 t.Fatalf("Repositories.GetContents returned error: %v", err) 671 } 672 } 673 674 func TestRepositoriesService_GetContents_Directory(t *testing.T) { 675 t.Parallel() 676 client, mux, _ := setup(t) 677 678 mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { 679 testMethod(t, r, "GET") 680 fmt.Fprint(w, `[{ 681 "type": "dir", 682 "name": "lib", 683 "path": "lib" 684 }, 685 { 686 "type": "file", 687 "size": 20678, 688 "name": "LICENSE", 689 "path": "LICENSE" 690 }]`) 691 }) 692 ctx := context.Background() 693 _, directoryContents, _, err := client.Repositories.GetContents(ctx, "o", "r", "p", &RepositoryContentGetOptions{}) 694 if err != nil { 695 t.Errorf("Repositories.GetContents returned error: %v", err) 696 } 697 want := []*RepositoryContent{{Type: Ptr("dir"), Name: Ptr("lib"), Path: Ptr("lib")}, 698 {Type: Ptr("file"), Name: Ptr("LICENSE"), Size: Ptr(20678), Path: Ptr("LICENSE")}} 699 if !cmp.Equal(directoryContents, want) { 700 t.Errorf("Repositories.GetContents_Directory returned %+v, want %+v", directoryContents, want) 701 } 702 } 703 704 func TestRepositoriesService_CreateFile(t *testing.T) { 705 t.Parallel() 706 client, mux, _ := setup(t) 707 708 mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { 709 testMethod(t, r, "PUT") 710 fmt.Fprint(w, `{ 711 "content":{ 712 "name":"p" 713 }, 714 "commit":{ 715 "message":"m", 716 "sha":"f5f369044773ff9c6383c087466d12adb6fa0828" 717 } 718 }`) 719 }) 720 message := "m" 721 content := []byte("c") 722 repositoryContentsOptions := &RepositoryContentFileOptions{ 723 Message: &message, 724 Content: content, 725 Committer: &CommitAuthor{Name: Ptr("n"), Email: Ptr("e")}, 726 } 727 ctx := context.Background() 728 createResponse, _, err := client.Repositories.CreateFile(ctx, "o", "r", "p", repositoryContentsOptions) 729 if err != nil { 730 t.Errorf("Repositories.CreateFile returned error: %v", err) 731 } 732 want := &RepositoryContentResponse{ 733 Content: &RepositoryContent{Name: Ptr("p")}, 734 Commit: Commit{ 735 Message: Ptr("m"), 736 SHA: Ptr("f5f369044773ff9c6383c087466d12adb6fa0828"), 737 }, 738 } 739 if !cmp.Equal(createResponse, want) { 740 t.Errorf("Repositories.CreateFile returned %+v, want %+v", createResponse, want) 741 } 742 743 const methodName = "CreateFile" 744 testBadOptions(t, methodName, func() (err error) { 745 _, _, err = client.Repositories.CreateFile(ctx, "\n", "\n", "\n", repositoryContentsOptions) 746 return err 747 }) 748 749 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 750 got, resp, err := client.Repositories.CreateFile(ctx, "o", "r", "p", repositoryContentsOptions) 751 if got != nil { 752 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 753 } 754 return resp, err 755 }) 756 } 757 758 func TestRepositoriesService_UpdateFile(t *testing.T) { 759 t.Parallel() 760 client, mux, _ := setup(t) 761 762 mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { 763 testMethod(t, r, "PUT") 764 fmt.Fprint(w, `{ 765 "content":{ 766 "name":"p" 767 }, 768 "commit":{ 769 "message":"m", 770 "sha":"f5f369044773ff9c6383c087466d12adb6fa0828" 771 } 772 }`) 773 }) 774 message := "m" 775 content := []byte("c") 776 sha := "f5f369044773ff9c6383c087466d12adb6fa0828" 777 repositoryContentsOptions := &RepositoryContentFileOptions{ 778 Message: &message, 779 Content: content, 780 SHA: &sha, 781 Committer: &CommitAuthor{Name: Ptr("n"), Email: Ptr("e")}, 782 } 783 ctx := context.Background() 784 updateResponse, _, err := client.Repositories.UpdateFile(ctx, "o", "r", "p", repositoryContentsOptions) 785 if err != nil { 786 t.Errorf("Repositories.UpdateFile returned error: %v", err) 787 } 788 want := &RepositoryContentResponse{ 789 Content: &RepositoryContent{Name: Ptr("p")}, 790 Commit: Commit{ 791 Message: Ptr("m"), 792 SHA: Ptr("f5f369044773ff9c6383c087466d12adb6fa0828"), 793 }, 794 } 795 if !cmp.Equal(updateResponse, want) { 796 t.Errorf("Repositories.UpdateFile returned %+v, want %+v", updateResponse, want) 797 } 798 799 const methodName = "UpdateFile" 800 testBadOptions(t, methodName, func() (err error) { 801 _, _, err = client.Repositories.UpdateFile(ctx, "\n", "\n", "\n", repositoryContentsOptions) 802 return err 803 }) 804 805 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 806 got, resp, err := client.Repositories.UpdateFile(ctx, "o", "r", "p", repositoryContentsOptions) 807 if got != nil { 808 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 809 } 810 return resp, err 811 }) 812 } 813 814 func TestRepositoriesService_DeleteFile(t *testing.T) { 815 t.Parallel() 816 client, mux, _ := setup(t) 817 818 mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { 819 testMethod(t, r, "DELETE") 820 fmt.Fprint(w, `{ 821 "content": null, 822 "commit":{ 823 "message":"m", 824 "sha":"f5f369044773ff9c6383c087466d12adb6fa0828" 825 } 826 }`) 827 }) 828 message := "m" 829 sha := "f5f369044773ff9c6383c087466d12adb6fa0828" 830 repositoryContentsOptions := &RepositoryContentFileOptions{ 831 Message: &message, 832 SHA: &sha, 833 Committer: &CommitAuthor{Name: Ptr("n"), Email: Ptr("e")}, 834 } 835 ctx := context.Background() 836 deleteResponse, _, err := client.Repositories.DeleteFile(ctx, "o", "r", "p", repositoryContentsOptions) 837 if err != nil { 838 t.Errorf("Repositories.DeleteFile returned error: %v", err) 839 } 840 want := &RepositoryContentResponse{ 841 Content: nil, 842 Commit: Commit{ 843 Message: Ptr("m"), 844 SHA: Ptr("f5f369044773ff9c6383c087466d12adb6fa0828"), 845 }, 846 } 847 if !cmp.Equal(deleteResponse, want) { 848 t.Errorf("Repositories.DeleteFile returned %+v, want %+v", deleteResponse, want) 849 } 850 851 const methodName = "DeleteFile" 852 testBadOptions(t, methodName, func() (err error) { 853 _, _, err = client.Repositories.DeleteFile(ctx, "\n", "\n", "\n", repositoryContentsOptions) 854 return err 855 }) 856 857 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 858 got, resp, err := client.Repositories.DeleteFile(ctx, "o", "r", "p", repositoryContentsOptions) 859 if got != nil { 860 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 861 } 862 return resp, err 863 }) 864 } 865 866 func TestRepositoriesService_GetArchiveLink(t *testing.T) { 867 t.Parallel() 868 tcs := []struct { 869 name string 870 respectRateLimits bool 871 }{ 872 { 873 name: "withoutRateLimits", 874 respectRateLimits: false, 875 }, 876 { 877 name: "withRateLimits", 878 respectRateLimits: true, 879 }, 880 } 881 882 for _, tc := range tcs { 883 t.Run(tc.name, func(t *testing.T) { 884 t.Parallel() 885 client, mux, _ := setup(t) 886 client.RateLimitRedirectionalEndpoints = tc.respectRateLimits 887 888 mux.HandleFunc("/repos/o/r/tarball/yo", func(w http.ResponseWriter, r *http.Request) { 889 testMethod(t, r, "GET") 890 http.Redirect(w, r, "http://github.com/a", http.StatusFound) 891 }) 892 ctx := context.Background() 893 url, resp, err := client.Repositories.GetArchiveLink(ctx, "o", "r", Tarball, &RepositoryContentGetOptions{Ref: "yo"}, 1) 894 if err != nil { 895 t.Errorf("Repositories.GetArchiveLink returned error: %v", err) 896 } 897 if resp.StatusCode != http.StatusFound { 898 t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusFound) 899 } 900 want := "http://github.com/a" 901 if url.String() != want { 902 t.Errorf("Repositories.GetArchiveLink returned %+v, want %+v", url.String(), want) 903 } 904 905 const methodName = "GetArchiveLink" 906 testBadOptions(t, methodName, func() (err error) { 907 _, _, err = client.Repositories.GetArchiveLink(ctx, "\n", "\n", Tarball, &RepositoryContentGetOptions{}, 1) 908 return err 909 }) 910 911 // Add custom round tripper 912 client.client.Transport = roundTripperFunc(func(*http.Request) (*http.Response, error) { 913 return nil, errors.New("failed to get archive link") 914 }) 915 testBadOptions(t, methodName, func() (err error) { 916 _, _, err = client.Repositories.GetArchiveLink(ctx, "o", "r", Tarball, &RepositoryContentGetOptions{}, 1) 917 return err 918 }) 919 }) 920 } 921 } 922 923 func TestRepositoriesService_GetArchiveLink_StatusMovedPermanently_dontFollowRedirects(t *testing.T) { 924 t.Parallel() 925 tcs := []struct { 926 name string 927 respectRateLimits bool 928 }{ 929 { 930 name: "withoutRateLimits", 931 respectRateLimits: false, 932 }, 933 { 934 name: "withRateLimits", 935 respectRateLimits: true, 936 }, 937 } 938 939 for _, tc := range tcs { 940 t.Run(tc.name, func(t *testing.T) { 941 t.Parallel() 942 client, mux, _ := setup(t) 943 client.RateLimitRedirectionalEndpoints = tc.respectRateLimits 944 945 mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) { 946 testMethod(t, r, "GET") 947 http.Redirect(w, r, "http://github.com/a", http.StatusMovedPermanently) 948 }) 949 ctx := context.Background() 950 _, resp, _ := client.Repositories.GetArchiveLink(ctx, "o", "r", Tarball, &RepositoryContentGetOptions{}, 0) 951 if resp.StatusCode != http.StatusMovedPermanently { 952 t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusMovedPermanently) 953 } 954 }) 955 } 956 } 957 958 func TestRepositoriesService_GetArchiveLink_StatusMovedPermanently_followRedirects(t *testing.T) { 959 t.Parallel() 960 tcs := []struct { 961 name string 962 respectRateLimits bool 963 }{ 964 { 965 name: "withoutRateLimits", 966 respectRateLimits: false, 967 }, 968 { 969 name: "withRateLimits", 970 respectRateLimits: true, 971 }, 972 } 973 974 for _, tc := range tcs { 975 t.Run(tc.name, func(t *testing.T) { 976 t.Parallel() 977 client, mux, serverURL := setup(t) 978 client.RateLimitRedirectionalEndpoints = tc.respectRateLimits 979 980 // Mock a redirect link, which leads to an archive link 981 mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) { 982 testMethod(t, r, "GET") 983 redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect") 984 http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) 985 }) 986 mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { 987 testMethod(t, r, "GET") 988 http.Redirect(w, r, "http://github.com/a", http.StatusFound) 989 }) 990 ctx := context.Background() 991 url, resp, err := client.Repositories.GetArchiveLink(ctx, "o", "r", Tarball, &RepositoryContentGetOptions{}, 1) 992 if err != nil { 993 t.Errorf("Repositories.GetArchiveLink returned error: %v", err) 994 } 995 if resp.StatusCode != http.StatusFound { 996 t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusFound) 997 } 998 want := "http://github.com/a" 999 if url.String() != want { 1000 t.Errorf("Repositories.GetArchiveLink returned %+v, want %+v", url.String(), want) 1001 } 1002 }) 1003 } 1004 } 1005 1006 func TestRepositoriesService_GetContents_NoTrailingSlashInDirectoryApiPath(t *testing.T) { 1007 t.Parallel() 1008 client, mux, _ := setup(t) 1009 1010 mux.HandleFunc("/repos/o/r/contents/.github", func(w http.ResponseWriter, r *http.Request) { 1011 testMethod(t, r, "GET") 1012 query := r.URL.Query() 1013 if query.Get("ref") != "mybranch" { 1014 t.Errorf("Repositories.GetContents returned %+v, want %+v", query.Get("ref"), "mybranch") 1015 } 1016 fmt.Fprint(w, `{}`) 1017 }) 1018 ctx := context.Background() 1019 _, _, _, err := client.Repositories.GetContents(ctx, "o", "r", ".github/", &RepositoryContentGetOptions{ 1020 Ref: "mybranch", 1021 }) 1022 if err != nil { 1023 t.Fatalf("Repositories.GetContents returned error: %v", err) 1024 } 1025 } 1026 1027 func TestRepositoryContent_Marshal(t *testing.T) { 1028 t.Parallel() 1029 testJSONMarshal(t, &RepositoryContent{}, "{}") 1030 1031 r := &RepositoryContent{ 1032 Type: Ptr("type"), 1033 Target: Ptr("target"), 1034 Encoding: Ptr("encoding"), 1035 Size: Ptr(1), 1036 Name: Ptr("name"), 1037 Path: Ptr("path"), 1038 Content: Ptr("content"), 1039 SHA: Ptr("sha"), 1040 URL: Ptr("url"), 1041 GitURL: Ptr("gurl"), 1042 HTMLURL: Ptr("hurl"), 1043 DownloadURL: Ptr("durl"), 1044 SubmoduleGitURL: Ptr("smgurl"), 1045 } 1046 1047 want := `{ 1048 "type": "type", 1049 "target": "target", 1050 "encoding": "encoding", 1051 "size": 1, 1052 "name": "name", 1053 "path": "path", 1054 "content": "content", 1055 "sha": "sha", 1056 "url": "url", 1057 "git_url": "gurl", 1058 "html_url": "hurl", 1059 "download_url": "durl", 1060 "submodule_git_url": "smgurl" 1061 }` 1062 1063 testJSONMarshal(t, r, want) 1064 } 1065 1066 func TestRepositoryContentResponse_Marshal(t *testing.T) { 1067 t.Parallel() 1068 testJSONMarshal(t, &RepositoryContentResponse{}, "{}") 1069 1070 r := &RepositoryContentResponse{ 1071 Content: &RepositoryContent{ 1072 Type: Ptr("type"), 1073 Target: Ptr("target"), 1074 Encoding: Ptr("encoding"), 1075 Size: Ptr(1), 1076 Name: Ptr("name"), 1077 Path: Ptr("path"), 1078 Content: Ptr("content"), 1079 SHA: Ptr("sha"), 1080 URL: Ptr("url"), 1081 GitURL: Ptr("gurl"), 1082 HTMLURL: Ptr("hurl"), 1083 DownloadURL: Ptr("durl"), 1084 SubmoduleGitURL: Ptr("smgurl"), 1085 }, 1086 Commit: Commit{ 1087 SHA: Ptr("s"), 1088 Author: &CommitAuthor{ 1089 Date: &Timestamp{referenceTime}, 1090 Name: Ptr("n"), 1091 Email: Ptr("e"), 1092 Login: Ptr("u"), 1093 }, 1094 Committer: &CommitAuthor{ 1095 Date: &Timestamp{referenceTime}, 1096 Name: Ptr("n"), 1097 Email: Ptr("e"), 1098 Login: Ptr("u"), 1099 }, 1100 Message: Ptr("m"), 1101 Tree: &Tree{ 1102 SHA: Ptr("s"), 1103 Entries: []*TreeEntry{{ 1104 SHA: Ptr("s"), 1105 Path: Ptr("p"), 1106 Mode: Ptr("m"), 1107 Type: Ptr("t"), 1108 Size: Ptr(1), 1109 Content: Ptr("c"), 1110 URL: Ptr("u"), 1111 }}, 1112 Truncated: Ptr(false), 1113 }, 1114 Parents: nil, 1115 HTMLURL: Ptr("h"), 1116 URL: Ptr("u"), 1117 Verification: &SignatureVerification{ 1118 Verified: Ptr(false), 1119 Reason: Ptr("r"), 1120 Signature: Ptr("s"), 1121 Payload: Ptr("p"), 1122 }, 1123 NodeID: Ptr("n"), 1124 CommentCount: Ptr(1), 1125 }, 1126 } 1127 1128 want := `{ 1129 "content": { 1130 "type": "type", 1131 "target": "target", 1132 "encoding": "encoding", 1133 "size": 1, 1134 "name": "name", 1135 "path": "path", 1136 "content": "content", 1137 "sha": "sha", 1138 "url": "url", 1139 "git_url": "gurl", 1140 "html_url": "hurl", 1141 "download_url": "durl", 1142 "submodule_git_url": "smgurl" 1143 }, 1144 "commit": { 1145 "sha": "s", 1146 "author": { 1147 "date": ` + referenceTimeStr + `, 1148 "name": "n", 1149 "email": "e", 1150 "username": "u" 1151 }, 1152 "committer": { 1153 "date": ` + referenceTimeStr + `, 1154 "name": "n", 1155 "email": "e", 1156 "username": "u" 1157 }, 1158 "message": "m", 1159 "tree": { 1160 "sha": "s", 1161 "tree": [ 1162 { 1163 "sha": "s", 1164 "path": "p", 1165 "mode": "m", 1166 "type": "t", 1167 "size": 1, 1168 "content": "c", 1169 "url": "u" 1170 } 1171 ], 1172 "truncated": false 1173 }, 1174 "html_url": "h", 1175 "url": "u", 1176 "verification": { 1177 "verified": false, 1178 "reason": "r", 1179 "signature": "s", 1180 "payload": "p" 1181 }, 1182 "node_id": "n", 1183 "comment_count": 1 1184 } 1185 }` 1186 1187 testJSONMarshal(t, r, want) 1188 } 1189 1190 func TestRepositoryContentFileOptions_Marshal(t *testing.T) { 1191 t.Parallel() 1192 testJSONMarshal(t, &RepositoryContentFileOptions{}, "{}") 1193 1194 r := &RepositoryContentFileOptions{ 1195 Message: Ptr("type"), 1196 Content: []byte{1}, 1197 SHA: Ptr("type"), 1198 Branch: Ptr("type"), 1199 Author: &CommitAuthor{ 1200 Date: &Timestamp{referenceTime}, 1201 Name: Ptr("name"), 1202 Email: Ptr("email"), 1203 Login: Ptr("login"), 1204 }, 1205 Committer: &CommitAuthor{ 1206 Date: &Timestamp{referenceTime}, 1207 Name: Ptr("name"), 1208 Email: Ptr("email"), 1209 Login: Ptr("login"), 1210 }, 1211 } 1212 1213 want := `{ 1214 "message": "type", 1215 "content": "AQ==", 1216 "sha": "type", 1217 "branch": "type", 1218 "author": { 1219 "date": ` + referenceTimeStr + `, 1220 "name": "name", 1221 "email": "email", 1222 "username": "login" 1223 }, 1224 "committer": { 1225 "date": ` + referenceTimeStr + `, 1226 "name": "name", 1227 "email": "email", 1228 "username": "login" 1229 } 1230 }` 1231 1232 testJSONMarshal(t, r, want) 1233 }