github.com/google/go-github/v65@v65.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  	tests := []struct {
    22  		encoding, content *string // input encoding and content
    23  		want              string  // desired output
    24  		wantErr           bool    // whether an error is expected
    25  	}{
    26  		{
    27  			encoding: String(""),
    28  			content:  String("hello"),
    29  			want:     "hello",
    30  			wantErr:  false,
    31  		},
    32  		{
    33  			encoding: nil,
    34  			content:  String("hello"),
    35  			want:     "hello",
    36  			wantErr:  false,
    37  		},
    38  		{
    39  			encoding: nil,
    40  			content:  nil,
    41  			want:     "",
    42  			wantErr:  false,
    43  		},
    44  		{
    45  			encoding: String("base64"),
    46  			content:  String("aGVsbG8="),
    47  			want:     "hello",
    48  			wantErr:  false,
    49  		},
    50  		{
    51  			encoding: String("bad"),
    52  			content:  String("aGVsbG8="),
    53  			want:     "",
    54  			wantErr:  true,
    55  		},
    56  		{
    57  			encoding: String("none"),
    58  			content:  nil,
    59  			want:     "",
    60  			wantErr:  true,
    61  		},
    62  	}
    63  
    64  	for _, tt := range tests {
    65  		r := RepositoryContent{Encoding: tt.encoding, Content: tt.content}
    66  		got, err := r.GetContent()
    67  		if err != nil && !tt.wantErr {
    68  			t.Errorf("RepositoryContent(%s, %s) returned unexpected error: %v",
    69  				stringOrNil(tt.encoding), stringOrNil(tt.content), err)
    70  		}
    71  		if err == nil && tt.wantErr {
    72  			t.Errorf("RepositoryContent(%s, %s) did not return unexpected error",
    73  				stringOrNil(tt.encoding), stringOrNil(tt.content))
    74  		}
    75  		if want := tt.want; got != want {
    76  			t.Errorf("RepositoryContent.GetContent returned %+v, want %+v", got, want)
    77  		}
    78  	}
    79  }
    80  
    81  // stringOrNil converts a potentially null string pointer to string.
    82  // For non-nil input pointer, the returned string is enclosed in double-quotes.
    83  func stringOrNil(s *string) string {
    84  	if s == nil {
    85  		return "<nil>"
    86  	}
    87  	return fmt.Sprintf("%q", *s)
    88  }
    89  
    90  func TestRepositoriesService_GetReadme(t *testing.T) {
    91  	client, mux, _, teardown := setup()
    92  	defer teardown()
    93  	mux.HandleFunc("/repos/o/r/readme", func(w http.ResponseWriter, r *http.Request) {
    94  		testMethod(t, r, "GET")
    95  		fmt.Fprint(w, `{
    96  		  "type": "file",
    97  		  "encoding": "base64",
    98  		  "size": 5362,
    99  		  "name": "README.md",
   100  		  "path": "README.md"
   101  		}`)
   102  	})
   103  	ctx := context.Background()
   104  	readme, _, err := client.Repositories.GetReadme(ctx, "o", "r", &RepositoryContentGetOptions{})
   105  	if err != nil {
   106  		t.Errorf("Repositories.GetReadme returned error: %v", err)
   107  	}
   108  	want := &RepositoryContent{Type: String("file"), Name: String("README.md"), Size: Int(5362), Encoding: String("base64"), Path: String("README.md")}
   109  	if !cmp.Equal(readme, want) {
   110  		t.Errorf("Repositories.GetReadme returned %+v, want %+v", readme, want)
   111  	}
   112  
   113  	const methodName = "GetReadme"
   114  	testBadOptions(t, methodName, func() (err error) {
   115  		_, _, err = client.Repositories.GetReadme(ctx, "\n", "\n", &RepositoryContentGetOptions{})
   116  		return err
   117  	})
   118  
   119  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   120  		got, resp, err := client.Repositories.GetReadme(ctx, "o", "r", &RepositoryContentGetOptions{})
   121  		if got != nil {
   122  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   123  		}
   124  		return resp, err
   125  	})
   126  }
   127  
   128  func TestRepositoriesService_DownloadContents_Success(t *testing.T) {
   129  	client, mux, serverURL, teardown := setup()
   130  	defer teardown()
   131  	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
   132  		testMethod(t, r, "GET")
   133  		fmt.Fprint(w, `[{
   134  		  "type": "file",
   135  		  "name": "f",
   136  		  "download_url": "`+serverURL+baseURLPath+`/download/f"
   137  		}]`)
   138  	})
   139  	mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) {
   140  		testMethod(t, r, "GET")
   141  		fmt.Fprint(w, "foo")
   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_FailedResponse(t *testing.T) {
   180  	client, mux, serverURL, teardown := setup()
   181  	defer teardown()
   182  	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
   183  		testMethod(t, r, "GET")
   184  		fmt.Fprint(w, `[{
   185  			"type": "file",
   186  			"name": "f",
   187  			"download_url": "`+serverURL+baseURLPath+`/download/f"
   188  		  }]`)
   189  	})
   190  	mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) {
   191  		testMethod(t, r, "GET")
   192  		w.WriteHeader(http.StatusInternalServerError)
   193  		fmt.Fprint(w, "foo error")
   194  	})
   195  
   196  	ctx := context.Background()
   197  	r, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil)
   198  	if err != nil {
   199  		t.Errorf("Repositories.DownloadContents returned error: %v", err)
   200  	}
   201  
   202  	if got, want := resp.Response.StatusCode, http.StatusInternalServerError; got != want {
   203  		t.Errorf("Repositories.DownloadContents returned status code %v, want %v", got, want)
   204  	}
   205  
   206  	bytes, err := io.ReadAll(r)
   207  	if err != nil {
   208  		t.Errorf("Error reading response body: %v", err)
   209  	}
   210  	r.Close()
   211  
   212  	if got, want := string(bytes), "foo error"; got != want {
   213  		t.Errorf("Repositories.DownloadContents returned %v, want %v", got, want)
   214  	}
   215  }
   216  
   217  func TestRepositoriesService_DownloadContents_NoDownloadURL(t *testing.T) {
   218  	client, mux, _, teardown := setup()
   219  	defer teardown()
   220  	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
   221  		testMethod(t, r, "GET")
   222  		fmt.Fprint(w, `[{
   223  		  "type": "file",
   224  		  "name": "f",
   225  		}]`)
   226  	})
   227  
   228  	ctx := context.Background()
   229  	_, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil)
   230  	if err == nil {
   231  		t.Errorf("Repositories.DownloadContents did not return expected error")
   232  	}
   233  
   234  	if resp == nil {
   235  		t.Errorf("Repositories.DownloadContents did not return expected response")
   236  	}
   237  }
   238  
   239  func TestRepositoriesService_DownloadContents_NoFile(t *testing.T) {
   240  	client, mux, _, teardown := setup()
   241  	defer teardown()
   242  	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
   243  		testMethod(t, r, "GET")
   244  		fmt.Fprint(w, `[]`)
   245  	})
   246  
   247  	ctx := context.Background()
   248  	_, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil)
   249  	if err == nil {
   250  		t.Errorf("Repositories.DownloadContents did not return expected error")
   251  	}
   252  
   253  	if resp == nil {
   254  		t.Errorf("Repositories.DownloadContents did not return expected response")
   255  	}
   256  }
   257  
   258  func TestRepositoriesService_DownloadContentsWithMeta_Success(t *testing.T) {
   259  	client, mux, serverURL, teardown := setup()
   260  	defer teardown()
   261  	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
   262  		testMethod(t, r, "GET")
   263  		fmt.Fprint(w, `[{
   264  		  "type": "file",
   265  		  "name": "f",
   266  		  "download_url": "`+serverURL+baseURLPath+`/download/f"
   267  		}]`)
   268  	})
   269  	mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) {
   270  		testMethod(t, r, "GET")
   271  		fmt.Fprint(w, "foo")
   272  	})
   273  
   274  	ctx := context.Background()
   275  	r, c, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil)
   276  	if err != nil {
   277  		t.Errorf("Repositories.DownloadContentsWithMeta returned error: %v", err)
   278  	}
   279  
   280  	if got, want := resp.Response.StatusCode, http.StatusOK; got != want {
   281  		t.Errorf("Repositories.DownloadContentsWithMeta returned status code %v, want %v", got, want)
   282  	}
   283  
   284  	bytes, err := io.ReadAll(r)
   285  	if err != nil {
   286  		t.Errorf("Error reading response body: %v", err)
   287  	}
   288  	r.Close()
   289  
   290  	if got, want := string(bytes), "foo"; got != want {
   291  		t.Errorf("Repositories.DownloadContentsWithMeta returned %v, want %v", got, want)
   292  	}
   293  
   294  	if c != nil && c.Name != nil {
   295  		if got, want := *c.Name, "f"; got != want {
   296  			t.Errorf("Repositories.DownloadContentsWithMeta returned content name %v, want %v", got, want)
   297  		}
   298  	} else {
   299  		t.Errorf("Returned RepositoryContent is null")
   300  	}
   301  
   302  	const methodName = "DownloadContentsWithMeta"
   303  	testBadOptions(t, methodName, func() (err error) {
   304  		_, _, _, err = client.Repositories.DownloadContentsWithMeta(ctx, "\n", "\n", "\n", nil)
   305  		return err
   306  	})
   307  
   308  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   309  		got, cot, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil)
   310  		if got != nil {
   311  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   312  		}
   313  		if cot != nil {
   314  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, cot)
   315  		}
   316  		return resp, err
   317  	})
   318  }
   319  
   320  func TestRepositoriesService_DownloadContentsWithMeta_FailedResponse(t *testing.T) {
   321  	client, mux, serverURL, teardown := setup()
   322  	defer teardown()
   323  	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
   324  		testMethod(t, r, "GET")
   325  		fmt.Fprint(w, `[{
   326  			"type": "file",
   327  			"name": "f",
   328  			"download_url": "`+serverURL+baseURLPath+`/download/f"
   329  		  }]`)
   330  	})
   331  	mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) {
   332  		testMethod(t, r, "GET")
   333  		w.WriteHeader(http.StatusInternalServerError)
   334  		fmt.Fprint(w, "foo error")
   335  	})
   336  
   337  	ctx := context.Background()
   338  	r, c, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil)
   339  	if err != nil {
   340  		t.Errorf("Repositories.DownloadContentsWithMeta returned error: %v", err)
   341  	}
   342  
   343  	if got, want := resp.Response.StatusCode, http.StatusInternalServerError; got != want {
   344  		t.Errorf("Repositories.DownloadContentsWithMeta returned status code %v, want %v", got, want)
   345  	}
   346  
   347  	bytes, err := io.ReadAll(r)
   348  	if err != nil {
   349  		t.Errorf("Error reading response body: %v", err)
   350  	}
   351  	r.Close()
   352  
   353  	if got, want := string(bytes), "foo error"; got != want {
   354  		t.Errorf("Repositories.DownloadContentsWithMeta returned %v, want %v", got, want)
   355  	}
   356  
   357  	if c != nil && c.Name != nil {
   358  		if got, want := *c.Name, "f"; got != want {
   359  			t.Errorf("Repositories.DownloadContentsWithMeta returned content name %v, want %v", got, want)
   360  		}
   361  	} else {
   362  		t.Errorf("Returned RepositoryContent is null")
   363  	}
   364  }
   365  
   366  func TestRepositoriesService_DownloadContentsWithMeta_NoDownloadURL(t *testing.T) {
   367  	client, mux, _, teardown := setup()
   368  	defer teardown()
   369  	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
   370  		testMethod(t, r, "GET")
   371  		fmt.Fprint(w, `[{
   372  		  "type": "file",
   373  		  "name": "f",
   374  		}]`)
   375  	})
   376  
   377  	ctx := context.Background()
   378  	_, _, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil)
   379  	if err == nil {
   380  		t.Errorf("Repositories.DownloadContentsWithMeta did not return expected error")
   381  	}
   382  
   383  	if resp == nil {
   384  		t.Errorf("Repositories.DownloadContentsWithMeta did not return expected response")
   385  	}
   386  }
   387  
   388  func TestRepositoriesService_DownloadContentsWithMeta_NoFile(t *testing.T) {
   389  	client, mux, _, teardown := setup()
   390  	defer teardown()
   391  	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
   392  		testMethod(t, r, "GET")
   393  		fmt.Fprint(w, `[]`)
   394  	})
   395  
   396  	ctx := context.Background()
   397  	_, _, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil)
   398  	if err == nil {
   399  		t.Errorf("Repositories.DownloadContentsWithMeta did not return expected error")
   400  	}
   401  
   402  	if resp == nil {
   403  		t.Errorf("Repositories.DownloadContentsWithMeta did not return expected response")
   404  	}
   405  }
   406  
   407  func TestRepositoriesService_GetContents_File(t *testing.T) {
   408  	client, mux, _, teardown := setup()
   409  	defer teardown()
   410  	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
   411  		testMethod(t, r, "GET")
   412  		fmt.Fprint(w, `{
   413  		  "type": "file",
   414  		  "encoding": "base64",
   415  		  "size": 20678,
   416  		  "name": "LICENSE",
   417  		  "path": "LICENSE"
   418  		}`)
   419  	})
   420  	ctx := context.Background()
   421  	fileContents, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "p", &RepositoryContentGetOptions{})
   422  	if err != nil {
   423  		t.Errorf("Repositories.GetContents returned error: %v", err)
   424  	}
   425  	want := &RepositoryContent{Type: String("file"), Name: String("LICENSE"), Size: Int(20678), Encoding: String("base64"), Path: String("LICENSE")}
   426  	if !cmp.Equal(fileContents, want) {
   427  		t.Errorf("Repositories.GetContents returned %+v, want %+v", fileContents, want)
   428  	}
   429  
   430  	const methodName = "GetContents"
   431  	testBadOptions(t, methodName, func() (err error) {
   432  		_, _, _, err = client.Repositories.GetContents(ctx, "\n", "\n", "\n", &RepositoryContentGetOptions{})
   433  		return err
   434  	})
   435  
   436  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   437  		got, _, resp, err := client.Repositories.GetContents(ctx, "o", "r", "p", &RepositoryContentGetOptions{})
   438  		if got != nil {
   439  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   440  		}
   441  		return resp, err
   442  	})
   443  }
   444  
   445  func TestRepositoriesService_GetContents_FilenameNeedsEscape(t *testing.T) {
   446  	client, mux, _, teardown := setup()
   447  	defer teardown()
   448  	mux.HandleFunc("/repos/o/r/contents/p#?%/中.go", func(w http.ResponseWriter, r *http.Request) {
   449  		testMethod(t, r, "GET")
   450  		fmt.Fprint(w, `{}`)
   451  	})
   452  	ctx := context.Background()
   453  	_, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "p#?%/中.go", &RepositoryContentGetOptions{})
   454  	if err != nil {
   455  		t.Fatalf("Repositories.GetContents returned error: %v", err)
   456  	}
   457  }
   458  
   459  func TestRepositoriesService_GetContents_DirectoryWithSpaces(t *testing.T) {
   460  	client, mux, _, teardown := setup()
   461  	defer teardown()
   462  	mux.HandleFunc("/repos/o/r/contents/some directory/file.go", func(w http.ResponseWriter, r *http.Request) {
   463  		testMethod(t, r, "GET")
   464  		fmt.Fprint(w, `{}`)
   465  	})
   466  	ctx := context.Background()
   467  	_, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "some directory/file.go", &RepositoryContentGetOptions{})
   468  	if err != nil {
   469  		t.Fatalf("Repositories.GetContents returned error: %v", err)
   470  	}
   471  }
   472  
   473  func TestRepositoriesService_GetContents_PathWithParent(t *testing.T) {
   474  	client, mux, _, teardown := setup()
   475  	defer teardown()
   476  	mux.HandleFunc("/repos/o/r/contents/some/../directory/file.go", func(w http.ResponseWriter, r *http.Request) {
   477  		testMethod(t, r, "GET")
   478  		fmt.Fprint(w, `{}`)
   479  	})
   480  	ctx := context.Background()
   481  	_, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "some/../directory/file.go", &RepositoryContentGetOptions{})
   482  	if err == nil {
   483  		t.Fatal("Repositories.GetContents expected error but got none")
   484  	}
   485  }
   486  
   487  func TestRepositoriesService_GetContents_DirectoryWithPlusChars(t *testing.T) {
   488  	client, mux, _, teardown := setup()
   489  	defer teardown()
   490  	mux.HandleFunc("/repos/o/r/contents/some directory+name/file.go", func(w http.ResponseWriter, r *http.Request) {
   491  		testMethod(t, r, "GET")
   492  		fmt.Fprint(w, `{}`)
   493  	})
   494  	ctx := context.Background()
   495  	_, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "some directory+name/file.go", &RepositoryContentGetOptions{})
   496  	if err != nil {
   497  		t.Fatalf("Repositories.GetContents returned error: %v", err)
   498  	}
   499  }
   500  
   501  func TestRepositoriesService_GetContents_Directory(t *testing.T) {
   502  	client, mux, _, teardown := setup()
   503  	defer teardown()
   504  	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
   505  		testMethod(t, r, "GET")
   506  		fmt.Fprint(w, `[{
   507  		  "type": "dir",
   508  		  "name": "lib",
   509  		  "path": "lib"
   510  		},
   511  		{
   512  		  "type": "file",
   513  		  "size": 20678,
   514  		  "name": "LICENSE",
   515  		  "path": "LICENSE"
   516  		}]`)
   517  	})
   518  	ctx := context.Background()
   519  	_, directoryContents, _, err := client.Repositories.GetContents(ctx, "o", "r", "p", &RepositoryContentGetOptions{})
   520  	if err != nil {
   521  		t.Errorf("Repositories.GetContents returned error: %v", err)
   522  	}
   523  	want := []*RepositoryContent{{Type: String("dir"), Name: String("lib"), Path: String("lib")},
   524  		{Type: String("file"), Name: String("LICENSE"), Size: Int(20678), Path: String("LICENSE")}}
   525  	if !cmp.Equal(directoryContents, want) {
   526  		t.Errorf("Repositories.GetContents_Directory returned %+v, want %+v", directoryContents, want)
   527  	}
   528  }
   529  
   530  func TestRepositoriesService_CreateFile(t *testing.T) {
   531  	client, mux, _, teardown := setup()
   532  	defer teardown()
   533  	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
   534  		testMethod(t, r, "PUT")
   535  		fmt.Fprint(w, `{
   536  			"content":{
   537  				"name":"p"
   538  			},
   539  			"commit":{
   540  				"message":"m",
   541  				"sha":"f5f369044773ff9c6383c087466d12adb6fa0828"
   542  			}
   543  		}`)
   544  	})
   545  	message := "m"
   546  	content := []byte("c")
   547  	repositoryContentsOptions := &RepositoryContentFileOptions{
   548  		Message:   &message,
   549  		Content:   content,
   550  		Committer: &CommitAuthor{Name: String("n"), Email: String("e")},
   551  	}
   552  	ctx := context.Background()
   553  	createResponse, _, err := client.Repositories.CreateFile(ctx, "o", "r", "p", repositoryContentsOptions)
   554  	if err != nil {
   555  		t.Errorf("Repositories.CreateFile returned error: %v", err)
   556  	}
   557  	want := &RepositoryContentResponse{
   558  		Content: &RepositoryContent{Name: String("p")},
   559  		Commit: Commit{
   560  			Message: String("m"),
   561  			SHA:     String("f5f369044773ff9c6383c087466d12adb6fa0828"),
   562  		},
   563  	}
   564  	if !cmp.Equal(createResponse, want) {
   565  		t.Errorf("Repositories.CreateFile returned %+v, want %+v", createResponse, want)
   566  	}
   567  
   568  	const methodName = "CreateFile"
   569  	testBadOptions(t, methodName, func() (err error) {
   570  		_, _, err = client.Repositories.CreateFile(ctx, "\n", "\n", "\n", repositoryContentsOptions)
   571  		return err
   572  	})
   573  
   574  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   575  		got, resp, err := client.Repositories.CreateFile(ctx, "o", "r", "p", repositoryContentsOptions)
   576  		if got != nil {
   577  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   578  		}
   579  		return resp, err
   580  	})
   581  }
   582  
   583  func TestRepositoriesService_UpdateFile(t *testing.T) {
   584  	client, mux, _, teardown := setup()
   585  	defer teardown()
   586  	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
   587  		testMethod(t, r, "PUT")
   588  		fmt.Fprint(w, `{
   589  			"content":{
   590  				"name":"p"
   591  			},
   592  			"commit":{
   593  				"message":"m",
   594  				"sha":"f5f369044773ff9c6383c087466d12adb6fa0828"
   595  			}
   596  		}`)
   597  	})
   598  	message := "m"
   599  	content := []byte("c")
   600  	sha := "f5f369044773ff9c6383c087466d12adb6fa0828"
   601  	repositoryContentsOptions := &RepositoryContentFileOptions{
   602  		Message:   &message,
   603  		Content:   content,
   604  		SHA:       &sha,
   605  		Committer: &CommitAuthor{Name: String("n"), Email: String("e")},
   606  	}
   607  	ctx := context.Background()
   608  	updateResponse, _, err := client.Repositories.UpdateFile(ctx, "o", "r", "p", repositoryContentsOptions)
   609  	if err != nil {
   610  		t.Errorf("Repositories.UpdateFile returned error: %v", err)
   611  	}
   612  	want := &RepositoryContentResponse{
   613  		Content: &RepositoryContent{Name: String("p")},
   614  		Commit: Commit{
   615  			Message: String("m"),
   616  			SHA:     String("f5f369044773ff9c6383c087466d12adb6fa0828"),
   617  		},
   618  	}
   619  	if !cmp.Equal(updateResponse, want) {
   620  		t.Errorf("Repositories.UpdateFile returned %+v, want %+v", updateResponse, want)
   621  	}
   622  
   623  	const methodName = "UpdateFile"
   624  	testBadOptions(t, methodName, func() (err error) {
   625  		_, _, err = client.Repositories.UpdateFile(ctx, "\n", "\n", "\n", repositoryContentsOptions)
   626  		return err
   627  	})
   628  
   629  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   630  		got, resp, err := client.Repositories.UpdateFile(ctx, "o", "r", "p", repositoryContentsOptions)
   631  		if got != nil {
   632  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   633  		}
   634  		return resp, err
   635  	})
   636  }
   637  
   638  func TestRepositoriesService_DeleteFile(t *testing.T) {
   639  	client, mux, _, teardown := setup()
   640  	defer teardown()
   641  	mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) {
   642  		testMethod(t, r, "DELETE")
   643  		fmt.Fprint(w, `{
   644  			"content": null,
   645  			"commit":{
   646  				"message":"m",
   647  				"sha":"f5f369044773ff9c6383c087466d12adb6fa0828"
   648  			}
   649  		}`)
   650  	})
   651  	message := "m"
   652  	sha := "f5f369044773ff9c6383c087466d12adb6fa0828"
   653  	repositoryContentsOptions := &RepositoryContentFileOptions{
   654  		Message:   &message,
   655  		SHA:       &sha,
   656  		Committer: &CommitAuthor{Name: String("n"), Email: String("e")},
   657  	}
   658  	ctx := context.Background()
   659  	deleteResponse, _, err := client.Repositories.DeleteFile(ctx, "o", "r", "p", repositoryContentsOptions)
   660  	if err != nil {
   661  		t.Errorf("Repositories.DeleteFile returned error: %v", err)
   662  	}
   663  	want := &RepositoryContentResponse{
   664  		Content: nil,
   665  		Commit: Commit{
   666  			Message: String("m"),
   667  			SHA:     String("f5f369044773ff9c6383c087466d12adb6fa0828"),
   668  		},
   669  	}
   670  	if !cmp.Equal(deleteResponse, want) {
   671  		t.Errorf("Repositories.DeleteFile returned %+v, want %+v", deleteResponse, want)
   672  	}
   673  
   674  	const methodName = "DeleteFile"
   675  	testBadOptions(t, methodName, func() (err error) {
   676  		_, _, err = client.Repositories.DeleteFile(ctx, "\n", "\n", "\n", repositoryContentsOptions)
   677  		return err
   678  	})
   679  
   680  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   681  		got, resp, err := client.Repositories.DeleteFile(ctx, "o", "r", "p", repositoryContentsOptions)
   682  		if got != nil {
   683  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   684  		}
   685  		return resp, err
   686  	})
   687  }
   688  
   689  func TestRepositoriesService_GetArchiveLink(t *testing.T) {
   690  	client, mux, _, teardown := setup()
   691  	defer teardown()
   692  	mux.HandleFunc("/repos/o/r/tarball/yo", func(w http.ResponseWriter, r *http.Request) {
   693  		testMethod(t, r, "GET")
   694  		http.Redirect(w, r, "http://github.com/a", http.StatusFound)
   695  	})
   696  	ctx := context.Background()
   697  	url, resp, err := client.Repositories.GetArchiveLink(ctx, "o", "r", Tarball, &RepositoryContentGetOptions{Ref: "yo"}, 1)
   698  	if err != nil {
   699  		t.Errorf("Repositories.GetArchiveLink returned error: %v", err)
   700  	}
   701  	if resp.StatusCode != http.StatusFound {
   702  		t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusFound)
   703  	}
   704  	want := "http://github.com/a"
   705  	if url.String() != want {
   706  		t.Errorf("Repositories.GetArchiveLink returned %+v, want %+v", url.String(), want)
   707  	}
   708  
   709  	const methodName = "GetArchiveLink"
   710  	testBadOptions(t, methodName, func() (err error) {
   711  		_, _, err = client.Repositories.GetArchiveLink(ctx, "\n", "\n", Tarball, &RepositoryContentGetOptions{}, 1)
   712  		return err
   713  	})
   714  
   715  	// Add custom round tripper
   716  	client.client.Transport = roundTripperFunc(func(r *http.Request) (*http.Response, error) {
   717  		return nil, errors.New("failed to get archive link")
   718  	})
   719  	testBadOptions(t, methodName, func() (err error) {
   720  		_, _, err = client.Repositories.GetArchiveLink(ctx, "o", "r", Tarball, &RepositoryContentGetOptions{}, 1)
   721  		return err
   722  	})
   723  }
   724  
   725  func TestRepositoriesService_GetArchiveLink_StatusMovedPermanently_dontFollowRedirects(t *testing.T) {
   726  	client, mux, _, teardown := setup()
   727  	defer teardown()
   728  	mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) {
   729  		testMethod(t, r, "GET")
   730  		http.Redirect(w, r, "http://github.com/a", http.StatusMovedPermanently)
   731  	})
   732  	ctx := context.Background()
   733  	_, resp, _ := client.Repositories.GetArchiveLink(ctx, "o", "r", Tarball, &RepositoryContentGetOptions{}, 0)
   734  	if resp.StatusCode != http.StatusMovedPermanently {
   735  		t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusMovedPermanently)
   736  	}
   737  }
   738  
   739  func TestRepositoriesService_GetArchiveLink_StatusMovedPermanently_followRedirects(t *testing.T) {
   740  	client, mux, serverURL, teardown := setup()
   741  	defer teardown()
   742  	// Mock a redirect link, which leads to an archive link
   743  	mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) {
   744  		testMethod(t, r, "GET")
   745  		redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect")
   746  		http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently)
   747  	})
   748  	mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
   749  		testMethod(t, r, "GET")
   750  		http.Redirect(w, r, "http://github.com/a", http.StatusFound)
   751  	})
   752  	ctx := context.Background()
   753  	url, resp, err := client.Repositories.GetArchiveLink(ctx, "o", "r", Tarball, &RepositoryContentGetOptions{}, 1)
   754  	if err != nil {
   755  		t.Errorf("Repositories.GetArchiveLink returned error: %v", err)
   756  	}
   757  	if resp.StatusCode != http.StatusFound {
   758  		t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusFound)
   759  	}
   760  	want := "http://github.com/a"
   761  	if url.String() != want {
   762  		t.Errorf("Repositories.GetArchiveLink returned %+v, want %+v", url.String(), want)
   763  	}
   764  }
   765  
   766  func TestRepositoriesService_GetContents_NoTrailingSlashInDirectoryApiPath(t *testing.T) {
   767  	client, mux, _, teardown := setup()
   768  	defer teardown()
   769  	mux.HandleFunc("/repos/o/r/contents/.github", func(w http.ResponseWriter, r *http.Request) {
   770  		testMethod(t, r, "GET")
   771  		query := r.URL.Query()
   772  		if query.Get("ref") != "mybranch" {
   773  			t.Errorf("Repositories.GetContents returned %+v, want %+v", query.Get("ref"), "mybranch")
   774  		}
   775  		fmt.Fprint(w, `{}`)
   776  	})
   777  	ctx := context.Background()
   778  	_, _, _, err := client.Repositories.GetContents(ctx, "o", "r", ".github/", &RepositoryContentGetOptions{
   779  		Ref: "mybranch",
   780  	})
   781  	if err != nil {
   782  		t.Fatalf("Repositories.GetContents returned error: %v", err)
   783  	}
   784  }
   785  
   786  func TestRepositoryContent_Marshal(t *testing.T) {
   787  	testJSONMarshal(t, &RepositoryContent{}, "{}")
   788  
   789  	r := &RepositoryContent{
   790  		Type:            String("type"),
   791  		Target:          String("target"),
   792  		Encoding:        String("encoding"),
   793  		Size:            Int(1),
   794  		Name:            String("name"),
   795  		Path:            String("path"),
   796  		Content:         String("content"),
   797  		SHA:             String("sha"),
   798  		URL:             String("url"),
   799  		GitURL:          String("gurl"),
   800  		HTMLURL:         String("hurl"),
   801  		DownloadURL:     String("durl"),
   802  		SubmoduleGitURL: String("smgurl"),
   803  	}
   804  
   805  	want := `{
   806  		"type": "type",
   807  		"target": "target",
   808  		"encoding": "encoding",
   809  		"size": 1,
   810  		"name": "name",
   811  		"path": "path",
   812  		"content": "content",
   813  		"sha": "sha",
   814  		"url": "url",
   815  		"git_url": "gurl",
   816  		"html_url": "hurl",
   817  		"download_url": "durl",
   818  		"submodule_git_url": "smgurl"
   819  	}`
   820  
   821  	testJSONMarshal(t, r, want)
   822  }
   823  
   824  func TestRepositoryContentResponse_Marshal(t *testing.T) {
   825  	testJSONMarshal(t, &RepositoryContentResponse{}, "{}")
   826  
   827  	r := &RepositoryContentResponse{
   828  		Content: &RepositoryContent{
   829  			Type:            String("type"),
   830  			Target:          String("target"),
   831  			Encoding:        String("encoding"),
   832  			Size:            Int(1),
   833  			Name:            String("name"),
   834  			Path:            String("path"),
   835  			Content:         String("content"),
   836  			SHA:             String("sha"),
   837  			URL:             String("url"),
   838  			GitURL:          String("gurl"),
   839  			HTMLURL:         String("hurl"),
   840  			DownloadURL:     String("durl"),
   841  			SubmoduleGitURL: String("smgurl"),
   842  		},
   843  		Commit: Commit{
   844  			SHA: String("s"),
   845  			Author: &CommitAuthor{
   846  				Date:  &Timestamp{referenceTime},
   847  				Name:  String("n"),
   848  				Email: String("e"),
   849  				Login: String("u"),
   850  			},
   851  			Committer: &CommitAuthor{
   852  				Date:  &Timestamp{referenceTime},
   853  				Name:  String("n"),
   854  				Email: String("e"),
   855  				Login: String("u"),
   856  			},
   857  			Message: String("m"),
   858  			Tree: &Tree{
   859  				SHA: String("s"),
   860  				Entries: []*TreeEntry{{
   861  					SHA:     String("s"),
   862  					Path:    String("p"),
   863  					Mode:    String("m"),
   864  					Type:    String("t"),
   865  					Size:    Int(1),
   866  					Content: String("c"),
   867  					URL:     String("u"),
   868  				}},
   869  				Truncated: Bool(false),
   870  			},
   871  			Parents: nil,
   872  			Stats: &CommitStats{
   873  				Additions: Int(1),
   874  				Deletions: Int(1),
   875  				Total:     Int(1),
   876  			},
   877  			HTMLURL: String("h"),
   878  			URL:     String("u"),
   879  			Verification: &SignatureVerification{
   880  				Verified:  Bool(false),
   881  				Reason:    String("r"),
   882  				Signature: String("s"),
   883  				Payload:   String("p"),
   884  			},
   885  			NodeID:       String("n"),
   886  			CommentCount: Int(1),
   887  		},
   888  	}
   889  
   890  	want := `{
   891  		"content": {
   892  			"type": "type",
   893  			"target": "target",
   894  			"encoding": "encoding",
   895  			"size": 1,
   896  			"name": "name",
   897  			"path": "path",
   898  			"content": "content",
   899  			"sha": "sha",
   900  			"url": "url",
   901  			"git_url": "gurl",
   902  			"html_url": "hurl",
   903  			"download_url": "durl",
   904  			"submodule_git_url": "smgurl"
   905  		},
   906  		"commit": {
   907  			"sha": "s",
   908  			"author": {
   909  				"date": ` + referenceTimeStr + `,
   910  				"name": "n",
   911  				"email": "e",
   912  				"username": "u"
   913  			},
   914  			"committer": {
   915  				"date": ` + referenceTimeStr + `,
   916  				"name": "n",
   917  				"email": "e",
   918  				"username": "u"
   919  			},
   920  			"message": "m",
   921  			"tree": {
   922  				"sha": "s",
   923  				"tree": [
   924  					{
   925  						"sha": "s",
   926  						"path": "p",
   927  						"mode": "m",
   928  						"type": "t",
   929  						"size": 1,
   930  						"content": "c",
   931  						"url": "u"
   932  					}
   933  				],
   934  				"truncated": false
   935  			},
   936  			"stats": {
   937  				"additions": 1,
   938  				"deletions": 1,
   939  				"total": 1
   940  			},
   941  			"html_url": "h",
   942  			"url": "u",
   943  			"verification": {
   944  				"verified": false,
   945  				"reason": "r",
   946  				"signature": "s",
   947  				"payload": "p"
   948  			},
   949  			"node_id": "n",
   950  			"comment_count": 1
   951  		}
   952  	}`
   953  
   954  	testJSONMarshal(t, r, want)
   955  }
   956  
   957  func TestRepositoryContentFileOptions_Marshal(t *testing.T) {
   958  	testJSONMarshal(t, &RepositoryContentFileOptions{}, "{}")
   959  
   960  	r := &RepositoryContentFileOptions{
   961  		Message: String("type"),
   962  		Content: []byte{1},
   963  		SHA:     String("type"),
   964  		Branch:  String("type"),
   965  		Author: &CommitAuthor{
   966  			Date:  &Timestamp{referenceTime},
   967  			Name:  String("name"),
   968  			Email: String("email"),
   969  			Login: String("login"),
   970  		},
   971  		Committer: &CommitAuthor{
   972  			Date:  &Timestamp{referenceTime},
   973  			Name:  String("name"),
   974  			Email: String("email"),
   975  			Login: String("login"),
   976  		},
   977  	}
   978  
   979  	want := `{
   980  		"message": "type",
   981  		"content": "AQ==",
   982  		"sha": "type",
   983  		"branch": "type",
   984  		"author": {
   985  			"date": ` + referenceTimeStr + `,
   986  			"name": "name",
   987  			"email": "email",
   988  			"username": "login"
   989  		},
   990  		"committer": {
   991  			"date": ` + referenceTimeStr + `,
   992  			"name": "name",
   993  			"email": "email",
   994  			"username": "login"
   995  		}
   996  	}`
   997  
   998  	testJSONMarshal(t, r, want)
   999  }