github.com/google/go-github/v33@v33.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  	"fmt"
    11  	"io/ioutil"
    12  	"net/http"
    13  	"net/url"
    14  	"reflect"
    15  	"testing"
    16  )
    17  
    18  func TestRepositoryContent_GetContent(t *testing.T) {
    19  	tests := []struct {
    20  		encoding, content *string // input encoding and content
    21  		want              string  // desired output
    22  		wantErr           bool    // whether an error is expected
    23  	}{
    24  		{
    25  			encoding: String(""),
    26  			content:  String("hello"),
    27  			want:     "hello",
    28  			wantErr:  false,
    29  		},
    30  		{
    31  			encoding: nil,
    32  			content:  String("hello"),
    33  			want:     "hello",
    34  			wantErr:  false,
    35  		},
    36  		{
    37  			encoding: nil,
    38  			content:  nil,
    39  			want:     "",
    40  			wantErr:  false,
    41  		},
    42  		{
    43  			encoding: String("base64"),
    44  			content:  String("aGVsbG8="),
    45  			want:     "hello",
    46  			wantErr:  false,
    47  		},
    48  		{
    49  			encoding: String("bad"),
    50  			content:  String("aGVsbG8="),
    51  			want:     "",
    52  			wantErr:  true,
    53  		},
    54  	}
    55  
    56  	for _, tt := range tests {
    57  		r := RepositoryContent{Encoding: tt.encoding, Content: tt.content}
    58  		got, err := r.GetContent()
    59  		if err != nil && !tt.wantErr {
    60  			t.Errorf("RepositoryContent(%s, %s) returned unexpected error: %v",
    61  				stringOrNil(tt.encoding), stringOrNil(tt.content), err)
    62  		}
    63  		if err == nil && tt.wantErr {
    64  			t.Errorf("RepositoryContent(%s, %s) did not return unexpected error",
    65  				stringOrNil(tt.encoding), stringOrNil(tt.content))
    66  		}
    67  		if want := tt.want; got != want {
    68  			t.Errorf("RepositoryContent.GetContent returned %+v, want %+v", got, want)
    69  		}
    70  	}
    71  }
    72  
    73  // stringOrNil converts a potentially null string pointer to string.
    74  // For non-nil input pointer, the returned string is enclosed in double-quotes.
    75  func stringOrNil(s *string) string {
    76  	if s == nil {
    77  		return "<nil>"
    78  	}
    79  	return fmt.Sprintf("%q", *s)
    80  }
    81  
    82  func TestRepositoriesService_GetReadme(t *testing.T) {
    83  	client, mux, _, teardown := setup()
    84  	defer teardown()
    85  	mux.HandleFunc("/repos/o/r/readme", func(w http.ResponseWriter, r *http.Request) {
    86  		testMethod(t, r, "GET")
    87  		fmt.Fprint(w, `{
    88  		  "type": "file",
    89  		  "encoding": "base64",
    90  		  "size": 5362,
    91  		  "name": "README.md",
    92  		  "path": "README.md"
    93  		}`)
    94  	})
    95  	readme, _, err := client.Repositories.GetReadme(context.Background(), "o", "r", &RepositoryContentGetOptions{})
    96  	if err != nil {
    97  		t.Errorf("Repositories.GetReadme returned error: %v", err)
    98  	}
    99  	want := &RepositoryContent{Type: String("file"), Name: String("README.md"), Size: Int(5362), Encoding: String("base64"), Path: String("README.md")}
   100  	if !reflect.DeepEqual(readme, want) {
   101  		t.Errorf("Repositories.GetReadme returned %+v, want %+v", readme, want)
   102  	}
   103  }
   104  
   105  func TestRepositoriesService_DownloadContents_Success(t *testing.T) {
   106  	client, mux, serverURL, teardown := setup()
   107  	defer teardown()
   108  	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
   109  		testMethod(t, r, "GET")
   110  		fmt.Fprint(w, `[{
   111  		  "type": "file",
   112  		  "name": "f",
   113  		  "download_url": "`+serverURL+baseURLPath+`/download/f"
   114  		}]`)
   115  	})
   116  	mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) {
   117  		testMethod(t, r, "GET")
   118  		fmt.Fprint(w, "foo")
   119  	})
   120  
   121  	r, resp, err := client.Repositories.DownloadContents(context.Background(), "o", "r", "d/f", nil)
   122  	if err != nil {
   123  		t.Errorf("Repositories.DownloadContents returned error: %v", err)
   124  	}
   125  
   126  	if got, want := resp.Response.StatusCode, http.StatusOK; got != want {
   127  		t.Errorf("Repositories.DownloadContents returned status code %v, want %v", got, want)
   128  	}
   129  
   130  	bytes, err := ioutil.ReadAll(r)
   131  	if err != nil {
   132  		t.Errorf("Error reading response body: %v", err)
   133  	}
   134  	r.Close()
   135  
   136  	if got, want := string(bytes), "foo"; got != want {
   137  		t.Errorf("Repositories.DownloadContents returned %v, want %v", got, want)
   138  	}
   139  }
   140  
   141  func TestRepositoriesService_DownloadContents_FailedResponse(t *testing.T) {
   142  	client, mux, serverURL, teardown := setup()
   143  	defer teardown()
   144  	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
   145  		testMethod(t, r, "GET")
   146  		fmt.Fprint(w, `[{
   147  			"type": "file",
   148  			"name": "f",
   149  			"download_url": "`+serverURL+baseURLPath+`/download/f"
   150  		  }]`)
   151  	})
   152  	mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) {
   153  		testMethod(t, r, "GET")
   154  		w.WriteHeader(http.StatusInternalServerError)
   155  		fmt.Fprint(w, "foo error")
   156  	})
   157  
   158  	r, resp, err := client.Repositories.DownloadContents(context.Background(), "o", "r", "d/f", nil)
   159  	if err != nil {
   160  		t.Errorf("Repositories.DownloadContents returned error: %v", err)
   161  	}
   162  
   163  	if got, want := resp.Response.StatusCode, http.StatusInternalServerError; got != want {
   164  		t.Errorf("Repositories.DownloadContents returned status code %v, want %v", got, want)
   165  	}
   166  
   167  	bytes, err := ioutil.ReadAll(r)
   168  	if err != nil {
   169  		t.Errorf("Error reading response body: %v", err)
   170  	}
   171  	r.Close()
   172  
   173  	if got, want := string(bytes), "foo error"; got != want {
   174  		t.Errorf("Repositories.DownloadContents returned %v, want %v", got, want)
   175  	}
   176  }
   177  
   178  func TestRepositoriesService_DownloadContents_NoDownloadURL(t *testing.T) {
   179  	client, mux, _, teardown := setup()
   180  	defer teardown()
   181  	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
   182  		testMethod(t, r, "GET")
   183  		fmt.Fprint(w, `[{
   184  		  "type": "file",
   185  		  "name": "f",
   186  		}]`)
   187  	})
   188  
   189  	_, resp, err := client.Repositories.DownloadContents(context.Background(), "o", "r", "d/f", nil)
   190  	if err == nil {
   191  		t.Errorf("Repositories.DownloadContents did not return expected error")
   192  	}
   193  
   194  	if resp == nil {
   195  		t.Errorf("Repositories.DownloadContents did not return expected response")
   196  	}
   197  }
   198  
   199  func TestRepositoriesService_DownloadContents_NoFile(t *testing.T) {
   200  	client, mux, _, teardown := setup()
   201  	defer teardown()
   202  	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
   203  		testMethod(t, r, "GET")
   204  		fmt.Fprint(w, `[]`)
   205  	})
   206  
   207  	_, resp, err := client.Repositories.DownloadContents(context.Background(), "o", "r", "d/f", nil)
   208  	if err == nil {
   209  		t.Errorf("Repositories.DownloadContents did not return expected error")
   210  	}
   211  
   212  	if resp == nil {
   213  		t.Errorf("Repositories.DownloadContents did not return expected response")
   214  	}
   215  }
   216  
   217  func TestRepositoriesService_GetContents_File(t *testing.T) {
   218  	client, mux, _, teardown := setup()
   219  	defer teardown()
   220  	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
   221  		testMethod(t, r, "GET")
   222  		fmt.Fprint(w, `{
   223  		  "type": "file",
   224  		  "encoding": "base64",
   225  		  "size": 20678,
   226  		  "name": "LICENSE",
   227  		  "path": "LICENSE"
   228  		}`)
   229  	})
   230  	fileContents, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "p", &RepositoryContentGetOptions{})
   231  	if err != nil {
   232  		t.Errorf("Repositories.GetContents returned error: %v", err)
   233  	}
   234  	want := &RepositoryContent{Type: String("file"), Name: String("LICENSE"), Size: Int(20678), Encoding: String("base64"), Path: String("LICENSE")}
   235  	if !reflect.DeepEqual(fileContents, want) {
   236  		t.Errorf("Repositories.GetContents returned %+v, want %+v", fileContents, want)
   237  	}
   238  }
   239  
   240  func TestRepositoriesService_GetContents_FilenameNeedsEscape(t *testing.T) {
   241  	client, mux, _, teardown := setup()
   242  	defer teardown()
   243  	mux.HandleFunc("/repos/o/r/contents/p#?%/中.go", func(w http.ResponseWriter, r *http.Request) {
   244  		testMethod(t, r, "GET")
   245  		fmt.Fprint(w, `{}`)
   246  	})
   247  	_, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "p#?%/中.go", &RepositoryContentGetOptions{})
   248  	if err != nil {
   249  		t.Fatalf("Repositories.GetContents returned error: %v", err)
   250  	}
   251  }
   252  
   253  func TestRepositoriesService_GetContents_DirectoryWithSpaces(t *testing.T) {
   254  	client, mux, _, teardown := setup()
   255  	defer teardown()
   256  	mux.HandleFunc("/repos/o/r/contents/some directory/file.go", func(w http.ResponseWriter, r *http.Request) {
   257  		testMethod(t, r, "GET")
   258  		fmt.Fprint(w, `{}`)
   259  	})
   260  	_, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "some directory/file.go", &RepositoryContentGetOptions{})
   261  	if err != nil {
   262  		t.Fatalf("Repositories.GetContents returned error: %v", err)
   263  	}
   264  }
   265  
   266  func TestRepositoriesService_GetContents_DirectoryWithPlusChars(t *testing.T) {
   267  	client, mux, _, teardown := setup()
   268  	defer teardown()
   269  	mux.HandleFunc("/repos/o/r/contents/some directory+name/file.go", func(w http.ResponseWriter, r *http.Request) {
   270  		testMethod(t, r, "GET")
   271  		fmt.Fprint(w, `{}`)
   272  	})
   273  	_, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "some directory+name/file.go", &RepositoryContentGetOptions{})
   274  	if err != nil {
   275  		t.Fatalf("Repositories.GetContents returned error: %v", err)
   276  	}
   277  }
   278  
   279  func TestRepositoriesService_GetContents_Directory(t *testing.T) {
   280  	client, mux, _, teardown := setup()
   281  	defer teardown()
   282  	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
   283  		testMethod(t, r, "GET")
   284  		fmt.Fprint(w, `[{
   285  		  "type": "dir",
   286  		  "name": "lib",
   287  		  "path": "lib"
   288  		},
   289  		{
   290  		  "type": "file",
   291  		  "size": 20678,
   292  		  "name": "LICENSE",
   293  		  "path": "LICENSE"
   294  		}]`)
   295  	})
   296  	_, directoryContents, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "p", &RepositoryContentGetOptions{})
   297  	if err != nil {
   298  		t.Errorf("Repositories.GetContents returned error: %v", err)
   299  	}
   300  	want := []*RepositoryContent{{Type: String("dir"), Name: String("lib"), Path: String("lib")},
   301  		{Type: String("file"), Name: String("LICENSE"), Size: Int(20678), Path: String("LICENSE")}}
   302  	if !reflect.DeepEqual(directoryContents, want) {
   303  		t.Errorf("Repositories.GetContents_Directory returned %+v, want %+v", directoryContents, want)
   304  	}
   305  }
   306  
   307  func TestRepositoriesService_CreateFile(t *testing.T) {
   308  	client, mux, _, teardown := setup()
   309  	defer teardown()
   310  	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
   311  		testMethod(t, r, "PUT")
   312  		fmt.Fprint(w, `{
   313  			"content":{
   314  				"name":"p"
   315  			},
   316  			"commit":{
   317  				"message":"m",
   318  				"sha":"f5f369044773ff9c6383c087466d12adb6fa0828"
   319  			}
   320  		}`)
   321  	})
   322  	message := "m"
   323  	content := []byte("c")
   324  	repositoryContentsOptions := &RepositoryContentFileOptions{
   325  		Message:   &message,
   326  		Content:   content,
   327  		Committer: &CommitAuthor{Name: String("n"), Email: String("e")},
   328  	}
   329  	createResponse, _, err := client.Repositories.CreateFile(context.Background(), "o", "r", "p", repositoryContentsOptions)
   330  	if err != nil {
   331  		t.Errorf("Repositories.CreateFile returned error: %v", err)
   332  	}
   333  	want := &RepositoryContentResponse{
   334  		Content: &RepositoryContent{Name: String("p")},
   335  		Commit: Commit{
   336  			Message: String("m"),
   337  			SHA:     String("f5f369044773ff9c6383c087466d12adb6fa0828"),
   338  		},
   339  	}
   340  	if !reflect.DeepEqual(createResponse, want) {
   341  		t.Errorf("Repositories.CreateFile returned %+v, want %+v", createResponse, want)
   342  	}
   343  }
   344  
   345  func TestRepositoriesService_UpdateFile(t *testing.T) {
   346  	client, mux, _, teardown := setup()
   347  	defer teardown()
   348  	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
   349  		testMethod(t, r, "PUT")
   350  		fmt.Fprint(w, `{
   351  			"content":{
   352  				"name":"p"
   353  			},
   354  			"commit":{
   355  				"message":"m",
   356  				"sha":"f5f369044773ff9c6383c087466d12adb6fa0828"
   357  			}
   358  		}`)
   359  	})
   360  	message := "m"
   361  	content := []byte("c")
   362  	sha := "f5f369044773ff9c6383c087466d12adb6fa0828"
   363  	repositoryContentsOptions := &RepositoryContentFileOptions{
   364  		Message:   &message,
   365  		Content:   content,
   366  		SHA:       &sha,
   367  		Committer: &CommitAuthor{Name: String("n"), Email: String("e")},
   368  	}
   369  	updateResponse, _, err := client.Repositories.UpdateFile(context.Background(), "o", "r", "p", repositoryContentsOptions)
   370  	if err != nil {
   371  		t.Errorf("Repositories.UpdateFile returned error: %v", err)
   372  	}
   373  	want := &RepositoryContentResponse{
   374  		Content: &RepositoryContent{Name: String("p")},
   375  		Commit: Commit{
   376  			Message: String("m"),
   377  			SHA:     String("f5f369044773ff9c6383c087466d12adb6fa0828"),
   378  		},
   379  	}
   380  	if !reflect.DeepEqual(updateResponse, want) {
   381  		t.Errorf("Repositories.UpdateFile returned %+v, want %+v", updateResponse, want)
   382  	}
   383  }
   384  
   385  func TestRepositoriesService_DeleteFile(t *testing.T) {
   386  	client, mux, _, teardown := setup()
   387  	defer teardown()
   388  	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
   389  		testMethod(t, r, "DELETE")
   390  		fmt.Fprint(w, `{
   391  			"content": null,
   392  			"commit":{
   393  				"message":"m",
   394  				"sha":"f5f369044773ff9c6383c087466d12adb6fa0828"
   395  			}
   396  		}`)
   397  	})
   398  	message := "m"
   399  	sha := "f5f369044773ff9c6383c087466d12adb6fa0828"
   400  	repositoryContentsOptions := &RepositoryContentFileOptions{
   401  		Message:   &message,
   402  		SHA:       &sha,
   403  		Committer: &CommitAuthor{Name: String("n"), Email: String("e")},
   404  	}
   405  	deleteResponse, _, err := client.Repositories.DeleteFile(context.Background(), "o", "r", "p", repositoryContentsOptions)
   406  	if err != nil {
   407  		t.Errorf("Repositories.DeleteFile returned error: %v", err)
   408  	}
   409  	want := &RepositoryContentResponse{
   410  		Content: nil,
   411  		Commit: Commit{
   412  			Message: String("m"),
   413  			SHA:     String("f5f369044773ff9c6383c087466d12adb6fa0828"),
   414  		},
   415  	}
   416  	if !reflect.DeepEqual(deleteResponse, want) {
   417  		t.Errorf("Repositories.DeleteFile returned %+v, want %+v", deleteResponse, want)
   418  	}
   419  }
   420  
   421  func TestRepositoriesService_GetArchiveLink(t *testing.T) {
   422  	client, mux, _, teardown := setup()
   423  	defer teardown()
   424  	mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) {
   425  		testMethod(t, r, "GET")
   426  		http.Redirect(w, r, "http://github.com/a", http.StatusFound)
   427  	})
   428  	url, resp, err := client.Repositories.GetArchiveLink(context.Background(), "o", "r", Tarball, &RepositoryContentGetOptions{}, true)
   429  	if err != nil {
   430  		t.Errorf("Repositories.GetArchiveLink returned error: %v", err)
   431  	}
   432  	if resp.StatusCode != http.StatusFound {
   433  		t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusFound)
   434  	}
   435  	want := "http://github.com/a"
   436  	if url.String() != want {
   437  		t.Errorf("Repositories.GetArchiveLink returned %+v, want %+v", url.String(), want)
   438  	}
   439  }
   440  
   441  func TestRepositoriesService_GetArchiveLink_StatusMovedPermanently_dontFollowRedirects(t *testing.T) {
   442  	client, mux, _, teardown := setup()
   443  	defer teardown()
   444  	mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) {
   445  		testMethod(t, r, "GET")
   446  		http.Redirect(w, r, "http://github.com/a", http.StatusMovedPermanently)
   447  	})
   448  	_, resp, _ := client.Repositories.GetArchiveLink(context.Background(), "o", "r", Tarball, &RepositoryContentGetOptions{}, false)
   449  	if resp.StatusCode != http.StatusMovedPermanently {
   450  		t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusMovedPermanently)
   451  	}
   452  }
   453  
   454  func TestRepositoriesService_GetArchiveLink_StatusMovedPermanently_followRedirects(t *testing.T) {
   455  	client, mux, serverURL, teardown := setup()
   456  	defer teardown()
   457  	// Mock a redirect link, which leads to an archive link
   458  	mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) {
   459  		testMethod(t, r, "GET")
   460  		redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect")
   461  		http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently)
   462  	})
   463  	mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
   464  		testMethod(t, r, "GET")
   465  		http.Redirect(w, r, "http://github.com/a", http.StatusFound)
   466  	})
   467  	url, resp, err := client.Repositories.GetArchiveLink(context.Background(), "o", "r", Tarball, &RepositoryContentGetOptions{}, true)
   468  	if err != nil {
   469  		t.Errorf("Repositories.GetArchiveLink returned error: %v", err)
   470  	}
   471  	if resp.StatusCode != http.StatusFound {
   472  		t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusFound)
   473  	}
   474  	want := "http://github.com/a"
   475  	if url.String() != want {
   476  		t.Errorf("Repositories.GetArchiveLink returned %+v, want %+v", url.String(), want)
   477  	}
   478  }
   479  
   480  func TestRepositoriesService_GetContents_NoTrailingSlashInDirectoryApiPath(t *testing.T) {
   481  	client, mux, _, teardown := setup()
   482  	defer teardown()
   483  	mux.HandleFunc("/repos/o/r/contents/.github", func(w http.ResponseWriter, r *http.Request) {
   484  		testMethod(t, r, "GET")
   485  		query := r.URL.Query()
   486  		if query.Get("ref") != "mybranch" {
   487  			t.Errorf("Repositories.GetContents returned %+v, want %+v", query.Get("ref"), "mybranch")
   488  		}
   489  		fmt.Fprint(w, `{}`)
   490  	})
   491  	_, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", ".github/", &RepositoryContentGetOptions{
   492  		Ref: "mybranch",
   493  	})
   494  	if err != nil {
   495  		t.Fatalf("Repositories.GetContents returned error: %v", err)
   496  	}
   497  }