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