github.com/google/go-github/v68@v68.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%20hash", 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  			escapedBase := url.QueryEscape(base)
   395  			escapedHead := url.QueryEscape(head)
   396  
   397  			pattern := fmt.Sprintf("/repos/o/r/compare/%v...%v", base, head)
   398  
   399  			mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
   400  				testMethod(t, r, "GET")
   401  				testFormValues(t, r, values{"per_page": "2", "page": "2"})
   402  				fmt.Fprintf(w, `{
   403    "base_commit": {
   404      "sha": "s",
   405      "commit": {
   406        "author": { "name": "n" },
   407        "committer": { "name": "n" },
   408        "message": "m",
   409        "tree": { "sha": "t" }
   410      },
   411      "author": { "login": "l" },
   412      "committer": { "login": "l" },
   413      "parents": [ { "sha": "s" } ]
   414    },
   415    "status": "s",
   416    "ahead_by": 1,
   417    "behind_by": 2,
   418    "total_commits": 1,
   419    "commits": [
   420      {
   421        "sha": "s",
   422        "commit": { "author": { "name": "n" } },
   423        "author": { "login": "l" },
   424        "committer": { "login": "l" },
   425        "parents": [ { "sha": "s" } ]
   426      }
   427    ],
   428    "files": [ { "filename": "f" } ],
   429    "html_url":      "https://github.com/o/r/compare/%[1]v...%[2]v",
   430    "permalink_url": "https://github.com/o/r/compare/o:bbcd538c8e72b8c175046e27cc8f907076331401...o:0328041d1152db8ae77652d1618a02e57f745f17",
   431    "diff_url":      "https://github.com/o/r/compare/%[1]v...%[2]v.diff",
   432    "patch_url":     "https://github.com/o/r/compare/%[1]v...%[2]v.patch",
   433    "url":           "https://api.github.com/repos/o/r/compare/%[1]v...%[2]v"
   434  }`, escapedBase, escapedHead)
   435  			})
   436  
   437  			opts := &ListOptions{Page: 2, PerPage: 2}
   438  			ctx := context.Background()
   439  			got, _, err := client.Repositories.CompareCommits(ctx, "o", "r", base, head, opts)
   440  			if err != nil {
   441  				t.Errorf("Repositories.CompareCommits returned error: %v", err)
   442  			}
   443  
   444  			want := &CommitsComparison{
   445  				BaseCommit: &RepositoryCommit{
   446  					SHA: Ptr("s"),
   447  					Commit: &Commit{
   448  						Author:    &CommitAuthor{Name: Ptr("n")},
   449  						Committer: &CommitAuthor{Name: Ptr("n")},
   450  						Message:   Ptr("m"),
   451  						Tree:      &Tree{SHA: Ptr("t")},
   452  					},
   453  					Author:    &User{Login: Ptr("l")},
   454  					Committer: &User{Login: Ptr("l")},
   455  					Parents: []*Commit{
   456  						{
   457  							SHA: Ptr("s"),
   458  						},
   459  					},
   460  				},
   461  				Status:       Ptr("s"),
   462  				AheadBy:      Ptr(1),
   463  				BehindBy:     Ptr(2),
   464  				TotalCommits: Ptr(1),
   465  				Commits: []*RepositoryCommit{
   466  					{
   467  						SHA: Ptr("s"),
   468  						Commit: &Commit{
   469  							Author: &CommitAuthor{Name: Ptr("n")},
   470  						},
   471  						Author:    &User{Login: Ptr("l")},
   472  						Committer: &User{Login: Ptr("l")},
   473  						Parents: []*Commit{
   474  							{
   475  								SHA: Ptr("s"),
   476  							},
   477  						},
   478  					},
   479  				},
   480  				Files: []*CommitFile{
   481  					{
   482  						Filename: Ptr("f"),
   483  					},
   484  				},
   485  				HTMLURL:      Ptr(fmt.Sprintf("https://github.com/o/r/compare/%v...%v", escapedBase, escapedHead)),
   486  				PermalinkURL: Ptr("https://github.com/o/r/compare/o:bbcd538c8e72b8c175046e27cc8f907076331401...o:0328041d1152db8ae77652d1618a02e57f745f17"),
   487  				DiffURL:      Ptr(fmt.Sprintf("https://github.com/o/r/compare/%v...%v.diff", escapedBase, escapedHead)),
   488  				PatchURL:     Ptr(fmt.Sprintf("https://github.com/o/r/compare/%v...%v.patch", escapedBase, escapedHead)),
   489  				URL:          Ptr(fmt.Sprintf("https://api.github.com/repos/o/r/compare/%v...%v", escapedBase, escapedHead)),
   490  			}
   491  
   492  			if !cmp.Equal(got, want) {
   493  				t.Errorf("Repositories.CompareCommits returned \n%+v, want \n%+v", got, want)
   494  			}
   495  
   496  			const methodName = "CompareCommits"
   497  			testBadOptions(t, methodName, func() (err error) {
   498  				_, _, err = client.Repositories.CompareCommits(ctx, "\n", "\n", "\n", "\n", opts)
   499  				return err
   500  			})
   501  
   502  			testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   503  				got, resp, err := client.Repositories.CompareCommits(ctx, "o", "r", base, head, opts)
   504  				if got != nil {
   505  					t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   506  				}
   507  				return resp, err
   508  			})
   509  		})
   510  	}
   511  }
   512  
   513  func TestRepositoriesService_CompareCommitsRaw_diff(t *testing.T) {
   514  	t.Parallel()
   515  	testCases := []struct {
   516  		base string
   517  		head string
   518  	}{
   519  		{base: "b", head: "h"},
   520  		{base: "123base", head: "head123"},
   521  		{base: "`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+123base", head: "head123`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+"},
   522  	}
   523  
   524  	for i, sample := range testCases {
   525  		sample := sample
   526  		t.Run(fmt.Sprintf("case #%v", i+1), func(t *testing.T) {
   527  			t.Parallel()
   528  			client, mux, _ := setup(t)
   529  
   530  			base := sample.base
   531  			head := sample.head
   532  			pattern := fmt.Sprintf("/repos/o/r/compare/%v...%v", base, head)
   533  			const rawStr = "@@diff content"
   534  
   535  			mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
   536  				testMethod(t, r, "GET")
   537  				testHeader(t, r, "Accept", mediaTypeV3Diff)
   538  				fmt.Fprint(w, rawStr)
   539  			})
   540  
   541  			ctx := context.Background()
   542  			got, _, err := client.Repositories.CompareCommitsRaw(ctx, "o", "r", base, head, RawOptions{Type: Diff})
   543  			if err != nil {
   544  				t.Fatalf("Repositories.GetCommitRaw returned error: %v", err)
   545  			}
   546  			want := rawStr
   547  			if got != want {
   548  				t.Errorf("Repositories.GetCommitRaw returned %s want %s", got, want)
   549  			}
   550  
   551  			const methodName = "CompareCommitsRaw"
   552  			testBadOptions(t, methodName, func() (err error) {
   553  				_, _, err = client.Repositories.CompareCommitsRaw(ctx, "\n", "\n", "\n", "\n", RawOptions{Type: Diff})
   554  				return err
   555  			})
   556  
   557  			testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   558  				got, resp, err := client.Repositories.CompareCommitsRaw(ctx, "o", "r", base, head, RawOptions{Type: Diff})
   559  				if got != "" {
   560  					t.Errorf("testNewRequestAndDoFailure %v = %#v, want ''", methodName, got)
   561  				}
   562  				return resp, err
   563  			})
   564  		})
   565  	}
   566  }
   567  
   568  func TestRepositoriesService_CompareCommitsRaw_patch(t *testing.T) {
   569  	t.Parallel()
   570  	testCases := []struct {
   571  		base string
   572  		head string
   573  	}{
   574  		{base: "b", head: "h"},
   575  		{base: "123base", head: "head123"},
   576  		{base: "`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+123base", head: "head123`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+"},
   577  	}
   578  
   579  	for i, sample := range testCases {
   580  		sample := sample
   581  		t.Run(fmt.Sprintf("case #%v", i+1), func(t *testing.T) {
   582  			t.Parallel()
   583  			client, mux, _ := setup(t)
   584  
   585  			base := sample.base
   586  			head := sample.head
   587  			pattern := fmt.Sprintf("/repos/o/r/compare/%v...%v", base, head)
   588  			const rawStr = "@@patch content"
   589  
   590  			mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
   591  				testMethod(t, r, "GET")
   592  				testHeader(t, r, "Accept", mediaTypeV3Patch)
   593  				fmt.Fprint(w, rawStr)
   594  			})
   595  
   596  			ctx := context.Background()
   597  			got, _, err := client.Repositories.CompareCommitsRaw(ctx, "o", "r", base, head, RawOptions{Type: Patch})
   598  			if err != nil {
   599  				t.Fatalf("Repositories.GetCommitRaw returned error: %v", err)
   600  			}
   601  			want := rawStr
   602  			if got != want {
   603  				t.Errorf("Repositories.GetCommitRaw returned %s want %s", got, want)
   604  			}
   605  		})
   606  	}
   607  }
   608  
   609  func TestRepositoriesService_CompareCommitsRaw_invalid(t *testing.T) {
   610  	t.Parallel()
   611  	ctx := context.Background()
   612  
   613  	testCases := []struct {
   614  		base string
   615  		head string
   616  	}{
   617  		{base: "b", head: "h"},
   618  		{base: "123base", head: "head123"},
   619  		{base: "`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+123base", head: "head123`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+"},
   620  	}
   621  
   622  	for i, sample := range testCases {
   623  		sample := sample
   624  		t.Run(fmt.Sprintf("case #%v", i+1), func(t *testing.T) {
   625  			t.Parallel()
   626  			client, _, _ := setup(t)
   627  			_, _, err := client.Repositories.CompareCommitsRaw(ctx, "o", "r", sample.base, sample.head, RawOptions{100})
   628  			if err == nil {
   629  				t.Fatal("Repositories.GetCommitRaw should return error")
   630  			}
   631  			if !strings.Contains(err.Error(), "unsupported raw type") {
   632  				t.Error("Repositories.GetCommitRaw should return unsupported raw type error")
   633  			}
   634  		})
   635  	}
   636  }
   637  
   638  func TestRepositoriesService_ListBranchesHeadCommit(t *testing.T) {
   639  	t.Parallel()
   640  	client, mux, _ := setup(t)
   641  
   642  	mux.HandleFunc("/repos/o/r/commits/s/branches-where-head", func(w http.ResponseWriter, r *http.Request) {
   643  		testMethod(t, r, "GET")
   644  		fmt.Fprintf(w, `[{"name": "b","commit":{"sha":"2e90302801c870f17b6152327d9b9a03c8eca0e2","url":"https://api.github.com/repos/google/go-github/commits/2e90302801c870f17b6152327d9b9a03c8eca0e2"},"protected":true}]`)
   645  	})
   646  
   647  	ctx := context.Background()
   648  	branches, _, err := client.Repositories.ListBranchesHeadCommit(ctx, "o", "r", "s")
   649  	if err != nil {
   650  		t.Errorf("Repositories.ListBranchesHeadCommit returned error: %v", err)
   651  	}
   652  
   653  	want := []*BranchCommit{
   654  		{
   655  			Name: Ptr("b"),
   656  			Commit: &Commit{
   657  				SHA: Ptr("2e90302801c870f17b6152327d9b9a03c8eca0e2"),
   658  				URL: Ptr("https://api.github.com/repos/google/go-github/commits/2e90302801c870f17b6152327d9b9a03c8eca0e2"),
   659  			},
   660  			Protected: Ptr(true),
   661  		},
   662  	}
   663  	if !cmp.Equal(branches, want) {
   664  		t.Errorf("Repositories.ListBranchesHeadCommit returned %+v, want %+v", branches, want)
   665  	}
   666  
   667  	const methodName = "ListBranchesHeadCommit"
   668  	testBadOptions(t, methodName, func() (err error) {
   669  		_, _, err = client.Repositories.ListBranchesHeadCommit(ctx, "\n", "\n", "\n")
   670  		return err
   671  	})
   672  
   673  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   674  		got, resp, err := client.Repositories.ListBranchesHeadCommit(ctx, "o", "r", "s")
   675  		if got != nil {
   676  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   677  		}
   678  		return resp, err
   679  	})
   680  }
   681  
   682  func TestBranchCommit_Marshal(t *testing.T) {
   683  	t.Parallel()
   684  	testJSONMarshal(t, &BranchCommit{}, "{}")
   685  
   686  	r := &BranchCommit{
   687  		Name: Ptr("n"),
   688  		Commit: &Commit{
   689  			SHA: Ptr("s"),
   690  			Author: &CommitAuthor{
   691  				Date:  &Timestamp{referenceTime},
   692  				Name:  Ptr("n"),
   693  				Email: Ptr("e"),
   694  				Login: Ptr("u"),
   695  			},
   696  			Committer: &CommitAuthor{
   697  				Date:  &Timestamp{referenceTime},
   698  				Name:  Ptr("n"),
   699  				Email: Ptr("e"),
   700  				Login: Ptr("u"),
   701  			},
   702  			Message: Ptr("m"),
   703  			Tree: &Tree{
   704  				SHA: Ptr("s"),
   705  				Entries: []*TreeEntry{{
   706  					SHA:     Ptr("s"),
   707  					Path:    Ptr("p"),
   708  					Mode:    Ptr("m"),
   709  					Type:    Ptr("t"),
   710  					Size:    Ptr(1),
   711  					Content: Ptr("c"),
   712  					URL:     Ptr("u"),
   713  				}},
   714  				Truncated: Ptr(false),
   715  			},
   716  			Parents: nil,
   717  			HTMLURL: Ptr("h"),
   718  			URL:     Ptr("u"),
   719  			Verification: &SignatureVerification{
   720  				Verified:  Ptr(false),
   721  				Reason:    Ptr("r"),
   722  				Signature: Ptr("s"),
   723  				Payload:   Ptr("p"),
   724  			},
   725  			NodeID:       Ptr("n"),
   726  			CommentCount: Ptr(1),
   727  		},
   728  		Protected: Ptr(false),
   729  	}
   730  
   731  	want := `{
   732  		"name": "n",
   733  		"commit": {
   734  			"sha": "s",
   735  			"author": {
   736  				"date": ` + referenceTimeStr + `,
   737  				"name": "n",
   738  				"email": "e",
   739  				"username": "u"
   740  			},
   741  			"committer": {
   742  				"date": ` + referenceTimeStr + `,
   743  				"name": "n",
   744  				"email": "e",
   745  				"username": "u"
   746  			},
   747  			"message": "m",
   748  			"tree": {
   749  				"sha": "s",
   750  				"tree": [
   751  					{
   752  						"sha": "s",
   753  						"path": "p",
   754  						"mode": "m",
   755  						"type": "t",
   756  						"size": 1,
   757  						"content": "c",
   758  						"url": "u"
   759  					}
   760  				],
   761  				"truncated": false
   762  			},
   763  			"html_url": "h",
   764  			"url": "u",
   765  			"verification": {
   766  				"verified": false,
   767  				"reason": "r",
   768  				"signature": "s",
   769  				"payload": "p"
   770  			},
   771  			"node_id": "n",
   772  			"comment_count": 1
   773  		},
   774  		"protected": false
   775  	}`
   776  
   777  	testJSONMarshal(t, r, want)
   778  }
   779  
   780  func TestCommitsComparison_Marshal(t *testing.T) {
   781  	t.Parallel()
   782  	testJSONMarshal(t, &CommitsComparison{}, "{}")
   783  
   784  	r := &CommitsComparison{
   785  		BaseCommit:      &RepositoryCommit{NodeID: Ptr("nid")},
   786  		MergeBaseCommit: &RepositoryCommit{NodeID: Ptr("nid")},
   787  		Status:          Ptr("status"),
   788  		AheadBy:         Ptr(1),
   789  		BehindBy:        Ptr(1),
   790  		TotalCommits:    Ptr(1),
   791  		Commits: []*RepositoryCommit{
   792  			{
   793  				NodeID: Ptr("nid"),
   794  			},
   795  		},
   796  		Files: []*CommitFile{
   797  			{
   798  				SHA: Ptr("sha"),
   799  			},
   800  		},
   801  		HTMLURL:      Ptr("hurl"),
   802  		PermalinkURL: Ptr("purl"),
   803  		DiffURL:      Ptr("durl"),
   804  		PatchURL:     Ptr("purl"),
   805  		URL:          Ptr("url"),
   806  	}
   807  
   808  	want := `{
   809  		"base_commit": {
   810  			"node_id": "nid"
   811  		},
   812  		"merge_base_commit": {
   813  			"node_id": "nid"
   814  		},
   815  		"status": "status",
   816  		"ahead_by": 1,
   817  		"behind_by": 1,
   818  		"total_commits": 1,
   819  		"commits": [
   820  			{
   821  				"node_id": "nid"
   822  			}
   823  		],
   824  		"files": [
   825  			{
   826  				"sha": "sha"
   827  			}
   828  		],
   829  		"html_url": "hurl",
   830  		"permalink_url": "purl",
   831  		"diff_url": "durl",
   832  		"patch_url": "purl",
   833  		"url": "url"
   834  	}`
   835  
   836  	testJSONMarshal(t, r, want)
   837  }
   838  
   839  func TestCommitFile_Marshal(t *testing.T) {
   840  	t.Parallel()
   841  	testJSONMarshal(t, &CommitFile{}, "{}")
   842  
   843  	r := &CommitFile{
   844  		SHA:              Ptr("sha"),
   845  		Filename:         Ptr("fn"),
   846  		Additions:        Ptr(1),
   847  		Deletions:        Ptr(1),
   848  		Changes:          Ptr(1),
   849  		Status:           Ptr("status"),
   850  		Patch:            Ptr("patch"),
   851  		BlobURL:          Ptr("burl"),
   852  		RawURL:           Ptr("rurl"),
   853  		ContentsURL:      Ptr("curl"),
   854  		PreviousFilename: Ptr("pf"),
   855  	}
   856  
   857  	want := `{
   858  		"sha": "sha",
   859  		"filename": "fn",
   860  		"additions": 1,
   861  		"deletions": 1,
   862  		"changes": 1,
   863  		"status": "status",
   864  		"patch": "patch",
   865  		"blob_url": "burl",
   866  		"raw_url": "rurl",
   867  		"contents_url": "curl",
   868  		"previous_filename": "pf"
   869  	}`
   870  
   871  	testJSONMarshal(t, r, want)
   872  }
   873  
   874  func TestCommitStats_Marshal(t *testing.T) {
   875  	t.Parallel()
   876  	testJSONMarshal(t, &CommitStats{}, "{}")
   877  
   878  	r := &CommitStats{
   879  		Additions: Ptr(1),
   880  		Deletions: Ptr(1),
   881  		Total:     Ptr(1),
   882  	}
   883  
   884  	want := `{
   885  		"additions": 1,
   886  		"deletions": 1,
   887  		"total": 1
   888  	}`
   889  
   890  	testJSONMarshal(t, r, want)
   891  }
   892  
   893  func TestRepositoryCommit_Marshal(t *testing.T) {
   894  	t.Parallel()
   895  	testJSONMarshal(t, &RepositoryCommit{}, "{}")
   896  
   897  	r := &RepositoryCommit{
   898  		NodeID: Ptr("nid"),
   899  		SHA:    Ptr("sha"),
   900  		Commit: &Commit{
   901  			Message: Ptr("m"),
   902  		},
   903  		Author: &User{
   904  			Login: Ptr("l"),
   905  		},
   906  		Committer: &User{
   907  			Login: Ptr("l"),
   908  		},
   909  		Parents: []*Commit{
   910  			{
   911  				SHA: Ptr("s"),
   912  			},
   913  		},
   914  		HTMLURL:     Ptr("hurl"),
   915  		URL:         Ptr("url"),
   916  		CommentsURL: Ptr("curl"),
   917  		Stats: &CommitStats{
   918  			Additions: Ptr(104),
   919  			Deletions: Ptr(4),
   920  			Total:     Ptr(108),
   921  		},
   922  		Files: []*CommitFile{
   923  			{
   924  				Filename:    Ptr("f"),
   925  				Additions:   Ptr(10),
   926  				Deletions:   Ptr(2),
   927  				Changes:     Ptr(12),
   928  				Status:      Ptr("s"),
   929  				Patch:       Ptr("p"),
   930  				BlobURL:     Ptr("b"),
   931  				RawURL:      Ptr("r"),
   932  				ContentsURL: Ptr("c"),
   933  			},
   934  		},
   935  	}
   936  
   937  	want := `{
   938  		"node_id": "nid",
   939  		"sha": "sha",
   940  		"commit": {
   941  			"message": "m"
   942  		},
   943  		"author": {
   944  			"login": "l"
   945  		},
   946  		"committer": {
   947  			"login": "l"
   948  		},
   949  		"parents": [
   950  			{
   951  				"sha": "s"
   952  			}
   953  		],
   954  		"html_url": "hurl",
   955  		"url": "url",
   956  		"comments_url": "curl",
   957  		"stats": {
   958  			"additions": 104,
   959  			"deletions": 4,
   960  			"total": 108
   961  		},
   962  		"files": [
   963  			{
   964  				"filename": "f",
   965  				"additions": 10,
   966  				"deletions": 2,
   967  				"changes": 12,
   968  				"status": "s",
   969  				"patch": "p",
   970  				"blob_url": "b",
   971  				"raw_url": "r",
   972  				"contents_url": "c"
   973  			}
   974  		]
   975  	}`
   976  
   977  	testJSONMarshal(t, r, want)
   978  }