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