Repository: google/go-github
Pull request #1816 (2021-03-02 21:38:22)

Support empty string as a commit message when merging a pull request
Fixes https://github.com/google/go-github/issues/1815

- changes the CommitMessage field in the request to merge a PR to a pointer
- adds a new boolean flag to the PullRequestOptions struct, which will make the commit message null if set and the given message is ""

google-cla[bot]: 
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project (if not, look below for help). Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

:memo: **Please visit <https://cla.developers.google.com/> to sign.**

Once you've signed (or fixed any issues), please reply here with `@googlebot I signed it!` and we'll verify it.

----

#### What to do if you already signed the CLA

##### Individual signers

*   It's possible we don't have your GitHub username or you're using a different email address on your commit. Check [your existing CLA data](https://cla.developers.google.com/clas) and verify that your [email is set on your git commits](https://help.github.com/articles/setting-your-email-in-git/).

##### Corporate signers

*   Your company has a Point of Contact who decides which employees are authorized to participate. Ask your POC to be added to the group of authorized contributors. If you don't know who your Point of Contact is, direct the Google project maintainer to [go/cla#troubleshoot](http://go/cla#troubleshoot) ([Public version](https://opensource.google/docs/cla/#troubleshoot)).
*   The email used to register you as an authorized contributor must be the email used for the Git commit. Check [your existing CLA data](https://cla.developers.google.com/clas) and verify that your [email is set on your git commits](https://help.github.com/articles/setting-your-email-in-git/).
*   The email used to register you as an authorized contributor must also be [attached to your GitHub account](https://github.com/settings/emails).
		

ℹ️ **Googlers: [Go here](https://goto.google.com/prinfo/https%3A%2F%2Fgithub.com%2Fgoogle%2Fgo-github%2Fpull%2F1816) for more info**.

<!-- need_sender_cla -->
john-m-liu: @googlebot I signed it!
codecov[bot]: # [Codecov](https://codecov.io/gh/google/go-github/pull/1816?src=pr&el=h1) Report
> Merging [#1816](https://codecov.io/gh/google/go-github/pull/1816?src=pr&el=desc) (29b10bb) into [master](https://codecov.io/gh/google/go-github/commit/b06cfbb1abc090e2ff47cac2f0d2d1750f847293?el=desc) (b06cfbb) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/google/go-github/pull/1816/graphs/tree.svg?width=650&height=150&src=pr&token=EgYjGjZlI0)](https://codecov.io/gh/google/go-github/pull/1816?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master    #1816   +/-   ##
=======================================
  Coverage   97.59%   97.59%           
=======================================
  Files         100      100           
  Lines        6521     6523    +2     
=======================================
+ Hits         6364     6366    +2     
  Misses         85       85           
  Partials       72       72           
```


| [Impacted Files](https://codecov.io/gh/google/go-github/pull/1816?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [github/pulls.go](https://codecov.io/gh/google/go-github/pull/1816/diff?src=pr&el=tree#diff-Z2l0aHViL3B1bGxzLmdv) | `97.01% <100.00%> (+0.04%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/google/go-github/pull/1816?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/google/go-github/pull/1816?src=pr&el=footer). Last update [b06cfbb...7724ab9](https://codecov.io/gh/google/go-github/pull/1816?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/github/apps.go b/github/apps.go
index 12df54dd..cb0069de 100644
--- a/github/apps.go
+++ b/github/apps.go
@@ -90,21 +90,26 @@ type InstallationPermissions struct {
 
 // Installation represents a GitHub Apps installation.
 type Installation struct {
-	ID                  *int64                   `json:"id,omitempty"`
-	NodeID              *string                  `json:"node_id,omitempty"`
-	AppID               *int64                   `json:"app_id,omitempty"`
-	TargetID            *int64                   `json:"target_id,omitempty"`
-	Account             *User                    `json:"account,omitempty"`
-	AccessTokensURL     *string                  `json:"access_tokens_url,omitempty"`
-	RepositoriesURL     *string                  `json:"repositories_url,omitempty"`
-	HTMLURL             *string                  `json:"html_url,omitempty"`
-	TargetType          *string                  `json:"target_type,omitempty"`
-	SingleFileName      *string                  `json:"single_file_name,omitempty"`
-	RepositorySelection *string                  `json:"repository_selection,omitempty"`
-	Events              []string                 `json:"events,omitempty"`
-	Permissions         *InstallationPermissions `json:"permissions,omitempty"`
-	CreatedAt           *Timestamp               `json:"created_at,omitempty"`
-	UpdatedAt           *Timestamp               `json:"updated_at,omitempty"`
+	ID                     *int64                   `json:"id,omitempty"`
+	NodeID                 *string                  `json:"node_id,omitempty"`
+	AppID                  *int64                   `json:"app_id,omitempty"`
+	AppSlug                *string                  `json:"app_slug,omitempty"`
+	TargetID               *int64                   `json:"target_id,omitempty"`
+	Account                *User                    `json:"account,omitempty"`
+	AccessTokensURL        *string                  `json:"access_tokens_url,omitempty"`
+	RepositoriesURL        *string                  `json:"repositories_url,omitempty"`
+	HTMLURL                *string                  `json:"html_url,omitempty"`
+	TargetType             *string                  `json:"target_type,omitempty"`
+	SingleFileName         *string                  `json:"single_file_name,omitempty"`
+	RepositorySelection    *string                  `json:"repository_selection,omitempty"`
+	Events                 []string                 `json:"events,omitempty"`
+	SingleFilePaths        []string                 `json:"single_file_paths,omitempty"`
+	Permissions            *InstallationPermissions `json:"permissions,omitempty"`
+	CreatedAt              *Timestamp               `json:"created_at,omitempty"`
+	UpdatedAt              *Timestamp               `json:"updated_at,omitempty"`
+	HasMultipleSingleFiles *bool                    `json:"has_multiple_single_files,omitempty"`
+	SuspendedBy            *User                    `json:"suspended_by,omitempty"`
+	SuspendedAt            *Timestamp               `json:"suspended_at,omitempty"`
 }
 
 // Attachment represents a GitHub Apps attachment.
diff --git a/github/apps_installation.go b/github/apps_installation.go
index b4ac5eef..501d8006 100644
--- a/github/apps_installation.go
+++ b/github/apps_installation.go
@@ -8,6 +8,7 @@ package github
 import (
 	"context"
 	"fmt"
+	"strings"
 )
 
 // ListRepositories represents the response from the list repos endpoints.
@@ -30,6 +31,10 @@ func (s *AppsService) ListRepos(ctx context.Context, opts *ListOptions) (*ListRe
 		return nil, nil, err
 	}
 
+	// TODO: remove custom Accept headers when APIs fully launch.
+	acceptHeaders := []string{mediaTypeTopicsPreview, mediaTypeRepositoryVisibilityPreview}
+	req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
+
 	var r *ListRepositories
 
 	resp, err := s.client.Do(ctx, req, &r)
@@ -56,6 +61,10 @@ func (s *AppsService) ListUserRepos(ctx context.Context, id int64, opts *ListOpt
 		return nil, nil, err
 	}
 
+	// TODO: remove custom Accept headers when APIs fully launch.
+	acceptHeaders := []string{mediaTypeTopicsPreview, mediaTypeRepositoryVisibilityPreview}
+	req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
+
 	var r *ListRepositories
 	resp, err := s.client.Do(ctx, req, &r)
 	if err != nil {
diff --git a/github/github-accessors.go b/github/github-accessors.go
index 4f82cd10..eba8d4cb 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -20,6 +20,46 @@ func (a *AbuseRateLimitError) GetRetryAfter() time.Duration {
 	return *a.RetryAfter
 }
 
+// GetGithubOwnedAllowed returns the GithubOwnedAllowed field if it's non-nil, zero value otherwise.
+func (a *ActionsAllowed) GetGithubOwnedAllowed() bool {
+	if a == nil || a.GithubOwnedAllowed == nil {
+		return false
+	}
+	return *a.GithubOwnedAllowed
+}
+
+// GetVerifiedAllowed returns the VerifiedAllowed field if it's non-nil, zero value otherwise.
+func (a *ActionsAllowed) GetVerifiedAllowed() bool {
+	if a == nil || a.VerifiedAllowed == nil {
+		return false
+	}
+	return *a.VerifiedAllowed
+}
+
+// GetAllowedActions returns the AllowedActions field if it's non-nil, zero value otherwise.
+func (a *ActionsPermissions) GetAllowedActions() string {
+	if a == nil || a.AllowedActions == nil {
+		return ""
+	}
+	return *a.AllowedActions
+}
+
+// GetEnabledRepositories returns the EnabledRepositories field if it's non-nil, zero value otherwise.
+func (a *ActionsPermissions) GetEnabledRepositories() string {
+	if a == nil || a.EnabledRepositories == nil {
+		return ""
+	}
+	return *a.EnabledRepositories
+}
+
+// GetSelectedActionsURL returns the SelectedActionsURL field if it's non-nil, zero value otherwise.
+func (a *ActionsPermissions) GetSelectedActionsURL() string {
+	if a == nil || a.SelectedActionsURL == nil {
+		return ""
+	}
+	return *a.SelectedActionsURL
+}
+
 // GetURL returns the URL field if it's non-nil, zero value otherwise.
 func (a *AdminEnforcement) GetURL() string {
 	if a == nil || a.URL == nil {
@@ -4268,6 +4308,14 @@ func (i *Installation) GetAppID() int64 {
 	return *i.AppID
 }
 
+// GetAppSlug returns the AppSlug field if it's non-nil, zero value otherwise.
+func (i *Installation) GetAppSlug() string {
+	if i == nil || i.AppSlug == nil {
+		return ""
+	}
+	return *i.AppSlug
+}
+
 // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise.
 func (i *Installation) GetCreatedAt() Timestamp {
 	if i == nil || i.CreatedAt == nil {
@@ -4276,6 +4324,14 @@ func (i *Installation) GetCreatedAt() Timestamp {
 	return *i.CreatedAt
 }
 
+// GetHasMultipleSingleFiles returns the HasMultipleSingleFiles field if it's non-nil, zero value otherwise.
+func (i *Installation) GetHasMultipleSingleFiles() bool {
+	if i == nil || i.HasMultipleSingleFiles == nil {
+		return false
+	}
+	return *i.HasMultipleSingleFiles
+}
+
 // GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise.
 func (i *Installation) GetHTMLURL() string {
 	if i == nil || i.HTMLURL == nil {
@@ -4332,6 +4388,22 @@ func (i *Installation) GetSingleFileName() string {
 	return *i.SingleFileName
 }
 
+// GetSuspendedAt returns the SuspendedAt field if it's non-nil, zero value otherwise.
+func (i *Installation) GetSuspendedAt() Timestamp {
+	if i == nil || i.SuspendedAt == nil {
+		return Timestamp{}
+	}
+	return *i.SuspendedAt
+}
+
+// GetSuspendedBy returns the SuspendedBy field.
+func (i *Installation) GetSuspendedBy() *User {
+	if i == nil {
+		return nil
+	}
+	return i.SuspendedBy
+}
+
 // GetTargetID returns the TargetID field if it's non-nil, zero value otherwise.
 func (i *Installation) GetTargetID() int64 {
 	if i == nil || i.TargetID == nil {
diff --git a/github/orgs_actions_allowed.go b/github/orgs_actions_allowed.go
new file mode 100644
index 00000000..9032d033
--- /dev/null
+++ b/github/orgs_actions_allowed.go
@@ -0,0 +1,58 @@
+// Copyright 2021 The go-github AUTHORS. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package github
+
+import (
+	"context"
+	"fmt"
+)
+
+// ActionsAllowed represents selected actions that are allowed in an organization.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/actions#get-allowed-actions-for-an-organization
+type ActionsAllowed struct {
+	GithubOwnedAllowed *bool    `json:"github_owned_allowed,omitempty"`
+	VerifiedAllowed    *bool    `json:"verified_allowed,omitempty"`
+	PatternsAllowed    []string `json:"patterns_allowed,omitempty"`
+}
+
+func (a ActionsAllowed) String() string {
+	return Stringify(a)
+}
+
+// GetActionsAllowed gets the actions that are allowed in an organization.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/actions#get-allowed-actions-for-an-organization
+func (s *OrganizationsService) GetActionsAllowed(ctx context.Context, org string) (*ActionsAllowed, *Response, error) {
+	u := fmt.Sprintf("orgs/%v/actions/permissions/selected-actions", org)
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	actionsAllowed := new(ActionsAllowed)
+	resp, err := s.client.Do(ctx, req, actionsAllowed)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return actionsAllowed, resp, nil
+}
+
+// EditActionsAllowed sets the actions that are allowed in an organization.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/actions#set-allowed-actions-for-an-organization
+func (s *OrganizationsService) EditActionsAllowed(ctx context.Context, org string, actionsAllowed ActionsAllowed) (*ActionsAllowed, *Response, error) {
+	u := fmt.Sprintf("orgs/%v/actions/permissions/selected-actions", org)
+	req, err := s.client.NewRequest("PUT", u, actionsAllowed)
+	if err != nil {
+		return nil, nil, err
+	}
+	p := new(ActionsAllowed)
+	resp, err := s.client.Do(ctx, req, p)
+	return p, resp, err
+}
diff --git a/github/orgs_actions_permissions.go b/github/orgs_actions_permissions.go
new file mode 100644
index 00000000..b8a10b25
--- /dev/null
+++ b/github/orgs_actions_permissions.go
@@ -0,0 +1,58 @@
+// Copyright 2021 The go-github AUTHORS. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package github
+
+import (
+	"context"
+	"fmt"
+)
+
+// ActionsPermissions represents a policy for repositories and allowed actions in an organization.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/actions#permissions
+type ActionsPermissions struct {
+	EnabledRepositories *string `json:"enabled_repositories,omitempty"`
+	AllowedActions      *string `json:"allowed_actions,omitempty"`
+	SelectedActionsURL  *string `json:"selected_actions_url,omitempty"`
+}
+
+func (a ActionsPermissions) String() string {
+	return Stringify(a)
+}
+
+// GetActionsPermissions gets the GitHub Actions permissions policy for repositories and allowed actions in an organization.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/actions#get-github-actions-permissions-for-an-organization
+func (s *OrganizationsService) GetActionsPermissions(ctx context.Context, org string) (*ActionsPermissions, *Response, error) {
+	u := fmt.Sprintf("orgs/%v/actions/permissions", org)
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	permissions := new(ActionsPermissions)
+	resp, err := s.client.Do(ctx, req, permissions)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return permissions, resp, nil
+}
+
+// EditActionsPermissions sets the permissions policy for repositories and allowed actions in an organization.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/actions#set-github-actions-permissions-for-an-organization
+func (s *OrganizationsService) EditActionsPermissions(ctx context.Context, org string, actionsPermissions ActionsPermissions) (*ActionsPermissions, *Response, error) {
+	u := fmt.Sprintf("orgs/%v/actions/permissions", org)
+	req, err := s.client.NewRequest("PUT", u, actionsPermissions)
+	if err != nil {
+		return nil, nil, err
+	}
+	p := new(ActionsPermissions)
+	resp, err := s.client.Do(ctx, req, p)
+	return p, resp, err
+}
diff --git a/github/pulls.go b/github/pulls.go
index 2e68d8a1..071eb121 100644
--- a/github/pulls.go
+++ b/github/pulls.go
@@ -430,13 +430,16 @@ type PullRequestOptions struct {
 
 	// The merge method to use. Possible values include: "merge", "squash", and "rebase" with the default being merge. (Optional.)
 	MergeMethod string
+
+	// If false, an empty string commit message will use the default commit message. If true, an empty string commit message will be used.
+	DontDefaultIfBlank bool
 }
 
 type pullRequestMergeRequest struct {
-	CommitMessage string `json:"commit_message,omitempty"`
-	CommitTitle   string `json:"commit_title,omitempty"`
-	MergeMethod   string `json:"merge_method,omitempty"`
-	SHA           string `json:"sha,omitempty"`
+	CommitMessage *string `json:"commit_message,omitempty"`
+	CommitTitle   string  `json:"commit_title,omitempty"`
+	MergeMethod   string  `json:"merge_method,omitempty"`
+	SHA           string  `json:"sha,omitempty"`
 }
 
 // Merge a pull request.
@@ -446,11 +449,17 @@ type pullRequestMergeRequest struct {
 func (s *PullRequestsService) Merge(ctx context.Context, owner string, repo string, number int, commitMessage string, options *PullRequestOptions) (*PullRequestMergeResult, *Response, error) {
 	u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number)
 
-	pullRequestBody := &pullRequestMergeRequest{CommitMessage: commitMessage}
+	pullRequestBody := &pullRequestMergeRequest{}
+	if commitMessage != "" {
+		pullRequestBody.CommitMessage = &commitMessage
+	}
 	if options != nil {
 		pullRequestBody.CommitTitle = options.CommitTitle
 		pullRequestBody.MergeMethod = options.MergeMethod
 		pullRequestBody.SHA = options.SHA
+		if options.DontDefaultIfBlank && commitMessage == "" {
+			pullRequestBody.CommitMessage = &commitMessage
+		}
 	}
 	req, err := s.client.NewRequest("PUT", u, pullRequestBody)
 	if err != nil {
diff --git a/github/repos_contents.go b/github/repos_contents.go
index 924fee2f..d88ae74b 100644
--- a/github/repos_contents.go
+++ b/github/repos_contents.go
@@ -144,6 +144,36 @@ func (s *RepositoriesService) DownloadContents(ctx context.Context, owner, repo,
 	return nil, resp, fmt.Errorf("No file named %s found in %s", filename, dir)
 }
 
+// DownloadContentsWithMeta is identical to DownloadContents but additionally
+// returns the RepositoryContent of the requested file. This additional data
+// is useful for future operations involving the requested file. For merely
+// reading the content of a file, DownloadContents is perfectly adequate.
+//
+// It is possible for the download to result in a failed response when the
+// returned error is nil. Callers should check the returned Response status
+// code to verify the content is from a successful response.
+func (s *RepositoriesService) DownloadContentsWithMeta(ctx context.Context, owner, repo, filepath string, opts *RepositoryContentGetOptions) (io.ReadCloser, *RepositoryContent, *Response, error) {
+	dir := path.Dir(filepath)
+	filename := path.Base(filepath)
+	_, dirContents, resp, err := s.GetContents(ctx, owner, repo, dir, opts)
+	if err != nil {
+		return nil, nil, resp, err
+	}
+	for _, contents := range dirContents {
+		if *contents.Name == filename {
+			if contents.DownloadURL == nil || *contents.DownloadURL == "" {
+				return nil, contents, resp, fmt.Errorf("No download link found for %s", filepath)
+			}
+			dlResp, err := s.client.client.Get(*contents.DownloadURL)
+			if err != nil {
+				return nil, contents, &Response{Response: dlResp}, err
+			}
+			return dlResp.Body, contents, &Response{Response: dlResp}, nil
+		}
+	}
+	return nil, nil, resp, fmt.Errorf("No file named %s found in %s", filename, dir)
+}
+
 // GetContents can return either the metadata and content of a single file
 // (when path references a file) or the metadata of all the files and/or
 // subdirectories of a directory (when path references a directory). To make it
diff --git a/update-urls/main.go b/update-urls/main.go
index bdfd0882..d0d4955a 100644
--- a/update-urls/main.go
+++ b/update-urls/main.go
@@ -55,38 +55,39 @@ var (
 	// skipMethods holds methods which are skipped because they do not have GitHub v3
 	// API URLs or are otherwise problematic in parsing, discovering, and/or fixing.
 	skipMethods = map[string]bool{
-		"ActionsService.DownloadArtifact":            true,
-		"AdminService.CreateOrg":                     true,
-		"AdminService.CreateUser":                    true,
-		"AdminService.CreateUserImpersonation":       true,
-		"AdminService.DeleteUserImpersonation":       true,
-		"AdminService.GetAdminStats":                 true,
-		"AdminService.RenameOrg":                     true,
-		"AdminService.RenameOrgByName":               true,
-		"AdminService.UpdateTeamLDAPMapping":         true,
-		"AdminService.UpdateUserLDAPMapping":         true,
-		"AppsService.FindRepositoryInstallationByID": true,
-		"AuthorizationsService.CreateImpersonation":  true,
-		"AuthorizationsService.DeleteImpersonation":  true,
-		"IssueImportService.CheckStatus":             true,
-		"IssueImportService.CheckStatusSince":        true,
-		"IssueImportService.Create":                  true,
-		"MarketplaceService.marketplaceURI":          true,
-		"OrganizationsService.GetByID":               true,
-		"RepositoriesService.DeletePreReceiveHook":   true,
-		"RepositoriesService.DownloadContents":       true,
-		"RepositoriesService.GetArchiveLink":         true,
-		"RepositoriesService.GetByID":                true,
-		"RepositoriesService.GetPreReceiveHook":      true,
-		"RepositoriesService.ListPreReceiveHooks":    true,
-		"RepositoriesService.UpdatePreReceiveHook":   true,
-		"SearchService.search":                       true,
-		"TeamsService.ListTeamMembersByID":           true,
-		"UsersService.DemoteSiteAdmin":               true,
-		"UsersService.GetByID":                       true,
-		"UsersService.PromoteSiteAdmin":              true,
-		"UsersService.Suspend":                       true,
-		"UsersService.Unsuspend":                     true,
+		"ActionsService.DownloadArtifact":              true,
+		"AdminService.CreateOrg":                       true,
+		"AdminService.CreateUser":                      true,
+		"AdminService.CreateUserImpersonation":         true,
+		"AdminService.DeleteUserImpersonation":         true,
+		"AdminService.GetAdminStats":                   true,
+		"AdminService.RenameOrg":                       true,
+		"AdminService.RenameOrgByName":                 true,
+		"AdminService.UpdateTeamLDAPMapping":           true,
+		"AdminService.UpdateUserLDAPMapping":           true,
+		"AppsService.FindRepositoryInstallationByID":   true,
+		"AuthorizationsService.CreateImpersonation":    true,
+		"AuthorizationsService.DeleteImpersonation":    true,
+		"IssueImportService.CheckStatus":               true,
+		"IssueImportService.CheckStatusSince":          true,
+		"IssueImportService.Create":                    true,
+		"MarketplaceService.marketplaceURI":            true,
+		"OrganizationsService.GetByID":                 true,
+		"RepositoriesService.DeletePreReceiveHook":     true,
+		"RepositoriesService.DownloadContents":         true,
+		"RepositoriesService.DownloadContentsWithMeta": true,
+		"RepositoriesService.GetArchiveLink":           true,
+		"RepositoriesService.GetByID":                  true,
+		"RepositoriesService.GetPreReceiveHook":        true,
+		"RepositoriesService.ListPreReceiveHooks":      true,
+		"RepositoriesService.UpdatePreReceiveHook":     true,
+		"SearchService.search":                         true,
+		"TeamsService.ListTeamMembersByID":             true,
+		"UsersService.DemoteSiteAdmin":                 true,
+		"UsersService.GetByID":                         true,
+		"UsersService.PromoteSiteAdmin":                true,
+		"UsersService.Suspend":                         true,
+		"UsersService.Unsuspend":                       true,
 	}
 
 	helperOverrides = map[string]overrideFunc{


Test patch:
diff --git a/github/apps_installation_test.go b/github/apps_installation_test.go
index a73498c6..5e8a18de 100644
--- a/github/apps_installation_test.go
+++ b/github/apps_installation_test.go
@@ -10,6 +10,7 @@ import (
 	"fmt"
 	"net/http"
 	"reflect"
+	"strings"
 	"testing"
 )
 
@@ -17,8 +18,10 @@ func TestAppsService_ListRepos(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
 
+	wantAcceptHeaders := []string{mediaTypeTopicsPreview, mediaTypeRepositoryVisibilityPreview}
 	mux.HandleFunc("/installation/repositories", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "GET")
+		testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", "))
 		testFormValues(t, r, values{
 			"page":     "1",
 			"per_page": "2",
@@ -52,8 +55,10 @@ func TestAppsService_ListUserRepos(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
 
+	wantAcceptHeaders := []string{mediaTypeTopicsPreview, mediaTypeRepositoryVisibilityPreview}
 	mux.HandleFunc("/user/installations/1/repositories", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "GET")
+		testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", "))
 		testFormValues(t, r, values{
 			"page":     "1",
 			"per_page": "2",
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index 24610c53..53d459b0 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -23,6 +23,56 @@ func TestAbuseRateLimitError_GetRetryAfter(tt *testing.T) {
 	a.GetRetryAfter()
 }
 
+func TestActionsAllowed_GetGithubOwnedAllowed(tt *testing.T) {
+	var zeroValue bool
+	a := &ActionsAllowed{GithubOwnedAllowed: &zeroValue}
+	a.GetGithubOwnedAllowed()
+	a = &ActionsAllowed{}
+	a.GetGithubOwnedAllowed()
+	a = nil
+	a.GetGithubOwnedAllowed()
+}
+
+func TestActionsAllowed_GetVerifiedAllowed(tt *testing.T) {
+	var zeroValue bool
+	a := &ActionsAllowed{VerifiedAllowed: &zeroValue}
+	a.GetVerifiedAllowed()
+	a = &ActionsAllowed{}
+	a.GetVerifiedAllowed()
+	a = nil
+	a.GetVerifiedAllowed()
+}
+
+func TestActionsPermissions_GetAllowedActions(tt *testing.T) {
+	var zeroValue string
+	a := &ActionsPermissions{AllowedActions: &zeroValue}
+	a.GetAllowedActions()
+	a = &ActionsPermissions{}
+	a.GetAllowedActions()
+	a = nil
+	a.GetAllowedActions()
+}
+
+func TestActionsPermissions_GetEnabledRepositories(tt *testing.T) {
+	var zeroValue string
+	a := &ActionsPermissions{EnabledRepositories: &zeroValue}
+	a.GetEnabledRepositories()
+	a = &ActionsPermissions{}
+	a.GetEnabledRepositories()
+	a = nil
+	a.GetEnabledRepositories()
+}
+
+func TestActionsPermissions_GetSelectedActionsURL(tt *testing.T) {
+	var zeroValue string
+	a := &ActionsPermissions{SelectedActionsURL: &zeroValue}
+	a.GetSelectedActionsURL()
+	a = &ActionsPermissions{}
+	a.GetSelectedActionsURL()
+	a = nil
+	a.GetSelectedActionsURL()
+}
+
 func TestAdminEnforcement_GetURL(tt *testing.T) {
 	var zeroValue string
 	a := &AdminEnforcement{URL: &zeroValue}
@@ -5003,6 +5053,16 @@ func TestInstallation_GetAppID(tt *testing.T) {
 	i.GetAppID()
 }
 
+func TestInstallation_GetAppSlug(tt *testing.T) {
+	var zeroValue string
+	i := &Installation{AppSlug: &zeroValue}
+	i.GetAppSlug()
+	i = &Installation{}
+	i.GetAppSlug()
+	i = nil
+	i.GetAppSlug()
+}
+
 func TestInstallation_GetCreatedAt(tt *testing.T) {
 	var zeroValue Timestamp
 	i := &Installation{CreatedAt: &zeroValue}
@@ -5013,6 +5073,16 @@ func TestInstallation_GetCreatedAt(tt *testing.T) {
 	i.GetCreatedAt()
 }
 
+func TestInstallation_GetHasMultipleSingleFiles(tt *testing.T) {
+	var zeroValue bool
+	i := &Installation{HasMultipleSingleFiles: &zeroValue}
+	i.GetHasMultipleSingleFiles()
+	i = &Installation{}
+	i.GetHasMultipleSingleFiles()
+	i = nil
+	i.GetHasMultipleSingleFiles()
+}
+
 func TestInstallation_GetHTMLURL(tt *testing.T) {
 	var zeroValue string
 	i := &Installation{HTMLURL: &zeroValue}
@@ -5080,6 +5150,23 @@ func TestInstallation_GetSingleFileName(tt *testing.T) {
 	i.GetSingleFileName()
 }
 
+func TestInstallation_GetSuspendedAt(tt *testing.T) {
+	var zeroValue Timestamp
+	i := &Installation{SuspendedAt: &zeroValue}
+	i.GetSuspendedAt()
+	i = &Installation{}
+	i.GetSuspendedAt()
+	i = nil
+	i.GetSuspendedAt()
+}
+
+func TestInstallation_GetSuspendedBy(tt *testing.T) {
+	i := &Installation{}
+	i.GetSuspendedBy()
+	i = nil
+	i.GetSuspendedBy()
+}
+
 func TestInstallation_GetTargetID(tt *testing.T) {
 	var zeroValue int64
 	i := &Installation{TargetID: &zeroValue}
diff --git a/github/github-stringify_test.go b/github/github-stringify_test.go
index d77747d8..9ff36ff3 100644
--- a/github/github-stringify_test.go
+++ b/github/github-stringify_test.go
@@ -13,6 +13,29 @@ import (
 
 func Float64(v float64) *float64 { return &v }
 
+func TestActionsAllowed_String(t *testing.T) {
+	v := ActionsAllowed{
+		GithubOwnedAllowed: Bool(false),
+		VerifiedAllowed:    Bool(false),
+	}
+	want := `github.ActionsAllowed{GithubOwnedAllowed:false, VerifiedAllowed:false}`
+	if got := v.String(); got != want {
+		t.Errorf("ActionsAllowed.String = %v, want %v", got, want)
+	}
+}
+
+func TestActionsPermissions_String(t *testing.T) {
+	v := ActionsPermissions{
+		EnabledRepositories: String(""),
+		AllowedActions:      String(""),
+		SelectedActionsURL:  String(""),
+	}
+	want := `github.ActionsPermissions{EnabledRepositories:"", AllowedActions:"", SelectedActionsURL:""}`
+	if got := v.String(); got != want {
+		t.Errorf("ActionsPermissions.String = %v, want %v", got, want)
+	}
+}
+
 func TestAdminStats_String(t *testing.T) {
 	v := AdminStats{
 		Issues:     &IssueStats{},
@@ -569,22 +592,26 @@ func TestImport_String(t *testing.T) {
 
 func TestInstallation_String(t *testing.T) {
 	v := Installation{
-		ID:                  Int64(0),
-		NodeID:              String(""),
-		AppID:               Int64(0),
-		TargetID:            Int64(0),
-		Account:             &User{},
-		AccessTokensURL:     String(""),
-		RepositoriesURL:     String(""),
-		HTMLURL:             String(""),
-		TargetType:          String(""),
-		SingleFileName:      String(""),
-		RepositorySelection: String(""),
-		Permissions:         &InstallationPermissions{},
-		CreatedAt:           &Timestamp{},
-		UpdatedAt:           &Timestamp{},
-	}
-	want := `github.Installation{ID:0, NodeID:"", AppID:0, TargetID:0, Account:github.User{}, AccessTokensURL:"", RepositoriesURL:"", HTMLURL:"", TargetType:"", SingleFileName:"", RepositorySelection:"", Permissions:github.InstallationPermissions{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}`
+		ID:                     Int64(0),
+		NodeID:                 String(""),
+		AppID:                  Int64(0),
+		AppSlug:                String(""),
+		TargetID:               Int64(0),
+		Account:                &User{},
+		AccessTokensURL:        String(""),
+		RepositoriesURL:        String(""),
+		HTMLURL:                String(""),
+		TargetType:             String(""),
+		SingleFileName:         String(""),
+		RepositorySelection:    String(""),
+		Permissions:            &InstallationPermissions{},
+		CreatedAt:              &Timestamp{},
+		UpdatedAt:              &Timestamp{},
+		HasMultipleSingleFiles: Bool(false),
+		SuspendedBy:            &User{},
+		SuspendedAt:            &Timestamp{},
+	}
+	want := `github.Installation{ID:0, NodeID:"", AppID:0, AppSlug:"", TargetID:0, Account:github.User{}, AccessTokensURL:"", RepositoriesURL:"", HTMLURL:"", TargetType:"", SingleFileName:"", RepositorySelection:"", Permissions:github.InstallationPermissions{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, HasMultipleSingleFiles:false, SuspendedBy:github.User{}, SuspendedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}`
 	if got := v.String(); got != want {
 		t.Errorf("Installation.String = %v, want %v", got, want)
 	}
diff --git a/github/orgs_actions_allowed_test.go b/github/orgs_actions_allowed_test.go
new file mode 100644
index 00000000..5b8276be
--- /dev/null
+++ b/github/orgs_actions_allowed_test.go
@@ -0,0 +1,84 @@
+// Copyright 2021 The go-github AUTHORS. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package github
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"reflect"
+	"testing"
+)
+
+func TestOrganizationsService_GetActionsAllowed(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/orgs/o/actions/permissions/selected-actions", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `{"github_owned_allowed":true, "verified_allowed":false, "patterns_allowed":["a/b"]}`)
+	})
+
+	ctx := context.Background()
+	org, _, err := client.Organizations.GetActionsAllowed(ctx, "o")
+	if err != nil {
+		t.Errorf("Organizations.GetActionsAllowed returned error: %v", err)
+	}
+	want := &ActionsAllowed{GithubOwnedAllowed: Bool(true), VerifiedAllowed: Bool(false), PatternsAllowed: []string{"a/b"}}
+	if !reflect.DeepEqual(org, want) {
+		t.Errorf("Organizations.GetActionsAllowed returned %+v, want %+v", org, want)
+	}
+
+	const methodName = "GetActionsAllowed"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Organizations.GetActionsAllowed(ctx, "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Organizations.GetActionsAllowed(ctx, "o")
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestOrganizationsService_EditActionsAllowed(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+	input := &ActionsAllowed{GithubOwnedAllowed: Bool(true), VerifiedAllowed: Bool(false), PatternsAllowed: []string{"a/b"}}
+
+	mux.HandleFunc("/orgs/o/actions/permissions/selected-actions", func(w http.ResponseWriter, r *http.Request) {
+		v := new(ActionsAllowed)
+		json.NewDecoder(r.Body).Decode(v)
+
+		testMethod(t, r, "PUT")
+		if !reflect.DeepEqual(v, input) {
+			t.Errorf("Request body = %+v, want %+v", v, input)
+		}
+
+		fmt.Fprint(w, `{"github_owned_allowed":true, "verified_allowed":false, "patterns_allowed":["a/b"]}`)
+	})
+
+	ctx := context.Background()
+	org, _, err := client.Organizations.EditActionsAllowed(ctx, "o", *input)
+	if err != nil {
+		t.Errorf("Organizations.EditActionsAllowed returned error: %v", err)
+	}
+
+	want := &ActionsAllowed{GithubOwnedAllowed: Bool(true), VerifiedAllowed: Bool(false), PatternsAllowed: []string{"a/b"}}
+	if !reflect.DeepEqual(org, want) {
+		t.Errorf("Organizations.EditActionsAllowed returned %+v, want %+v", org, want)
+	}
+
+	const methodName = "EditActionsAllowed"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Organizations.EditActionsAllowed(ctx, "\n", *input)
+		return err
+	})
+}
diff --git a/github/orgs_actions_permissions_test.go b/github/orgs_actions_permissions_test.go
new file mode 100644
index 00000000..cb27c6b1
--- /dev/null
+++ b/github/orgs_actions_permissions_test.go
@@ -0,0 +1,85 @@
+// Copyright 2021 The go-github AUTHORS. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package github
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"reflect"
+	"testing"
+)
+
+func TestOrganizationsService_GetActionsPermissions(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/orgs/o/actions/permissions", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `{"enabled_repositories": "all", "allowed_actions": "all"}`)
+	})
+
+	ctx := context.Background()
+	org, _, err := client.Organizations.GetActionsPermissions(ctx, "o")
+	if err != nil {
+		t.Errorf("Organizations.GetActionsPermissions returned error: %v", err)
+	}
+	want := &ActionsPermissions{EnabledRepositories: String("all"), AllowedActions: String("all")}
+	if !reflect.DeepEqual(org, want) {
+		t.Errorf("Organizations.GetActionsPermissions returned %+v, want %+v", org, want)
+	}
+
+	const methodName = "GetActionsPermissions"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Organizations.GetActionsPermissions(ctx, "\n")
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Organizations.GetActionsPermissions(ctx, "o")
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
+func TestOrganizationsService_EditActionsPermissions(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	input := &ActionsPermissions{EnabledRepositories: String("all"), AllowedActions: String("selected")}
+
+	mux.HandleFunc("/orgs/o/actions/permissions", func(w http.ResponseWriter, r *http.Request) {
+		v := new(ActionsPermissions)
+		json.NewDecoder(r.Body).Decode(v)
+
+		testMethod(t, r, "PUT")
+		if !reflect.DeepEqual(v, input) {
+			t.Errorf("Request body = %+v, want %+v", v, input)
+		}
+
+		fmt.Fprint(w, `{"enabled_repositories": "all", "allowed_actions": "selected"}`)
+	})
+
+	ctx := context.Background()
+	org, _, err := client.Organizations.EditActionsPermissions(ctx, "o", *input)
+	if err != nil {
+		t.Errorf("Organizations.EditActionsPermissions returned error: %v", err)
+	}
+
+	want := &ActionsPermissions{EnabledRepositories: String("all"), AllowedActions: String("selected")}
+	if !reflect.DeepEqual(org, want) {
+		t.Errorf("Organizations.EditActionsPermissions returned %+v, want %+v", org, want)
+	}
+
+	const methodName = "EditActionsPermissions"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Organizations.EditActionsPermissions(ctx, "\n", *input)
+		return err
+	})
+}
diff --git a/github/pulls_test.go b/github/pulls_test.go
index d5a47bc9..d2405e23 100644
--- a/github/pulls_test.go
+++ b/github/pulls_test.go
@@ -789,6 +789,12 @@ func TestPullRequestsService_Merge_options(t *testing.T) {
 			},
 			wantBody: `{"commit_message":"merging pull request","commit_title":"Extra detail","merge_method":"squash","sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e"}`,
 		},
+		{
+			options: &PullRequestOptions{
+				DontDefaultIfBlank: true,
+			},
+			wantBody: `{"commit_message":"merging pull request"}`,
+		},
 	}
 
 	for i, test := range tests {
@@ -805,3 +811,32 @@ func TestPullRequestsService_Merge_options(t *testing.T) {
 		}
 	}
 }
+
+func TestPullRequestsService_Merge_Blank_Message(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+	madeRequest := false
+	expectedBody := ""
+	mux.HandleFunc("/repos/o/r/pulls/1/merge", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "PUT")
+		testBody(t, r, expectedBody+"\n")
+		madeRequest = true
+	})
+
+	ctx := context.Background()
+	expectedBody = `{}`
+	_, _, _ = client.PullRequests.Merge(ctx, "o", "r", 1, "", nil)
+	if !madeRequest {
+		t.Error("TestPullRequestsService_Merge_Blank_Message #1 did not make request")
+	}
+
+	madeRequest = false
+	opts := PullRequestOptions{
+		DontDefaultIfBlank: true,
+	}
+	expectedBody = `{"commit_message":""}`
+	_, _, _ = client.PullRequests.Merge(ctx, "o", "r", 1, "", &opts)
+	if !madeRequest {
+		t.Error("TestPullRequestsService_Merge_Blank_Message #2 did not make request")
+	}
+}
diff --git a/github/repos_contents_test.go b/github/repos_contents_test.go
index afc260b3..c8cfb45c 100644
--- a/github/repos_contents_test.go
+++ b/github/repos_contents_test.go
@@ -247,6 +247,155 @@ func TestRepositoriesService_DownloadContents_NoFile(t *testing.T) {
 	}
 }
 
+func TestRepositoriesService_DownloadContentsWithMeta_Success(t *testing.T) {
+	client, mux, serverURL, teardown := setup()
+	defer teardown()
+	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `[{
+		  "type": "file",
+		  "name": "f",
+		  "download_url": "`+serverURL+baseURLPath+`/download/f"
+		}]`)
+	})
+	mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, "foo")
+	})
+
+	ctx := context.Background()
+	r, c, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil)
+	if err != nil {
+		t.Errorf("Repositories.DownloadContentsWithMeta returned error: %v", err)
+	}
+
+	if got, want := resp.Response.StatusCode, http.StatusOK; got != want {
+		t.Errorf("Repositories.DownloadContentsWithMeta returned status code %v, want %v", got, want)
+	}
+
+	bytes, err := ioutil.ReadAll(r)
+	if err != nil {
+		t.Errorf("Error reading response body: %v", err)
+	}
+	r.Close()
+
+	if got, want := string(bytes), "foo"; got != want {
+		t.Errorf("Repositories.DownloadContentsWithMeta returned %v, want %v", got, want)
+	}
+
+	if c != nil && c.Name != nil {
+		if got, want := *c.Name, "f"; got != want {
+			t.Errorf("Repositories.DownloadContentsWithMeta returned content name %v, want %v", got, want)
+		}
+	} else {
+		t.Errorf("Returned RepositoryContent is null")
+	}
+
+	const methodName = "DownloadContentsWithMeta"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, _, err = client.Repositories.DownloadContentsWithMeta(ctx, "\n", "\n", "\n", nil)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, cot, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		if cot != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, cot)
+		}
+		return resp, err
+	})
+}
+
+func TestRepositoriesService_DownloadContentsWithMeta_FailedResponse(t *testing.T) {
+	client, mux, serverURL, teardown := setup()
+	defer teardown()
+	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `[{
+			"type": "file",
+			"name": "f",
+			"download_url": "`+serverURL+baseURLPath+`/download/f"
+		  }]`)
+	})
+	mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		w.WriteHeader(http.StatusInternalServerError)
+		fmt.Fprint(w, "foo error")
+	})
+
+	ctx := context.Background()
+	r, c, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil)
+	if err != nil {
+		t.Errorf("Repositories.DownloadContentsWithMeta returned error: %v", err)
+	}
+
+	if got, want := resp.Response.StatusCode, http.StatusInternalServerError; got != want {
+		t.Errorf("Repositories.DownloadContentsWithMeta returned status code %v, want %v", got, want)
+	}
+
+	bytes, err := ioutil.ReadAll(r)
+	if err != nil {
+		t.Errorf("Error reading response body: %v", err)
+	}
+	r.Close()
+
+	if got, want := string(bytes), "foo error"; got != want {
+		t.Errorf("Repositories.DownloadContentsWithMeta returned %v, want %v", got, want)
+	}
+
+	if c != nil && c.Name != nil {
+		if got, want := *c.Name, "f"; got != want {
+			t.Errorf("Repositories.DownloadContentsWithMeta returned content name %v, want %v", got, want)
+		}
+	} else {
+		t.Errorf("Returned RepositoryContent is null")
+	}
+}
+
+func TestRepositoriesService_DownloadContentsWithMeta_NoDownloadURL(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `[{
+		  "type": "file",
+		  "name": "f",
+		}]`)
+	})
+
+	ctx := context.Background()
+	_, _, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil)
+	if err == nil {
+		t.Errorf("Repositories.DownloadContentsWithMeta did not return expected error")
+	}
+
+	if resp == nil {
+		t.Errorf("Repositories.DownloadContentsWithMeta did not return expected response")
+	}
+}
+
+func TestRepositoriesService_DownloadContentsWithMeta_NoFile(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+	mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		fmt.Fprint(w, `[]`)
+	})
+
+	ctx := context.Background()
+	_, _, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil)
+	if err == nil {
+		t.Errorf("Repositories.DownloadContentsWithMeta did not return expected error")
+	}
+
+	if resp == nil {
+		t.Errorf("Repositories.DownloadContentsWithMeta did not return expected response")
+	}
+}
+
 func TestRepositoriesService_GetContents_File(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()