github.com/google/go-github/v70@v70.0.0/github/repos_commits_test.go (about)

     1  // Copyright 2013 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  	"net/http"
    12  	"net/url"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/google/go-cmp/cmp"
    18  )
    19  
    20  func TestRepositoriesService_ListCommits(t *testing.T) {
    21  	t.Parallel()
    22  	client, mux, _ := setup(t)
    23  
    24  	// given
    25  	mux.HandleFunc("/repos/o/r/commits", func(w http.ResponseWriter, r *http.Request) {
    26  		testMethod(t, r, "GET")
    27  		testFormValues(t, r,
    28  			values{
    29  				"sha":    "s",
    30  				"path":   "p",
    31  				"author": "a",
    32  				"since":  "2013-08-01T00:00:00Z",
    33  				"until":  "2013-09-03T00:00:00Z",
    34  			})
    35  		fmt.Fprintf(w, `[{"sha": "s"}]`)
    36  	})
    37  
    38  	opt := &CommitsListOptions{
    39  		SHA:    "s",
    40  		Path:   "p",
    41  		Author: "a",
    42  		Since:  time.Date(2013, time.August, 1, 0, 0, 0, 0, time.UTC),
    43  		Until:  time.Date(2013, time.September, 3, 0, 0, 0, 0, time.UTC),
    44  	}
    45  	ctx := context.Background()
    46  	commits, _, err := client.Repositories.ListCommits(ctx, "o", "r", opt)
    47  	if err != nil {
    48  		t.Errorf("Repositories.ListCommits returned error: %v", err)
    49  	}
    50  
    51  	want := []*RepositoryCommit{{SHA: Ptr("s")}}
    52  	if !cmp.Equal(commits, want) {
    53  		t.Errorf("Repositories.ListCommits returned %+v, want %+v", commits, want)
    54  	}
    55  
    56  	const methodName = "ListCommits"
    57  	testBadOptions(t, methodName, func() (err error) {
    58  		_, _, err = client.Repositories.ListCommits(ctx, "\n", "\n", opt)
    59  		return err
    60  	})
    61  
    62  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
    63  		got, resp, err := client.Repositories.ListCommits(ctx, "o", "r", opt)
    64  		if got != nil {
    65  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
    66  		}
    67  		return resp, err
    68  	})
    69  }
    70  
    71  func TestRepositoriesService_GetCommit(t *testing.T) {
    72  	t.Parallel()
    73  	client, mux, _ := setup(t)
    74  
    75  	mux.HandleFunc("/repos/o/r/commits/s", func(w http.ResponseWriter, r *http.Request) {
    76  		testMethod(t, r, "GET")
    77  		testFormValues(t, r, values{"per_page": "2", "page": "2"})
    78  		fmt.Fprintf(w, `{
    79  		  "sha": "s",
    80  		  "commit": { "message": "m" },
    81  		  "author": { "login": "l" },
    82  		  "committer": { "login": "l" },
    83  		  "parents": [ { "sha": "s" } ],
    84  		  "stats": { "additions": 104, "deletions": 4, "total": 108 },
    85  		  "files": [
    86  		    {
    87  		      "filename": "f",
    88  		      "additions": 10,
    89  		      "deletions": 2,
    90  		      "changes": 12,
    91  		      "status": "s",
    92  		      "patch": "p",
    93  		      "blob_url": "b",
    94  		      "raw_url": "r",
    95  		      "contents_url": "c"
    96  		    }
    97  		  ]
    98  		}`)
    99  	})
   100  
   101  	opts := &ListOptions{Page: 2, PerPage: 2}
   102  	ctx := context.Background()
   103  	commit, _, err := client.Repositories.GetCommit(ctx, "o", "r", "s", opts)
   104  	if err != nil {
   105  		t.Errorf("Repositories.GetCommit returned error: %v", err)
   106  	}
   107  
   108  	want := &RepositoryCommit{
   109  		SHA: Ptr("s"),
   110  		Commit: &Commit{
   111  			Message: Ptr("m"),
   112  		},
   113  		Author: &User{
   114  			Login: Ptr("l"),
   115  		},
   116  		Committer: &User{
   117  			Login: Ptr("l"),
   118  		},
   119  		Parents: []*Commit{
   120  			{
   121  				SHA: Ptr("s"),
   122  			},
   123  		},
   124  		Stats: &CommitStats{
   125  			Additions: Ptr(104),
   126  			Deletions: Ptr(4),
   127  			Total:     Ptr(108),
   128  		},
   129  		Files: []*CommitFile{
   130  			{
   131  				Filename:    Ptr("f"),
   132  				Additions:   Ptr(10),
   133  				Deletions:   Ptr(2),
   134  				Changes:     Ptr(12),
   135  				Status:      Ptr("s"),
   136  				Patch:       Ptr("p"),
   137  				BlobURL:     Ptr("b"),
   138  				RawURL:      Ptr("r"),
   139  				ContentsURL: Ptr("c"),
   140  			},
   141  		},
   142  	}
   143  	if !cmp.Equal(commit, want) {
   144  		t.Errorf("Repositories.GetCommit returned \n%+v, want \n%+v", commit, want)
   145  	}
   146  
   147  	const methodName = "GetCommit"
   148  	testBadOptions(t, methodName, func() (err error) {
   149  		_, _, err = client.Repositories.GetCommit(ctx, "\n", "\n", "\n", opts)
   150  		return err
   151  	})
   152  
   153  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   154  		got, resp, err := client.Repositories.GetCommit(ctx, "o", "r", "s", opts)
   155  		if got != nil {
   156  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   157  		}
   158  		return resp, err
   159  	})
   160  }
   161  
   162  func TestRepositoriesService_GetCommitRaw_diff(t *testing.T) {
   163  	t.Parallel()
   164  	client, mux, _ := setup(t)
   165  
   166  	const rawStr = "@@diff content"
   167  
   168  	mux.HandleFunc("/repos/o/r/commits/s", func(w http.ResponseWriter, r *http.Request) {
   169  		testMethod(t, r, "GET")
   170  		testHeader(t, r, "Accept", mediaTypeV3Diff)
   171  		fmt.Fprint(w, rawStr)
   172  	})
   173  
   174  	ctx := context.Background()
   175  	got, _, err := client.Repositories.GetCommitRaw(ctx, "o", "r", "s", RawOptions{Type: Diff})
   176  	if err != nil {
   177  		t.Fatalf("Repositories.GetCommitRaw returned error: %v", err)
   178  	}
   179  	want := rawStr
   180  	if got != want {
   181  		t.Errorf("Repositories.GetCommitRaw returned %s want %s", got, want)
   182  	}
   183  
   184  	const methodName = "GetCommitRaw"
   185  	testBadOptions(t, methodName, func() (err error) {
   186  		_, _, err = client.Repositories.GetCommitRaw(ctx, "\n", "\n", "\n", RawOptions{Type: Diff})
   187  		return err
   188  	})
   189  
   190  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   191  		got, resp, err := client.Repositories.GetCommitRaw(ctx, "o", "r", "s", RawOptions{Type: Diff})
   192  		if got != "" {
   193  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want ''", methodName, got)
   194  		}
   195  		return resp, err
   196  	})
   197  }
   198  
   199  func TestRepositoriesService_GetCommitRaw_patch(t *testing.T) {
   200  	t.Parallel()
   201  	client, mux, _ := setup(t)
   202  
   203  	const rawStr = "@@patch content"
   204  
   205  	mux.HandleFunc("/repos/o/r/commits/s", func(w http.ResponseWriter, r *http.Request) {
   206  		testMethod(t, r, "GET")
   207  		testHeader(t, r, "Accept", mediaTypeV3Patch)
   208  		fmt.Fprint(w, rawStr)
   209  	})
   210  
   211  	ctx := context.Background()
   212  	got, _, err := client.Repositories.GetCommitRaw(ctx, "o", "r", "s", RawOptions{Type: Patch})
   213  	if err != nil {
   214  		t.Fatalf("Repositories.GetCommitRaw returned error: %v", err)
   215  	}
   216  	want := rawStr
   217  	if got != want {
   218  		t.Errorf("Repositories.GetCommitRaw returned %s want %s", got, want)
   219  	}
   220  }
   221  
   222  func TestRepositoriesService_GetCommitRaw_invalid(t *testing.T) {
   223  	t.Parallel()
   224  	client, _, _ := setup(t)
   225  
   226  	ctx := context.Background()
   227  	_, _, err := client.Repositories.GetCommitRaw(ctx, "o", "r", "s", RawOptions{100})
   228  	if err == nil {
   229  		t.Fatal("Repositories.GetCommitRaw should return error")
   230  	}
   231  	if !strings.Contains(err.Error(), "unsupported raw type") {
   232  		t.Error("Repositories.GetCommitRaw should return unsupported raw type error")
   233  	}
   234  }
   235  
   236  func TestRepositoriesService_GetCommitSHA1(t *testing.T) {
   237  	t.Parallel()
   238  	client, mux, _ := setup(t)
   239  
   240  	const sha1 = "01234abcde"
   241  
   242  	mux.HandleFunc("/repos/o/r/commits/master", func(w http.ResponseWriter, r *http.Request) {
   243  		testMethod(t, r, "GET")
   244  		testHeader(t, r, "Accept", mediaTypeV3SHA)
   245  
   246  		fmt.Fprint(w, sha1)
   247  	})
   248  
   249  	ctx := context.Background()
   250  	got, _, err := client.Repositories.GetCommitSHA1(ctx, "o", "r", "master", "")
   251  	if err != nil {
   252  		t.Errorf("Repositories.GetCommitSHA1 returned error: %v", err)
   253  	}
   254  
   255  	want := sha1
   256  	if got != want {
   257  		t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want)
   258  	}
   259  
   260  	mux.HandleFunc("/repos/o/r/commits/tag", func(w http.ResponseWriter, r *http.Request) {
   261  		testMethod(t, r, "GET")
   262  		testHeader(t, r, "Accept", mediaTypeV3SHA)
   263  		testHeader(t, r, "If-None-Match", `"`+sha1+`"`)
   264  
   265  		w.WriteHeader(http.StatusNotModified)
   266  	})
   267  
   268  	got, _, err = client.Repositories.GetCommitSHA1(ctx, "o", "r", "tag", sha1)
   269  	if err == nil {
   270  		t.Errorf("Expected HTTP 304 response")
   271  	}
   272  
   273  	want = ""
   274  	if got != want {
   275  		t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want)
   276  	}
   277  
   278  	const methodName = "GetCommitSHA1"
   279  	testBadOptions(t, methodName, func() (err error) {
   280  		_, _, err = client.Repositories.GetCommitSHA1(ctx, "\n", "\n", "\n", "\n")
   281  		return err
   282  	})
   283  
   284  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   285  		got, resp, err := client.Repositories.GetCommitSHA1(ctx, "o", "r", "master", "")
   286  		if got != "" {
   287  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want ''", methodName, got)
   288  		}
   289  		return resp, err
   290  	})
   291  }
   292  
   293  func TestRepositoriesService_NonAlphabetCharacter_GetCommitSHA1(t *testing.T) {
   294  	t.Parallel()
   295  	client, mux, _ := setup(t)
   296  
   297  	const sha1 = "01234abcde"
   298  
   299  	mux.HandleFunc("/repos/o/r/commits/master%2520hash", func(w http.ResponseWriter, r *http.Request) {
   300  		testMethod(t, r, "GET")
   301  		testHeader(t, r, "Accept", mediaTypeV3SHA)
   302  
   303  		fmt.Fprint(w, sha1)
   304  	})
   305  
   306  	ctx := context.Background()
   307  	got, _, err := client.Repositories.GetCommitSHA1(ctx, "o", "r", "master%20hash", "")
   308  	if err != nil {
   309  		t.Errorf("Repositories.GetCommitSHA1 returned error: %v", err)
   310  	}
   311  
   312  	if want := sha1; got != want {
   313  		t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want)
   314  	}
   315  
   316  	mux.HandleFunc("/repos/o/r/commits/tag", func(w http.ResponseWriter, r *http.Request) {
   317  		testMethod(t, r, "GET")
   318  		testHeader(t, r, "Accept", mediaTypeV3SHA)
   319  		testHeader(t, r, "If-None-Match", `"`+sha1+`"`)
   320  
   321  		w.WriteHeader(http.StatusNotModified)
   322  	})
   323  
   324  	got, _, err = client.Repositories.GetCommitSHA1(ctx, "o", "r", "tag", sha1)
   325  	if err == nil {
   326  		t.Errorf("Expected HTTP 304 response")
   327  	}
   328  
   329  	if want := ""; got != want {
   330  		t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want)
   331  	}
   332  }
   333  
   334  func TestRepositoriesService_TrailingPercent_GetCommitSHA1(t *testing.T) {
   335  	t.Parallel()
   336  	client, mux, _ := setup(t)
   337  
   338  	const sha1 = "01234abcde"
   339  
   340  	mux.HandleFunc("/repos/o/r/commits/comm%", func(w http.ResponseWriter, r *http.Request) {
   341  		testMethod(t, r, "GET")
   342  		testHeader(t, r, "Accept", mediaTypeV3SHA)
   343  
   344  		fmt.Fprint(w, sha1)
   345  	})
   346  
   347  	ctx := context.Background()
   348  	got, _, err := client.Repositories.GetCommitSHA1(ctx, "o", "r", "comm%", "")
   349  	if err != nil {
   350  		t.Errorf("Repositories.GetCommitSHA1 returned error: %v", err)
   351  	}
   352  
   353  	if want := sha1; got != want {
   354  		t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want)
   355  	}
   356  
   357  	mux.HandleFunc("/repos/o/r/commits/tag", func(w http.ResponseWriter, r *http.Request) {
   358  		testMethod(t, r, "GET")
   359  		testHeader(t, r, "Accept", mediaTypeV3SHA)
   360  		testHeader(t, r, "If-None-Match", `"`+sha1+`"`)
   361  
   362  		w.WriteHeader(http.StatusNotModified)
   363  	})
   364  
   365  	got, _, err = client.Repositories.GetCommitSHA1(ctx, "o", "r", "tag", sha1)
   366  	if err == nil {
   367  		t.Errorf("Expected HTTP 304 response")
   368  	}
   369  
   370  	if want := ""; got != want {
   371  		t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want)
   372  	}
   373  }
   374  
   375  func TestRepositoriesService_CompareCommits(t *testing.T) {
   376  	t.Parallel()
   377  	testCases := []struct {
   378  		base string
   379  		head string
   380  	}{
   381  		{base: "b", head: "h"},
   382  		{base: "123base", head: "head123"},
   383  		{base: "`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+123base", head: "head123`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+"},
   384  	}
   385  
   386  	for i, sample := range testCases {
   387  		sample := sample
   388  		t.Run(fmt.Sprintf("case #%v", i+1), func(t *testing.T) {
   389  			t.Parallel()
   390  			client, mux, _ := setup(t)
   391  
   392  			base := sample.base
   393  			head := sample.head
   394  
   395  			encodedBase := url.PathEscape(base)
   396  			encodedHead := url.PathEscape(head)
   397  
   398  			escapedBase := url.QueryEscape(base)
   399  			escapedHead := url.QueryEscape(head)
   400  
   401  			pattern := fmt.Sprintf("/repos/o/r/compare/%v...%v", encodedBase, encodedHead)
   402  
   403  			mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
   404  				testMethod(t, r, "GET")
   405  				testFormValues(t, r, values{"per_page": "2", "page": "2"})
   406  				fmt.Fprintf(w, `{
   407    "base_commit": {
   408      "sha": "s",
   409      "commit": {
   410        "author": { "name": "n" },
   411        "committer": { "name": "n" },
   412        "message": "m",
   413        "tree": { "sha": "t" }
   414      },
   415      "author": { "login": "l" },
   416      "committer": { "login": "l" },
   417      "parents": [ { "sha": "s" } ]
   418    },
   419    "status": "s",
   420    "ahead_by": 1,
   421    "behind_by": 2,
   422    "total_commits": 1,
   423    "commits": [
   424      {
   425        "sha": "s",
   426        "commit": { "author": { "name": "n" } },
   427        "author": { "login": "l" },
   428        "committer": { "login": "l" },
   429        "parents": [ { "sha": "s" } ]
   430      }
   431    ],
   432    "files": [ { "filename": "f" } ],
   433    "html_url":      "https://github.com/o/r/compare/%[1]v...%[2]v",
   434    "permalink_url": "https://github.com/o/r/compare/o:bbcd538c8e72b8c175046e27cc8f907076331401...o:0328041d1152db8ae77652d1618a02e57f745f17",
   435    "diff_url":      "https://github.com/o/r/compare/%[1]v...%[2]v.diff",
   436    "patch_url":     "https://github.com/o/r/compare/%[1]v...%[2]v.patch",
   437    "url":           "https://api.github.com/repos/o/r/compare/%[1]v...%[2]v"
   438  }`, escapedBase, escapedHead)
   439  			})
   440  
   441  			opts := &ListOptions{Page: 2, PerPage: 2}
   442  			ctx := context.Background()
   443  			got, _, err := client.Repositories.CompareCommits(ctx, "o", "r", base, head, opts)
   444  			if err != nil {
   445  				t.Errorf("Repositories.CompareCommits returned error: %v", err)
   446  			}
   447  
   448  			want := &CommitsComparison{
   449  				BaseCommit: &RepositoryCommit{
   450  					SHA: Ptr("s"),
   451  					Commit: &Commit{
   452  						Author:    &CommitAuthor{Name: Ptr("n")},
   453  						Committer: &CommitAuthor{Name: Ptr("n")},
   454  						Message:   Ptr("m"),
   455  						Tree:      &Tree{SHA: Ptr("t")},
   456  					},
   457  					Author:    &User{Login: Ptr("l")},
   458  					Committer: &User{Login: Ptr("l")},
   459  					Parents: []*Commit{
   460  						{
   461  							SHA: Ptr("s"),
   462  						},
   463  					},
   464  				},
   465  				Status:       Ptr("s"),
   466  				AheadBy:      Ptr(1),
   467  				BehindBy:     Ptr(2),
   468  				TotalCommits: Ptr(1),
   469  				Commits: []*RepositoryCommit{
   470  					{
   471  						SHA: Ptr("s"),
   472  						Commit: &Commit{
   473  							Author: &CommitAuthor{Name: Ptr("n")},
   474  						},
   475  						Author:    &User{Login: Ptr("l")},
   476  						Committer: &User{Login: Ptr("l")},
   477  						Parents: []*Commit{
   478  							{
   479  								SHA: Ptr("s"),
   480  							},
   481  						},
   482  					},
   483  				},
   484  				Files: []*CommitFile{
   485  					{
   486  						Filename: Ptr("f"),
   487  					},
   488  				},
   489  				HTMLURL:      Ptr(fmt.Sprintf("https://github.com/o/r/compare/%v...%v", escapedBase, escapedHead)),
   490  				PermalinkURL: Ptr("https://github.com/o/r/compare/o:bbcd538c8e72b8c175046e27cc8f907076331401...o:0328041d1152db8ae77652d1618a02e57f745f17"),
   491  				DiffURL:      Ptr(fmt.Sprintf("https://github.com/o/r/compare/%v...%v.diff", escapedBase, escapedHead)),
   492  				PatchURL:     Ptr(fmt.Sprintf("https://github.com/o/r/compare/%v...%v.patch", escapedBase, escapedHead)),
   493  				URL:          Ptr(fmt.Sprintf("https://api.github.com/repos/o/r/compare/%v...%v", escapedBase, escapedHead)),
   494  			}
   495  
   496  			if !cmp.Equal(got, want) {
   497  				t.Errorf("Repositories.CompareCommits returned \n%+v, want \n%+v", got, want)
   498  			}
   499  
   500  			const methodName = "CompareCommits"
   501  			testBadOptions(t, methodName, func() (err error) {
   502  				_, _, err = client.Repositories.CompareCommits(ctx, "\n", "\n", "\n", "\n", opts)
   503  				return err
   504  			})
   505  
   506  			testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   507  				got, resp, err := client.Repositories.CompareCommits(ctx, "o", "r", base, head, opts)
   508  				if got != nil {
   509  					t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   510  				}
   511  				return resp, err
   512  			})
   513  		})
   514  	}
   515  }
   516  
   517  func TestRepositoriesService_CompareCommitsRaw_diff(t *testing.T) {
   518  	t.Parallel()
   519  	testCases := []struct {
   520  		base string
   521  		head string
   522  	}{
   523  		{base: "b", head: "h"},
   524  		{base: "123base", head: "head123"},
   525  		{base: "`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+123base", head: "head123`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+"},
   526  	}
   527  
   528  	for i, sample := range testCases {
   529  		sample := sample
   530  		t.Run(fmt.Sprintf("case #%v", i+1), func(t *testing.T) {
   531  			t.Parallel()
   532  			client, mux, _ := setup(t)
   533  
   534  			base := sample.base
   535  			head := sample.head
   536  
   537  			encodedBase := url.PathEscape(base)
   538  			encodedHead := url.PathEscape(head)
   539  
   540  			pattern := fmt.Sprintf("/repos/o/r/compare/%v...%v", encodedBase, encodedHead)
   541  			const rawStr = "@@diff content"
   542  
   543  			mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
   544  				testMethod(t, r, "GET")
   545  				testHeader(t, r, "Accept", mediaTypeV3Diff)
   546  				fmt.Fprint(w, rawStr)
   547  			})
   548  
   549  			ctx := context.Background()
   550  			got, _, err := client.Repositories.CompareCommitsRaw(ctx, "o", "r", base, head, RawOptions{Type: Diff})
   551  			if err != nil {
   552  				t.Fatalf("Repositories.GetCommitRaw returned error: %v", err)
   553  			}
   554  			want := rawStr
   555  			if got != want {
   556  				t.Errorf("Repositories.GetCommitRaw returned %s want %s", got, want)
   557  			}
   558  
   559  			const methodName = "CompareCommitsRaw"
   560  			testBadOptions(t, methodName, func() (err error) {
   561  				_, _, err = client.Repositories.CompareCommitsRaw(ctx, "\n", "\n", "\n", "\n", RawOptions{Type: Diff})
   562  				return err
   563  			})
   564  
   565  			testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   566  				got, resp, err := client.Repositories.CompareCommitsRaw(ctx, "o", "r", base, head, RawOptions{Type: Diff})
   567  				if got != "" {
   568  					t.Errorf("testNewRequestAndDoFailure %v = %#v, want ''", methodName, got)
   569  				}
   570  				return resp, err
   571  			})
   572  		})
   573  	}
   574  }
   575  
   576  func TestRepositoriesService_CompareCommitsRaw_patch(t *testing.T) {
   577  	t.Parallel()
   578  	testCases := []struct {
   579  		base string
   580  		head string
   581  	}{
   582  		{base: "b", head: "h"},
   583  		{base: "123base", head: "head123"},
   584  		{base: "`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+123base", head: "head123`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+"},
   585  	}
   586  
   587  	for i, sample := range testCases {
   588  		sample := sample
   589  		t.Run(fmt.Sprintf("case #%v", i+1), func(t *testing.T) {
   590  			t.Parallel()
   591  			client, mux, _ := setup(t)
   592  
   593  			base := sample.base
   594  			head := sample.head
   595  
   596  			encodedBase := url.PathEscape(base)
   597  			encodedHead := url.PathEscape(head)
   598  
   599  			pattern := fmt.Sprintf("/repos/o/r/compare/%v...%v", encodedBase, encodedHead)
   600  			const rawStr = "@@patch content"
   601  
   602  			mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
   603  				testMethod(t, r, "GET")
   604  				testHeader(t, r, "Accept", mediaTypeV3Patch)
   605  				fmt.Fprint(w, rawStr)
   606  			})
   607  
   608  			ctx := context.Background()
   609  			got, _, err := client.Repositories.CompareCommitsRaw(ctx, "o", "r", base, head, RawOptions{Type: Patch})
   610  			if err != nil {
   611  				t.Fatalf("Repositories.GetCommitRaw returned error: %v", err)
   612  			}
   613  			want := rawStr
   614  			if got != want {
   615  				t.Errorf("Repositories.GetCommitRaw returned %s want %s", got, want)
   616  			}
   617  		})
   618  	}
   619  }
   620  
   621  func TestRepositoriesService_CompareCommitsRaw_invalid(t *testing.T) {
   622  	t.Parallel()
   623  	ctx := context.Background()
   624  
   625  	testCases := []struct {
   626  		base string
   627  		head string
   628  	}{
   629  		{base: "b", head: "h"},
   630  		{base: "123base", head: "head123"},
   631  		{base: "`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+123base", head: "head123`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+"},
   632  	}
   633  
   634  	for i, sample := range testCases {
   635  		sample := sample
   636  		t.Run(fmt.Sprintf("case #%v", i+1), func(t *testing.T) {
   637  			t.Parallel()
   638  			client, _, _ := setup(t)
   639  			_, _, err := client.Repositories.CompareCommitsRaw(ctx, "o", "r", sample.base, sample.head, RawOptions{100})
   640  			if err == nil {
   641  				t.Fatal("Repositories.GetCommitRaw should return error")
   642  			}
   643  			if !strings.Contains(err.Error(), "unsupported raw type") {
   644  				t.Error("Repositories.GetCommitRaw should return unsupported raw type error")
   645  			}
   646  		})
   647  	}
   648  }
   649  
   650  func TestRepositoriesService_ListBranchesHeadCommit(t *testing.T) {
   651  	t.Parallel()
   652  	client, mux, _ := setup(t)
   653  
   654  	mux.HandleFunc("/repos/o/r/commits/s/branches-where-head", func(w http.ResponseWriter, r *http.Request) {
   655  		testMethod(t, r, "GET")
   656  		fmt.Fprintf(w, `[{"name": "b","commit":{"sha":"2e90302801c870f17b6152327d9b9a03c8eca0e2","url":"https://api.github.com/repos/google/go-github/commits/2e90302801c870f17b6152327d9b9a03c8eca0e2"},"protected":true}]`)
   657  	})
   658  
   659  	ctx := context.Background()
   660  	branches, _, err := client.Repositories.ListBranchesHeadCommit(ctx, "o", "r", "s")
   661  	if err != nil {
   662  		t.Errorf("Repositories.ListBranchesHeadCommit returned error: %v", err)
   663  	}
   664  
   665  	want := []*BranchCommit{
   666  		{
   667  			Name: Ptr("b"),
   668  			Commit: &Commit{
   669  				SHA: Ptr("2e90302801c870f17b6152327d9b9a03c8eca0e2"),
   670  				URL: Ptr("https://api.github.com/repos/google/go-github/commits/2e90302801c870f17b6152327d9b9a03c8eca0e2"),
   671  			},
   672  			Protected: Ptr(true),
   673  		},
   674  	}
   675  	if !cmp.Equal(branches, want) {
   676  		t.Errorf("Repositories.ListBranchesHeadCommit returned %+v, want %+v", branches, want)
   677  	}
   678  
   679  	const methodName = "ListBranchesHeadCommit"
   680  	testBadOptions(t, methodName, func() (err error) {
   681  		_, _, err = client.Repositories.ListBranchesHeadCommit(ctx, "\n", "\n", "\n")
   682  		return err
   683  	})
   684  
   685  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   686  		got, resp, err := client.Repositories.ListBranchesHeadCommit(ctx, "o", "r", "s")
   687  		if got != nil {
   688  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   689  		}
   690  		return resp, err
   691  	})
   692  }
   693  
   694  func TestBranchCommit_Marshal(t *testing.T) {
   695  	t.Parallel()
   696  	testJSONMarshal(t, &BranchCommit{}, "{}")
   697  
   698  	r := &BranchCommit{
   699  		Name: Ptr("n"),
   700  		Commit: &Commit{
   701  			SHA: Ptr("s"),
   702  			Author: &CommitAuthor{
   703  				Date:  &Timestamp{referenceTime},
   704  				Name:  Ptr("n"),
   705  				Email: Ptr("e"),
   706  				Login: Ptr("u"),
   707  			},
   708  			Committer: &CommitAuthor{
   709  				Date:  &Timestamp{referenceTime},
   710  				Name:  Ptr("n"),
   711  				Email: Ptr("e"),
   712  				Login: Ptr("u"),
   713  			},
   714  			Message: Ptr("m"),
   715  			Tree: &Tree{
   716  				SHA: Ptr("s"),
   717  				Entries: []*TreeEntry{{
   718  					SHA:     Ptr("s"),
   719  					Path:    Ptr("p"),
   720  					Mode:    Ptr("m"),
   721  					Type:    Ptr("t"),
   722  					Size:    Ptr(1),
   723  					Content: Ptr("c"),
   724  					URL:     Ptr("u"),
   725  				}},
   726  				Truncated: Ptr(false),
   727  			},
   728  			Parents: nil,
   729  			HTMLURL: Ptr("h"),
   730  			URL:     Ptr("u"),
   731  			Verification: &SignatureVerification{
   732  				Verified:  Ptr(false),
   733  				Reason:    Ptr("r"),
   734  				Signature: Ptr("s"),
   735  				Payload:   Ptr("p"),
   736  			},
   737  			NodeID:       Ptr("n"),
   738  			CommentCount: Ptr(1),
   739  		},
   740  		Protected: Ptr(false),
   741  	}
   742  
   743  	want := `{
   744  		"name": "n",
   745  		"commit": {
   746  			"sha": "s",
   747  			"author": {
   748  				"date": ` + referenceTimeStr + `,
   749  				"name": "n",
   750  				"email": "e",
   751  				"username": "u"
   752  			},
   753  			"committer": {
   754  				"date": ` + referenceTimeStr + `,
   755  				"name": "n",
   756  				"email": "e",
   757  				"username": "u"
   758  			},
   759  			"message": "m",
   760  			"tree": {
   761  				"sha": "s",
   762  				"tree": [
   763  					{
   764  						"sha": "s",
   765  						"path": "p",
   766  						"mode": "m",
   767  						"type": "t",
   768  						"size": 1,
   769  						"content": "c",
   770  						"url": "u"
   771  					}
   772  				],
   773  				"truncated": false
   774  			},
   775  			"html_url": "h",
   776  			"url": "u",
   777  			"verification": {
   778  				"verified": false,
   779  				"reason": "r",
   780  				"signature": "s",
   781  				"payload": "p"
   782  			},
   783  			"node_id": "n",
   784  			"comment_count": 1
   785  		},
   786  		"protected": false
   787  	}`
   788  
   789  	testJSONMarshal(t, r, want)
   790  }
   791  
   792  func TestCommitsComparison_Marshal(t *testing.T) {
   793  	t.Parallel()
   794  	testJSONMarshal(t, &CommitsComparison{}, "{}")
   795  
   796  	r := &CommitsComparison{
   797  		BaseCommit:      &RepositoryCommit{NodeID: Ptr("nid")},
   798  		MergeBaseCommit: &RepositoryCommit{NodeID: Ptr("nid")},
   799  		Status:          Ptr("status"),
   800  		AheadBy:         Ptr(1),
   801  		BehindBy:        Ptr(1),
   802  		TotalCommits:    Ptr(1),
   803  		Commits: []*RepositoryCommit{
   804  			{
   805  				NodeID: Ptr("nid"),
   806  			},
   807  		},
   808  		Files: []*CommitFile{
   809  			{
   810  				SHA: Ptr("sha"),
   811  			},
   812  		},
   813  		HTMLURL:      Ptr("hurl"),
   814  		PermalinkURL: Ptr("purl"),
   815  		DiffURL:      Ptr("durl"),
   816  		PatchURL:     Ptr("purl"),
   817  		URL:          Ptr("url"),
   818  	}
   819  
   820  	want := `{
   821  		"base_commit": {
   822  			"node_id": "nid"
   823  		},
   824  		"merge_base_commit": {
   825  			"node_id": "nid"
   826  		},
   827  		"status": "status",
   828  		"ahead_by": 1,
   829  		"behind_by": 1,
   830  		"total_commits": 1,
   831  		"commits": [
   832  			{
   833  				"node_id": "nid"
   834  			}
   835  		],
   836  		"files": [
   837  			{
   838  				"sha": "sha"
   839  			}
   840  		],
   841  		"html_url": "hurl",
   842  		"permalink_url": "purl",
   843  		"diff_url": "durl",
   844  		"patch_url": "purl",
   845  		"url": "url"
   846  	}`
   847  
   848  	testJSONMarshal(t, r, want)
   849  }
   850  
   851  func TestCommitFile_Marshal(t *testing.T) {
   852  	t.Parallel()
   853  	testJSONMarshal(t, &CommitFile{}, "{}")
   854  
   855  	r := &CommitFile{
   856  		SHA:              Ptr("sha"),
   857  		Filename:         Ptr("fn"),
   858  		Additions:        Ptr(1),
   859  		Deletions:        Ptr(1),
   860  		Changes:          Ptr(1),
   861  		Status:           Ptr("status"),
   862  		Patch:            Ptr("patch"),
   863  		BlobURL:          Ptr("burl"),
   864  		RawURL:           Ptr("rurl"),
   865  		ContentsURL:      Ptr("curl"),
   866  		PreviousFilename: Ptr("pf"),
   867  	}
   868  
   869  	want := `{
   870  		"sha": "sha",
   871  		"filename": "fn",
   872  		"additions": 1,
   873  		"deletions": 1,
   874  		"changes": 1,
   875  		"status": "status",
   876  		"patch": "patch",
   877  		"blob_url": "burl",
   878  		"raw_url": "rurl",
   879  		"contents_url": "curl",
   880  		"previous_filename": "pf"
   881  	}`
   882  
   883  	testJSONMarshal(t, r, want)
   884  }
   885  
   886  func TestCommitStats_Marshal(t *testing.T) {
   887  	t.Parallel()
   888  	testJSONMarshal(t, &CommitStats{}, "{}")
   889  
   890  	r := &CommitStats{
   891  		Additions: Ptr(1),
   892  		Deletions: Ptr(1),
   893  		Total:     Ptr(1),
   894  	}
   895  
   896  	want := `{
   897  		"additions": 1,
   898  		"deletions": 1,
   899  		"total": 1
   900  	}`
   901  
   902  	testJSONMarshal(t, r, want)
   903  }
   904  
   905  func TestRepositoryCommit_Marshal(t *testing.T) {
   906  	t.Parallel()
   907  	testJSONMarshal(t, &RepositoryCommit{}, "{}")
   908  
   909  	r := &RepositoryCommit{
   910  		NodeID: Ptr("nid"),
   911  		SHA:    Ptr("sha"),
   912  		Commit: &Commit{
   913  			Message: Ptr("m"),
   914  		},
   915  		Author: &User{
   916  			Login: Ptr("l"),
   917  		},
   918  		Committer: &User{
   919  			Login: Ptr("l"),
   920  		},
   921  		Parents: []*Commit{
   922  			{
   923  				SHA: Ptr("s"),
   924  			},
   925  		},
   926  		HTMLURL:     Ptr("hurl"),
   927  		URL:         Ptr("url"),
   928  		CommentsURL: Ptr("curl"),
   929  		Stats: &CommitStats{
   930  			Additions: Ptr(104),
   931  			Deletions: Ptr(4),
   932  			Total:     Ptr(108),
   933  		},
   934  		Files: []*CommitFile{
   935  			{
   936  				Filename:    Ptr("f"),
   937  				Additions:   Ptr(10),
   938  				Deletions:   Ptr(2),
   939  				Changes:     Ptr(12),
   940  				Status:      Ptr("s"),
   941  				Patch:       Ptr("p"),
   942  				BlobURL:     Ptr("b"),
   943  				RawURL:      Ptr("r"),
   944  				ContentsURL: Ptr("c"),
   945  			},
   946  		},
   947  	}
   948  
   949  	want := `{
   950  		"node_id": "nid",
   951  		"sha": "sha",
   952  		"commit": {
   953  			"message": "m"
   954  		},
   955  		"author": {
   956  			"login": "l"
   957  		},
   958  		"committer": {
   959  			"login": "l"
   960  		},
   961  		"parents": [
   962  			{
   963  				"sha": "s"
   964  			}
   965  		],
   966  		"html_url": "hurl",
   967  		"url": "url",
   968  		"comments_url": "curl",
   969  		"stats": {
   970  			"additions": 104,
   971  			"deletions": 4,
   972  			"total": 108
   973  		},
   974  		"files": [
   975  			{
   976  				"filename": "f",
   977  				"additions": 10,
   978  				"deletions": 2,
   979  				"changes": 12,
   980  				"status": "s",
   981  				"patch": "p",
   982  				"blob_url": "b",
   983  				"raw_url": "r",
   984  				"contents_url": "c"
   985  			}
   986  		]
   987  	}`
   988  
   989  	testJSONMarshal(t, r, want)
   990  }