Repository: google/go-github
Pull request #2165 (2021-10-28 20:21:54)

Upload Code Scanning Sarif results to GitHub
Add a method to upload Code Scanning Sarif results to GitHub


Patch:
diff --git a/github/code-scanning.go b/github/code-scanning.go
index 50264a0c..4508c339 100644
--- a/github/code-scanning.go
+++ b/github/code-scanning.go
@@ -119,6 +119,26 @@ type AlertListOptions struct {
 	ListOptions
 }
 
+// SarifAnalysis specifies the results of a code scanning job.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/code-scanning#upload-an-analysis-as-sarif-data
+type SarifAnalysis struct {
+	CommitSHA   *string    `json:"commit_sha,omitempty"`
+	Ref         *string    `json:"ref,omitempty"`
+	Sarif       *string    `json:"sarif,omitempty"`
+	CheckoutURI *string    `json:"checkout_uri,omitempty"`
+	StartedAt   *Timestamp `json:"started_at,omitempty"`
+	ToolName    *string    `json:"tool_name,omitempty"`
+}
+
+// SarifID identifies a sarif analysis upload.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/code-scanning#upload-an-analysis-as-sarif-data
+type SarifID struct {
+	ID  *string `json:"id,omitempty"`
+	URL *string `json:"url,omitempty"`
+}
+
 // ListAlertsForRepo lists code scanning alerts for a repository.
 //
 // Lists all open code scanning alerts for the default branch (usually master) and protected branches in a repository.
@@ -171,3 +191,27 @@ func (s *CodeScanningService) GetAlert(ctx context.Context, owner, repo string,
 
 	return a, resp, nil
 }
+
+// UploadSarif uploads the result of code scanning job to GitHub.
+//
+// For the parameter sarif, you must first compress your SARIF file using gzip and then translate the contents of the file into a Base64 encoding string.
+// You must use an access token with the security_events scope to use this endpoint. GitHub Apps must have the security_events
+// write permission to use this endpoint.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/code-scanning#upload-an-analysis-as-sarif-data
+func (s *CodeScanningService) UploadSarif(ctx context.Context, owner, repo string, sarif *SarifAnalysis) (*SarifID, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/code-scanning/sarifs", owner, repo)
+
+	req, err := s.client.NewRequest("POST", u, sarif)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	sarifID := new(SarifID)
+	resp, err := s.client.Do(ctx, req, sarifID)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return sarifID, resp, nil
+}
diff --git a/github/github-accessors.go b/github/github-accessors.go
index 1d2db4ec..b2c53f36 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -15148,6 +15148,70 @@ func (r *RunnerLabels) GetType() string {
 	return *r.Type
 }
 
+// GetCheckoutURI returns the CheckoutURI field if it's non-nil, zero value otherwise.
+func (s *SarifAnalysis) GetCheckoutURI() string {
+	if s == nil || s.CheckoutURI == nil {
+		return ""
+	}
+	return *s.CheckoutURI
+}
+
+// GetCommitSHA returns the CommitSHA field if it's non-nil, zero value otherwise.
+func (s *SarifAnalysis) GetCommitSHA() string {
+	if s == nil || s.CommitSHA == nil {
+		return ""
+	}
+	return *s.CommitSHA
+}
+
+// GetRef returns the Ref field if it's non-nil, zero value otherwise.
+func (s *SarifAnalysis) GetRef() string {
+	if s == nil || s.Ref == nil {
+		return ""
+	}
+	return *s.Ref
+}
+
+// GetSarif returns the Sarif field if it's non-nil, zero value otherwise.
+func (s *SarifAnalysis) GetSarif() string {
+	if s == nil || s.Sarif == nil {
+		return ""
+	}
+	return *s.Sarif
+}
+
+// GetStartedAt returns the StartedAt field if it's non-nil, zero value otherwise.
+func (s *SarifAnalysis) GetStartedAt() Timestamp {
+	if s == nil || s.StartedAt == nil {
+		return Timestamp{}
+	}
+	return *s.StartedAt
+}
+
+// GetToolName returns the ToolName field if it's non-nil, zero value otherwise.
+func (s *SarifAnalysis) GetToolName() string {
+	if s == nil || s.ToolName == nil {
+		return ""
+	}
+	return *s.ToolName
+}
+
+// GetID returns the ID field if it's non-nil, zero value otherwise.
+func (s *SarifID) GetID() string {
+	if s == nil || s.ID == nil {
+		return ""
+	}
+	return *s.ID
+}
+
+// GetURL returns the URL field if it's non-nil, zero value otherwise.
+func (s *SarifID) GetURL() string {
+	if s == nil || s.URL == nil {
+		return ""
+	}
+	return *s.URL
+}
+
 // GetActive returns the Active field if it's non-nil, zero value otherwise.
 func (s *SCIMUserAttributes) GetActive() bool {
 	if s == nil || s.Active == nil {
@@ -16372,6 +16436,30 @@ func (t *Timeline) GetAssignee() *User {
 	return t.Assignee
 }
 
+// GetAssigner returns the Assigner field.
+func (t *Timeline) GetAssigner() *User {
+	if t == nil {
+		return nil
+	}
+	return t.Assigner
+}
+
+// GetAuthor returns the Author field.
+func (t *Timeline) GetAuthor() *CommitAuthor {
+	if t == nil {
+		return nil
+	}
+	return t.Author
+}
+
+// GetBody returns the Body field if it's non-nil, zero value otherwise.
+func (t *Timeline) GetBody() string {
+	if t == nil || t.Body == nil {
+		return ""
+	}
+	return *t.Body
+}
+
 // GetCommitID returns the CommitID field if it's non-nil, zero value otherwise.
 func (t *Timeline) GetCommitID() string {
 	if t == nil || t.CommitID == nil {
@@ -16380,6 +16468,14 @@ func (t *Timeline) GetCommitID() string {
 	return *t.CommitID
 }
 
+// GetCommitter returns the Committer field.
+func (t *Timeline) GetCommitter() *CommitAuthor {
+	if t == nil {
+		return nil
+	}
+	return t.Committer
+}
+
 // GetCommitURL returns the CommitURL field if it's non-nil, zero value otherwise.
 func (t *Timeline) GetCommitURL() string {
 	if t == nil || t.CommitURL == nil {
@@ -16420,6 +16516,14 @@ func (t *Timeline) GetLabel() *Label {
 	return t.Label
 }
 
+// GetMessage returns the Message field if it's non-nil, zero value otherwise.
+func (t *Timeline) GetMessage() string {
+	if t == nil || t.Message == nil {
+		return ""
+	}
+	return *t.Message
+}
+
 // GetMilestone returns the Milestone field.
 func (t *Timeline) GetMilestone() *Milestone {
 	if t == nil {
@@ -16444,6 +16548,30 @@ func (t *Timeline) GetRename() *Rename {
 	return t.Rename
 }
 
+// GetRequester returns the Requester field.
+func (t *Timeline) GetRequester() *User {
+	if t == nil {
+		return nil
+	}
+	return t.Requester
+}
+
+// GetReviewer returns the Reviewer field.
+func (t *Timeline) GetReviewer() *User {
+	if t == nil {
+		return nil
+	}
+	return t.Reviewer
+}
+
+// GetSHA returns the SHA field if it's non-nil, zero value otherwise.
+func (t *Timeline) GetSHA() string {
+	if t == nil || t.SHA == nil {
+		return ""
+	}
+	return *t.SHA
+}
+
 // GetSource returns the Source field.
 func (t *Timeline) GetSource() *Source {
 	if t == nil {
@@ -16460,6 +16588,14 @@ func (t *Timeline) GetState() string {
 	return *t.State
 }
 
+// GetSubmittedAt returns the SubmittedAt field if it's non-nil, zero value otherwise.
+func (t *Timeline) GetSubmittedAt() time.Time {
+	if t == nil || t.SubmittedAt == nil {
+		return time.Time{}
+	}
+	return *t.SubmittedAt
+}
+
 // GetURL returns the URL field if it's non-nil, zero value otherwise.
 func (t *Timeline) GetURL() string {
 	if t == nil || t.URL == nil {
@@ -16468,6 +16604,14 @@ func (t *Timeline) GetURL() string {
 	return *t.URL
 }
 
+// GetUser returns the User field.
+func (t *Timeline) GetUser() *User {
+	if t == nil {
+		return nil
+	}
+	return t.User
+}
+
 // GetGUID returns the GUID field if it's non-nil, zero value otherwise.
 func (t *Tool) GetGUID() string {
 	if t == nil || t.GUID == nil {
diff --git a/github/github.go b/github/github.go
index d4d7a54a..02cdabd5 100644
--- a/github/github.go
+++ b/github/github.go
@@ -48,6 +48,21 @@ const (
 	mediaTypeIssueImportAPI    = "application/vnd.github.golden-comet-preview+json"
 
 	// Media Type values to access preview APIs
+	// These media types will be added to the API request as headers
+	// and used to enable particular features on GitHub API that are still in preview.
+	// After some time, specific media types will be promoted (to a "stable" state).
+	// From then on, the preview headers are not required anymore to activate the additional
+	// feature on GitHub.com's API. However, this API header might still be needed for users
+	// to run a GitHub Enterprise Server on-premise.
+	// It's not uncommon for GitHub Enterprise Server customers to run older versions which
+	// would probably rely on the preview headers for some time.
+	// While the header promotion is going out for GitHub.com, it may be some time before it
+	// even arrives in GitHub Enterprise Server.
+	// We keep those preview headers around to avoid breaking older GitHub Enterprise Server
+	// versions. Additionally, non-functional (preview) headers don't create any side effects
+	// on GitHub Cloud version.
+	//
+	// See https://github.com/google/go-github/pull/2125 for full context.
 
 	// https://developer.github.com/changes/2014-12-09-new-attributes-for-stars-api/
 	mediaTypeStarringPreview = "application/vnd.github.v3.star+json"
diff --git a/github/issues.go b/github/issues.go
index 46aff295..f35f2b56 100644
--- a/github/issues.go
+++ b/github/issues.go
@@ -299,6 +299,29 @@ func (s *IssuesService) Edit(ctx context.Context, owner string, repo string, num
 	return i, resp, nil
 }
 
+// Remove a milestone from an issue.
+//
+// This is a helper method to explicitly update an issue with a `null` milestone, thereby removing it.
+//
+// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/issues/#update-an-issue
+func (s *IssuesService) RemoveMilestone(ctx context.Context, owner, repo string, issueNumber int) (*Issue, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/issues/%v", owner, repo, issueNumber)
+	req, err := s.client.NewRequest("PATCH", u, &struct {
+		Milestone *Milestone `json:"milestone"`
+	}{})
+	if err != nil {
+		return nil, nil, err
+	}
+
+	i := new(Issue)
+	resp, err := s.client.Do(ctx, req, i)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return i, resp, nil
+}
+
 // LockIssueOptions specifies the optional parameters to the
 // IssuesService.Lock method.
 type LockIssueOptions struct {
diff --git a/github/issues_timeline.go b/github/issues_timeline.go
index 1fd13b12..845e3f76 100644
--- a/github/issues_timeline.go
+++ b/github/issues_timeline.go
@@ -15,7 +15,7 @@ import (
 // Timeline represents an event that occurred around an Issue or Pull Request.
 //
 // It is similar to an IssueEvent but may contain more information.
-// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/issues/timeline/
+// GitHub API docs: https://docs.github.com/en/developers/webhooks-and-events/events/issue-event-types
 type Timeline struct {
 	ID        *int64  `json:"id,omitempty"`
 	URL       *string `json:"url,omitempty"`
@@ -24,6 +24,18 @@ type Timeline struct {
 	// The User object that generated the event.
 	Actor *User `json:"actor,omitempty"`
 
+	// The person who commented on the issue.
+	User *User `json:"user,omitempty"`
+
+	// The person who authored the commit.
+	Author *CommitAuthor `json:"author,omitempty"`
+	// The person who committed the commit on behalf of the author.
+	Committer *CommitAuthor `json:"committer,omitempty"`
+	// The SHA of the commit in the pull request.
+	SHA *string `json:"sha,omitempty"`
+	// The commit message.
+	Message *string `json:"message,omitempty"`
+
 	// Event identifies the actual type of Event that occurred. Possible values
 	// are:
 	//
@@ -111,6 +123,8 @@ type Timeline struct {
 	// The User object which was assigned to (or unassigned from) this Issue or
 	// Pull Request. Only provided for 'assigned' and 'unassigned' events.
 	Assignee *User `json:"assignee,omitempty"`
+	Assigner *User `json:"assigner,omitempty"`
+
 	// The Milestone object including a 'title' attribute.
 	// Only provided for 'milestoned' and 'demilestoned' events.
 	Milestone *Milestone `json:"milestone,omitempty"`
@@ -125,6 +139,15 @@ type Timeline struct {
 	// 'changes_requested' or 'approved'.
 	// Only provided for 'reviewed' events.
 	State *string `json:"state,omitempty"`
+
+	// The person requested to review the pull request.
+	Reviewer *User `json:"requested_reviewer,omitempty"`
+	// The person who requested a review.
+	Requester *User `json:"review_requester,omitempty"`
+
+	// The review summary text.
+	Body        *string    `json:"body,omitempty"`
+	SubmittedAt *time.Time `json:"submitted_at,omitempty"`
 }
 
 // Source represents a reference's source.
diff --git a/github/repos.go b/github/repos.go
index 391f3139..45c46180 100644
--- a/github/repos.go
+++ b/github/repos.go
@@ -1073,6 +1073,34 @@ func (s *RepositoriesService) getBranchFromURL(ctx context.Context, u string, fo
 	return resp, err
 }
 
+// renameBranchRequest represents a request to rename a branch.
+type renameBranchRequest struct {
+	NewName string `json:"new_name"`
+}
+
+// RenameBranch renames a branch in a repository.
+//
+// To rename a non-default branch: Users must have push access. GitHub Apps must have the `contents:write` repository permission.
+// To rename the default branch: Users must have admin or owner permissions. GitHub Apps must have the `administration:write` repository permission.
+//
+// GitHub API docs: https://docs.github.com/en/rest/reference/repos#rename-a-branch
+func (s *RepositoriesService) RenameBranch(ctx context.Context, owner, repo, branch, newName string) (*Branch, *Response, error) {
+	u := fmt.Sprintf("repos/%v/%v/branches/%v/rename", owner, repo, branch)
+	r := &renameBranchRequest{NewName: newName}
+	req, err := s.client.NewRequest("POST", u, r)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	b := new(Branch)
+	resp, err := s.client.Do(ctx, req, b)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return b, resp, nil
+}
+
 // GetBranchProtection gets the protection of a given branch.
 //
 // GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/repos/#get-branch-protection
diff --git a/scrape/go.mod b/scrape/go.mod
index 2bf7b812..16e3e3df 100644
--- a/scrape/go.mod
+++ b/scrape/go.mod
@@ -3,9 +3,9 @@ module github.com/google/go-github/scrape
 go 1.13
 
 require (
-	github.com/PuerkitoBio/goquery v1.7.1
+	github.com/PuerkitoBio/goquery v1.8.0
 	github.com/google/go-cmp v0.5.6
 	github.com/google/go-github/v39 v39.2.0
 	github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119
-	golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d
+	golang.org/x/net v0.0.0-20210916014120-12bc252f5db8
 )
diff --git a/scrape/go.sum b/scrape/go.sum
index 89953874..c156c104 100644
--- a/scrape/go.sum
+++ b/scrape/go.sum
@@ -1,7 +1,7 @@
-github.com/PuerkitoBio/goquery v1.7.1 h1:oE+T06D+1T7LNrn91B4aERsRIeCLJ/oPSa6xB9FPnz4=
-github.com/PuerkitoBio/goquery v1.7.1/go.mod h1:XY0pP4kfraEmmV1O7Uf6XyjoslwsneBbgeDjLYuN8xY=
-github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE=
-github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY=
+github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
+github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
+github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
+github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -16,12 +16,10 @@ github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119/go.mod h1:/nuTSlK+okRfR/
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
-golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk=
+golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=


Test patch:
diff --git a/github/code-scanning_test.go b/github/code-scanning_test.go
index 7f8a6460..e96d0a0d 100644
--- a/github/code-scanning_test.go
+++ b/github/code-scanning_test.go
@@ -7,6 +7,7 @@ package github
 
 import (
 	"context"
+	"encoding/json"
 	"fmt"
 	"net/http"
 	"testing"
@@ -53,6 +54,41 @@ func TestActionsService_Alert_ID(t *testing.T) {
 	}
 }
 
+func TestActionsService_UploadSarif(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/code-scanning/sarifs", func(w http.ResponseWriter, r *http.Request) {
+		v := new(SarifAnalysis)
+		json.NewDecoder(r.Body).Decode(v)
+		testMethod(t, r, "POST")
+		want := &SarifAnalysis{CommitSHA: String("abc"), Ref: String("ref/head/main"), Sarif: String("abc"), CheckoutURI: String("uri"), StartedAt: &Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)}, ToolName: String("codeql-cli")}
+		if !cmp.Equal(v, want) {
+			t.Errorf("Request body = %+v, want %+v", v, want)
+		}
+
+		fmt.Fprint(w, `{"commit_sha":"abc","ref":"ref/head/main","sarif":"abc"}`)
+	})
+
+	ctx := context.Background()
+	sarifAnalysis := &SarifAnalysis{CommitSHA: String("abc"), Ref: String("ref/head/main"), Sarif: String("abc"), CheckoutURI: String("uri"), StartedAt: &Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)}, ToolName: String("codeql-cli")}
+	_, _, err := client.CodeScanning.UploadSarif(ctx, "o", "r", sarifAnalysis)
+	if err != nil {
+		t.Errorf("CodeScanning.UploadSarif returned error: %v", err)
+	}
+
+	const methodName = "UploadSarif"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.CodeScanning.UploadSarif(ctx, "\n", "\n", sarifAnalysis)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		_, resp, err := client.CodeScanning.UploadSarif(ctx, "o", "r", sarifAnalysis)
+		return resp, err
+	})
+}
+
 func TestActionsService_ListAlertsForRepo(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
@@ -480,3 +516,17 @@ func TestTool_Marshal(t *testing.T) {
 
 	testJSONMarshal(t, u, want)
 }
+
+func TestMessage_Marshal(t *testing.T) {
+	testJSONMarshal(t, &Message{}, "{}")
+
+	u := &Message{
+		Text: String("text"),
+	}
+
+	want := `{
+		"text": "text"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index f6af9991..61b32451 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -17709,6 +17709,86 @@ func TestRunnerLabels_GetType(tt *testing.T) {
 	r.GetType()
 }
 
+func TestSarifAnalysis_GetCheckoutURI(tt *testing.T) {
+	var zeroValue string
+	s := &SarifAnalysis{CheckoutURI: &zeroValue}
+	s.GetCheckoutURI()
+	s = &SarifAnalysis{}
+	s.GetCheckoutURI()
+	s = nil
+	s.GetCheckoutURI()
+}
+
+func TestSarifAnalysis_GetCommitSHA(tt *testing.T) {
+	var zeroValue string
+	s := &SarifAnalysis{CommitSHA: &zeroValue}
+	s.GetCommitSHA()
+	s = &SarifAnalysis{}
+	s.GetCommitSHA()
+	s = nil
+	s.GetCommitSHA()
+}
+
+func TestSarifAnalysis_GetRef(tt *testing.T) {
+	var zeroValue string
+	s := &SarifAnalysis{Ref: &zeroValue}
+	s.GetRef()
+	s = &SarifAnalysis{}
+	s.GetRef()
+	s = nil
+	s.GetRef()
+}
+
+func TestSarifAnalysis_GetSarif(tt *testing.T) {
+	var zeroValue string
+	s := &SarifAnalysis{Sarif: &zeroValue}
+	s.GetSarif()
+	s = &SarifAnalysis{}
+	s.GetSarif()
+	s = nil
+	s.GetSarif()
+}
+
+func TestSarifAnalysis_GetStartedAt(tt *testing.T) {
+	var zeroValue Timestamp
+	s := &SarifAnalysis{StartedAt: &zeroValue}
+	s.GetStartedAt()
+	s = &SarifAnalysis{}
+	s.GetStartedAt()
+	s = nil
+	s.GetStartedAt()
+}
+
+func TestSarifAnalysis_GetToolName(tt *testing.T) {
+	var zeroValue string
+	s := &SarifAnalysis{ToolName: &zeroValue}
+	s.GetToolName()
+	s = &SarifAnalysis{}
+	s.GetToolName()
+	s = nil
+	s.GetToolName()
+}
+
+func TestSarifID_GetID(tt *testing.T) {
+	var zeroValue string
+	s := &SarifID{ID: &zeroValue}
+	s.GetID()
+	s = &SarifID{}
+	s.GetID()
+	s = nil
+	s.GetID()
+}
+
+func TestSarifID_GetURL(tt *testing.T) {
+	var zeroValue string
+	s := &SarifID{URL: &zeroValue}
+	s.GetURL()
+	s = &SarifID{}
+	s.GetURL()
+	s = nil
+	s.GetURL()
+}
+
 func TestSCIMUserAttributes_GetActive(tt *testing.T) {
 	var zeroValue bool
 	s := &SCIMUserAttributes{Active: &zeroValue}
@@ -19119,6 +19199,30 @@ func TestTimeline_GetAssignee(tt *testing.T) {
 	t.GetAssignee()
 }
 
+func TestTimeline_GetAssigner(tt *testing.T) {
+	t := &Timeline{}
+	t.GetAssigner()
+	t = nil
+	t.GetAssigner()
+}
+
+func TestTimeline_GetAuthor(tt *testing.T) {
+	t := &Timeline{}
+	t.GetAuthor()
+	t = nil
+	t.GetAuthor()
+}
+
+func TestTimeline_GetBody(tt *testing.T) {
+	var zeroValue string
+	t := &Timeline{Body: &zeroValue}
+	t.GetBody()
+	t = &Timeline{}
+	t.GetBody()
+	t = nil
+	t.GetBody()
+}
+
 func TestTimeline_GetCommitID(tt *testing.T) {
 	var zeroValue string
 	t := &Timeline{CommitID: &zeroValue}
@@ -19129,6 +19233,13 @@ func TestTimeline_GetCommitID(tt *testing.T) {
 	t.GetCommitID()
 }
 
+func TestTimeline_GetCommitter(tt *testing.T) {
+	t := &Timeline{}
+	t.GetCommitter()
+	t = nil
+	t.GetCommitter()
+}
+
 func TestTimeline_GetCommitURL(tt *testing.T) {
 	var zeroValue string
 	t := &Timeline{CommitURL: &zeroValue}
@@ -19176,6 +19287,16 @@ func TestTimeline_GetLabel(tt *testing.T) {
 	t.GetLabel()
 }
 
+func TestTimeline_GetMessage(tt *testing.T) {
+	var zeroValue string
+	t := &Timeline{Message: &zeroValue}
+	t.GetMessage()
+	t = &Timeline{}
+	t.GetMessage()
+	t = nil
+	t.GetMessage()
+}
+
 func TestTimeline_GetMilestone(tt *testing.T) {
 	t := &Timeline{}
 	t.GetMilestone()
@@ -19197,6 +19318,30 @@ func TestTimeline_GetRename(tt *testing.T) {
 	t.GetRename()
 }
 
+func TestTimeline_GetRequester(tt *testing.T) {
+	t := &Timeline{}
+	t.GetRequester()
+	t = nil
+	t.GetRequester()
+}
+
+func TestTimeline_GetReviewer(tt *testing.T) {
+	t := &Timeline{}
+	t.GetReviewer()
+	t = nil
+	t.GetReviewer()
+}
+
+func TestTimeline_GetSHA(tt *testing.T) {
+	var zeroValue string
+	t := &Timeline{SHA: &zeroValue}
+	t.GetSHA()
+	t = &Timeline{}
+	t.GetSHA()
+	t = nil
+	t.GetSHA()
+}
+
 func TestTimeline_GetSource(tt *testing.T) {
 	t := &Timeline{}
 	t.GetSource()
@@ -19214,6 +19359,16 @@ func TestTimeline_GetState(tt *testing.T) {
 	t.GetState()
 }
 
+func TestTimeline_GetSubmittedAt(tt *testing.T) {
+	var zeroValue time.Time
+	t := &Timeline{SubmittedAt: &zeroValue}
+	t.GetSubmittedAt()
+	t = &Timeline{}
+	t.GetSubmittedAt()
+	t = nil
+	t.GetSubmittedAt()
+}
+
 func TestTimeline_GetURL(tt *testing.T) {
 	var zeroValue string
 	t := &Timeline{URL: &zeroValue}
@@ -19224,6 +19379,13 @@ func TestTimeline_GetURL(tt *testing.T) {
 	t.GetURL()
 }
 
+func TestTimeline_GetUser(tt *testing.T) {
+	t := &Timeline{}
+	t.GetUser()
+	t = nil
+	t.GetUser()
+}
+
 func TestTool_GetGUID(tt *testing.T) {
 	var zeroValue string
 	t := &Tool{GUID: &zeroValue}
diff --git a/github/issues_test.go b/github/issues_test.go
index 80bfb3ff..3ed7390f 100644
--- a/github/issues_test.go
+++ b/github/issues_test.go
@@ -345,6 +345,40 @@ func TestIssuesService_Edit(t *testing.T) {
 	})
 }
 
+func TestIssuesService_RemoveMilestone(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+	mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "PATCH")
+		fmt.Fprint(w, `{"number":1}`)
+	})
+
+	ctx := context.Background()
+	issue, _, err := client.Issues.RemoveMilestone(ctx, "o", "r", 1)
+	if err != nil {
+		t.Errorf("Issues.RemoveMilestone returned error: %v", err)
+	}
+
+	want := &Issue{Number: Int(1)}
+	if !cmp.Equal(issue, want) {
+		t.Errorf("Issues.RemoveMilestone returned %+v, want %+v", issue, want)
+	}
+
+	const methodName = "RemoveMilestone"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Issues.RemoveMilestone(ctx, "\n", "\n", -1)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Issues.RemoveMilestone(ctx, "o", "r", 1)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
 func TestIssuesService_Edit_invalidOwner(t *testing.T) {
 	client, _, _, teardown := setup()
 	defer teardown()
diff --git a/github/repos_environments_test.go b/github/repos_environments_test.go
index 4165ad75..18a30ad6 100644
--- a/github/repos_environments_test.go
+++ b/github/repos_environments_test.go
@@ -326,3 +326,97 @@ func TestRepoEnvironment_Marshal(t *testing.T) {
 
 	testJSONMarshal(t, repoEnv, want)
 }
+
+func TestEnvReviewers_Marshal(t *testing.T) {
+	testJSONMarshal(t, &EnvReviewers{}, "{}")
+
+	repoEnv := &EnvReviewers{
+		Type: String("main"),
+		ID:   Int64(1),
+	}
+
+	want := `{
+		"type":"main",
+		"id":1
+	}`
+
+	testJSONMarshal(t, repoEnv, want)
+}
+
+func TestEnvironment_Marshal(t *testing.T) {
+	testJSONMarshal(t, &Environment{}, "{}")
+
+	repoEnv := &Environment{
+		Owner:           String("o"),
+		Repo:            String("r"),
+		EnvironmentName: String("e"),
+		WaitTimer:       Int(123),
+		Reviewers: []*EnvReviewers{
+			{
+				Type: String("main"),
+				ID:   Int64(1),
+			},
+			{
+				Type: String("rev"),
+				ID:   Int64(2),
+			},
+		},
+		DeploymentBranchPolicy: &BranchPolicy{
+			ProtectedBranches:    Bool(false),
+			CustomBranchPolicies: Bool(false),
+		},
+		ID:        Int64(2),
+		NodeID:    String("star"),
+		Name:      String("eg"),
+		URL:       String("https://hey.in"),
+		HTMLURL:   String("htmlurl"),
+		CreatedAt: &Timestamp{referenceTime},
+		UpdatedAt: &Timestamp{referenceTime},
+		ProtectionRules: []*ProtectionRule{
+			{
+				ID:        Int64(21),
+				NodeID:    String("mnb"),
+				Type:      String("ewq"),
+				WaitTimer: Int(9090),
+			},
+		},
+	}
+
+	want := `{
+		"owner":"o",
+		"repo":"r",
+		"environment_name":"e",
+		"wait_timer":123,
+		"reviewers":[
+			{
+				"type":"main",
+				"id":1
+			},
+			{
+				"type":"rev",
+				"id":2
+			}
+		],
+		"deployment_branch_policy":{
+			"protected_branches":false,
+			"custom_branch_policies":false
+		},
+		"id":2,
+		"node_id":"star",
+		"name":"eg",
+		"url":"https://hey.in",
+		"html_url":"htmlurl",
+		"created_at":` + referenceTimeStr + `,
+		"updated_at":` + referenceTimeStr + `,
+		"protection_rules":[
+			{
+				"id":21,
+				"node_id":"mnb",
+				"type":"ewq",
+				"wait_timer":9090
+			}
+		]
+	}`
+
+	testJSONMarshal(t, repoEnv, want)
+}
diff --git a/github/repos_releases_test.go b/github/repos_releases_test.go
index b67c83a0..bebbc0ed 100644
--- a/github/repos_releases_test.go
+++ b/github/repos_releases_test.go
@@ -820,3 +820,21 @@ func TestRepositoryRelease_Marshal(t *testing.T) {
 
 	testJSONMarshal(t, u, want)
 }
+
+func TestGenerateNotesOptions_Marshal(t *testing.T) {
+	testJSONMarshal(t, &GenerateNotesOptions{}, "{}")
+
+	u := &GenerateNotesOptions{
+		TagName:         "tag_name",
+		PreviousTagName: String("previous_tag_name"),
+		TargetCommitish: String("target_commitish"),
+	}
+
+	want := `{
+		"tag_name":          "tag_name",
+		"previous_tag_name": "previous_tag_name",
+		"target_commitish":  "target_commitish"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/repos_test.go b/github/repos_test.go
index dee5fdf8..dae6d221 100644
--- a/github/repos_test.go
+++ b/github/repos_test.go
@@ -981,6 +981,51 @@ func TestRepositoriesService_GetBranch_notFound(t *testing.T) {
 	})
 }
 
+func TestRepositoriesService_RenameBranch(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	renameBranchReq := "nn"
+
+	mux.HandleFunc("/repos/o/r/branches/b/rename", func(w http.ResponseWriter, r *http.Request) {
+		v := new(renameBranchRequest)
+		json.NewDecoder(r.Body).Decode(v)
+
+		testMethod(t, r, "POST")
+		want := &renameBranchRequest{NewName: "nn"}
+		if !cmp.Equal(v, want) {
+			t.Errorf("Request body = %+v, want %+v", v, want)
+		}
+
+		fmt.Fprint(w, `{"protected":true,"name":"nn"}`)
+	})
+
+	ctx := context.Background()
+	got, _, err := client.Repositories.RenameBranch(ctx, "o", "r", "b", renameBranchReq)
+	if err != nil {
+		t.Errorf("Repositories.RenameBranch returned error: %v", err)
+	}
+
+	want := &Branch{Name: String("nn"), Protected: Bool(true)}
+	if !cmp.Equal(got, want) {
+		t.Errorf("Repositories.RenameBranch returned %+v, want %+v", got, want)
+	}
+
+	const methodName = "RenameBranch"
+	testBadOptions(t, methodName, func() (err error) {
+		_, _, err = client.Repositories.RenameBranch(ctx, "\n", "\n", "\n", renameBranchReq)
+		return err
+	})
+
+	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
+		got, resp, err := client.Repositories.RenameBranch(ctx, "o", "r", "b", renameBranchReq)
+		if got != nil {
+			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
+		}
+		return resp, err
+	})
+}
+
 func TestRepositoriesService_GetBranchProtection(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
@@ -2353,3 +2398,17 @@ func TestRepositoriesService_Dispatch(t *testing.T) {
 		return resp, err
 	})
 }
+
+func TestAdvancedSecurity_Marshal(t *testing.T) {
+	testJSONMarshal(t, &AdvancedSecurity{}, "{}")
+
+	u := &AdvancedSecurity{
+		Status: String("status"),
+	}
+
+	want := `{
+		"status": "status"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/repos_traffic_test.go b/github/repos_traffic_test.go
index adb4dfb3..88046b7b 100644
--- a/github/repos_traffic_test.go
+++ b/github/repos_traffic_test.go
@@ -218,3 +218,93 @@ func TestTrafficReferrer_Marshal(t *testing.T) {
 
 	testJSONMarshal(t, u, want)
 }
+
+func TestTrafficViews_Marshal(t *testing.T) {
+	testJSONMarshal(t, &TrafficViews{}, "{}")
+
+	u := &TrafficViews{
+		Views: []*TrafficData{{
+			Timestamp: &Timestamp{time.Date(2016, time.May, 31, 16, 0, 0, 0, time.UTC)},
+			Count:     Int(7),
+			Uniques:   Int(6),
+		}},
+		Count:   Int(0),
+		Uniques: Int(0),
+	}
+
+	want := `{
+		"views": [{
+			"timestamp": "2016-05-31T16:00:00.000Z",
+			"count": 7,
+			"uniques": 6
+		}],
+		"count" : 0,
+		"uniques" : 0
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestTrafficClones_Marshal(t *testing.T) {
+	testJSONMarshal(t, &TrafficClones{}, "{}")
+
+	u := &TrafficClones{
+		Clones: []*TrafficData{{
+			Timestamp: &Timestamp{time.Date(2021, time.October, 29, 16, 0, 0, 0, time.UTC)},
+			Count:     Int(1),
+			Uniques:   Int(1),
+		}},
+		Count:   Int(0),
+		Uniques: Int(0),
+	}
+
+	want := `{
+		"clones": [{
+			"timestamp": "2021-10-29T16:00:00.000Z",
+			"count": 1,
+			"uniques": 1
+		}],
+		"count" : 0,
+		"uniques" : 0
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestTrafficPath_Marshal(t *testing.T) {
+	testJSONMarshal(t, &TrafficPath{}, "{}")
+
+	u := &TrafficPath{
+		Path:    String("test/path"),
+		Title:   String("test"),
+		Count:   Int(2),
+		Uniques: Int(3),
+	}
+
+	want := `{
+		"path" : "test/path",
+		"title": "test",
+		"count": 2,
+		"uniques": 3
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestTrafficData_Marshal(t *testing.T) {
+	testJSONMarshal(t, &TrafficData{}, "{}")
+
+	u := &TrafficData{
+		Timestamp: &Timestamp{time.Date(2016, time.May, 31, 16, 0, 0, 0, time.UTC)},
+		Count:     Int(7),
+		Uniques:   Int(6),
+	}
+
+	want := `{	
+			"timestamp": "2016-05-31T16:00:00.000Z",
+			"count": 7,
+			"uniques": 6
+      }`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/scim_test.go b/github/scim_test.go
index 3338a28a..328bc209 100644
--- a/github/scim_test.go
+++ b/github/scim_test.go
@@ -192,3 +192,51 @@ func TestSCIMService_DeleteSCIMUserFromOrg(t *testing.T) {
 		return client.SCIM.DeleteSCIMUserFromOrg(ctx, "o", "123")
 	})
 }
+
+func TestSCIMUserAttributes_Marshal(t *testing.T) {
+	testJSONMarshal(t, &SCIMUserAttributes{}, `{
+		"userName":"","name":{"givenName":"","familyName":""},"emails":null
+	}`)
+
+	u := &SCIMUserAttributes{
+		UserName: "userName1",
+		Name: SCIMUserName{
+			GivenName:  "Name1",
+			FamilyName: "Fname",
+			Formatted:  String("formatted name"),
+		},
+		DisplayName: String("Name"),
+		Emails: []*SCIMUserEmail{
+			{
+				Value:   "value",
+				Primary: Bool(false),
+				Type:    String("type"),
+			},
+		},
+		Schemas:    []string{"schema1"},
+		ExternalID: String("id"),
+		Groups:     []string{"group1"},
+		Active:     Bool(true),
+	}
+
+	want := `{
+		"userName": "userName1",
+		"name": {
+			"givenName": "Name1",
+			"familyName": "Fname",
+			"formatted": "formatted name"
+		},
+		"displayName": "Name",
+		"emails": [{
+			"value": "value",
+			"primary": false,
+			"type": "type"
+		}],
+		"schemas": ["schema1"],
+		"externalId": "id",
+		"groups": ["group1"],
+		"active": true
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/search_test.go b/github/search_test.go
index f5cdd068..4562fcf5 100644
--- a/github/search_test.go
+++ b/github/search_test.go
@@ -575,3 +575,224 @@ func TestRepositoriesSearchResult_Marshal(t *testing.T) {
 
 	testJSONMarshal(t, u, want)
 }
+
+func TestCommitsSearchResult_Marshal(t *testing.T) {
+	testJSONMarshal(t, &CommitsSearchResult{}, "{}")
+
+	c := &CommitsSearchResult{
+		Total:             Int(0),
+		IncompleteResults: Bool(true),
+		Commits: []*CommitResult{{
+			SHA: String("s"),
+		}},
+	}
+
+	want := `{
+		"total_count" : 0,
+		"incomplete_results" : true,
+		"items" : [{"sha" : "s"}]
+	}`
+
+	testJSONMarshal(t, c, want)
+}
+
+func TestTopicsSearchResult_Marshal(t *testing.T) {
+	testJSONMarshal(t, &TopicsSearchResult{}, "{}")
+
+	u := &TopicsSearchResult{
+		Total:             Int(2),
+		IncompleteResults: Bool(false),
+		Topics: []*TopicResult{
+			{
+				Name:             String("t1"),
+				DisplayName:      String("tt"),
+				ShortDescription: String("t desc"),
+				Description:      String("desc"),
+				CreatedBy:        String("mi"),
+				CreatedAt:        &Timestamp{referenceTime},
+				UpdatedAt:        String("2006-01-02T15:04:05Z"),
+				Featured:         Bool(true),
+				Curated:          Bool(true),
+				Score:            Float64(123),
+			},
+		},
+	}
+
+	want := `{
+		"total_count" : 2,
+		"incomplete_results" : false,
+		"items" : [
+			{
+				"name" : "t1",
+				"display_name":"tt",
+				"short_description":"t desc",
+				"description":"desc",
+				"created_by":"mi",
+				"created_at":` + referenceTimeStr + `,
+				"updated_at":"2006-01-02T15:04:05Z",
+				"featured":true,
+				"curated":true,
+				"score":123
+			}
+		]
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestLabelResult_Marshal(t *testing.T) {
+	testJSONMarshal(t, &LabelResult{}, "{}")
+
+	u := &LabelResult{
+		ID:          Int64(11),
+		URL:         String("url"),
+		Name:        String("label"),
+		Color:       String("green"),
+		Default:     Bool(true),
+		Description: String("desc"),
+		Score:       Float64(123),
+	}
+
+	want := `{
+		"id":11,
+		"url":"url",
+		"name":"label",
+		"color":"green",
+		"default":true,
+		"description":"desc",
+		"score":123
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestSearchOptions_Marshal(t *testing.T) {
+	testJSONMarshal(t, &SearchOptions{}, "{}")
+
+	u := &SearchOptions{
+		Sort:      "author-date",
+		Order:     "asc",
+		TextMatch: false,
+		ListOptions: ListOptions{
+			Page:    int(1),
+			PerPage: int(10),
+		},
+	}
+
+	want := `{	
+		"sort": "author-date",
+		"order": "asc",
+		"page": 1,
+		"perPage": 10
+      }`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestIssuesSearchResult_Marshal(t *testing.T) {
+	testJSONMarshal(t, &IssuesSearchResult{}, "{}")
+
+	u := &IssuesSearchResult{
+		Total:             Int(48),
+		IncompleteResults: Bool(false),
+		Issues: []*Issue{
+			{
+				ID:                Int64(1),
+				Number:            Int(1),
+				State:             String("s"),
+				Locked:            Bool(false),
+				Title:             String("title"),
+				Body:              String("body"),
+				AuthorAssociation: String("aa"),
+				User:              &User{ID: Int64(1)},
+				Labels:            []*Label{{ID: Int64(1)}},
+				Assignee:          &User{ID: Int64(1)},
+				Comments:          Int(1),
+				ClosedAt:          &referenceTime,
+				CreatedAt:         &referenceTime,
+				UpdatedAt:         &referenceTime,
+				ClosedBy:          &User{ID: Int64(1)},
+				URL:               String("url"),
+				HTMLURL:           String("hurl"),
+				CommentsURL:       String("curl"),
+				EventsURL:         String("eurl"),
+				LabelsURL:         String("lurl"),
+				RepositoryURL:     String("rurl"),
+				Milestone:         &Milestone{ID: Int64(1)},
+				PullRequestLinks:  &PullRequestLinks{URL: String("url")},
+				Repository:        &Repository{ID: Int64(1)},
+				Reactions:         &Reactions{TotalCount: Int(1)},
+				Assignees:         []*User{{ID: Int64(1)}},
+				NodeID:            String("nid"),
+				TextMatches:       []*TextMatch{{ObjectURL: String("ourl")}},
+				ActiveLockReason:  String("alr"),
+			},
+		},
+	}
+
+	want := `{
+		"total_count": 48,
+		"incomplete_results": false,
+		"items": [
+			{
+				"id": 1,
+				"number": 1,
+				"state": "s",
+				"locked": false,
+				"title": "title",
+				"body": "body",
+				"author_association": "aa",
+				"user": {
+					"id": 1
+				},
+				"labels": [
+					{
+						"id": 1
+					}
+				],
+				"assignee": {
+					"id": 1
+				},
+				"comments": 1,
+				"closed_at": ` + referenceTimeStr + `,
+				"created_at": ` + referenceTimeStr + `,
+				"updated_at": ` + referenceTimeStr + `,
+				"closed_by": {
+					"id": 1
+				},
+				"url": "url",
+				"html_url": "hurl",
+				"comments_url": "curl",
+				"events_url": "eurl",
+				"labels_url": "lurl",
+				"repository_url": "rurl",
+				"milestone": {
+					"id": 1
+				},
+				"pull_request": {
+					"url": "url"
+				},
+				"repository": {
+					"id": 1
+				},
+				"reactions": {
+					"total_count": 1
+				},
+				"assignees": [
+					{
+						"id": 1
+					}
+				],
+				"node_id": "nid",
+				"text_matches": [
+					{
+						"object_url": "ourl"
+					}
+				],
+				"active_lock_reason": "alr"
+			}
+		]
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/teams_discussion_comments_test.go b/github/teams_discussion_comments_test.go
index 3f911650..f6b5433b 100644
--- a/github/teams_discussion_comments_test.go
+++ b/github/teams_discussion_comments_test.go
@@ -431,3 +431,63 @@ func TestTeamsService_DeleteComment(t *testing.T) {
 		return resp, err
 	})
 }
+
+func TestDiscussionComment_Marshal(t *testing.T) {
+	testJSONMarshal(t, &DiscussionComment{}, "{}")
+
+	u := &DiscussionComment{
+		Author:        &User{},
+		Body:          String("body"),
+		BodyHTML:      String("body html"),
+		BodyVersion:   String("body version"),
+		CreatedAt:     &Timestamp{referenceTime},
+		LastEditedAt:  &Timestamp{referenceTime},
+		DiscussionURL: String("url"),
+		HTMLURL:       String("html url"),
+		NodeID:        String("node"),
+		Number:        Int(1),
+		UpdatedAt:     &Timestamp{referenceTime},
+		URL:           String("url"),
+		Reactions: &Reactions{
+			TotalCount: Int(10),
+			PlusOne:    Int(1),
+			MinusOne:   Int(1),
+			Laugh:      Int(1),
+			Confused:   Int(1),
+			Heart:      Int(2),
+			Hooray:     Int(5),
+			Rocket:     Int(3),
+			Eyes:       Int(9),
+			URL:        String("url"),
+		},
+	}
+
+	want := `{
+		"author":{},
+		"body":"body",
+		"body_html":"body html",
+		"body_version":"body version",
+		"created_at":` + referenceTimeStr + `,
+		"last_edited_at":` + referenceTimeStr + `,
+		"discussion_url":"url",
+		"html_url":"html url",
+		"node_id":"node",
+		"number":1,
+		"updated_at":` + referenceTimeStr + `,
+		"url":"url",
+		"reactions":{
+			"total_count": 10,
+			"+1": 1,
+			"-1": 1,
+			"laugh": 1,
+			"confused": 1,
+			"heart": 2,
+			"hooray": 5,
+			"rocket": 3,
+			"eyes": 9,
+			"url":"url"
+		}
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/teams_discussions_test.go b/github/teams_discussions_test.go
index 3c94f566..06328acb 100644
--- a/github/teams_discussions_test.go
+++ b/github/teams_discussions_test.go
@@ -541,3 +541,47 @@ func TestTeamsService_DeleteDiscussionBySlug(t *testing.T) {
 		return client.Teams.DeleteDiscussionBySlug(ctx, "o", "s", 3)
 	})
 }
+
+func TestTeamDiscussion_Marshal(t *testing.T) {
+	testJSONMarshal(t, &TeamDiscussion{}, "{}")
+
+	u := &TeamDiscussion{
+		Author: &User{
+			Login:       String("author"),
+			ID:          Int64(0),
+			URL:         String("https://api.github.com/users/author"),
+			AvatarURL:   String("https://avatars1.githubusercontent.com/u/0?v=4"),
+			GravatarID:  String(""),
+			CreatedAt:   &Timestamp{referenceTime},
+			SuspendedAt: &Timestamp{referenceTime},
+		},
+		Body:          String("test"),
+		BodyHTML:      String("<p>test</p>"),
+		BodyVersion:   String("version"),
+		CommentsCount: Int(1),
+		CommentsURL:   String("https://api.github.com/teams/2/discussions/3/comments"),
+		CreatedAt:     &Timestamp{referenceTime},
+		LastEditedAt:  &Timestamp{referenceTime},
+	}
+
+	want := `{
+		"author": {
+			"login": "author",
+			"id": 0,
+			"avatar_url": "https://avatars1.githubusercontent.com/u/0?v=4",
+			"gravatar_id": "",
+			"url": "https://api.github.com/users/author",
+			"created_at": ` + referenceTimeStr + `,
+			"suspended_at": ` + referenceTimeStr + `	
+		},
+		"body": "test",
+		"body_html": "<p>test</p>",
+		"body_version": "version",
+		"comments_count": 1,
+		"comments_url": "https://api.github.com/teams/2/discussions/3/comments",
+		"created_at": ` + referenceTimeStr + `,
+		"last_edited_at": ` + referenceTimeStr + `
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/teams_test.go b/github/teams_test.go
index f71fb8b3..26cf2c03 100644
--- a/github/teams_test.go
+++ b/github/teams_test.go
@@ -1699,3 +1699,49 @@ func TestTeams_Marshal(t *testing.T) {
 
 	testJSONMarshal(t, u, want)
 }
+
+func TestInvitation_Marshal(t *testing.T) {
+	testJSONMarshal(t, &Invitation{}, "{}")
+
+	u := &Invitation{
+		ID:                Int64(1),
+		NodeID:            String("test node"),
+		Login:             String("login123"),
+		Email:             String("go@github.com"),
+		Role:              String("developer"),
+		CreatedAt:         &referenceTime,
+		TeamCount:         Int(99),
+		InvitationTeamURL: String("url"),
+	}
+
+	want := `{
+		"id": 1,
+		"node_id": "test node",
+		"login":"login123",
+		"email":"go@github.com",
+		"role":"developer",
+		"created_at":` + referenceTimeStr + `,
+		"team_count":99,
+		"invitation_team_url":"url"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
+
+func TestIDPGroup_Marshal(t *testing.T) {
+	testJSONMarshal(t, &IDPGroup{}, "{}")
+
+	u := &IDPGroup{
+		GroupID:          String("abc1"),
+		GroupName:        String("test group"),
+		GroupDescription: String("test group descripation"),
+	}
+
+	want := `{
+		"group_id": "abc1",
+		"group_name": "test group",
+		"group_description":"test group descripation"
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/users_gpg_keys_test.go b/github/users_gpg_keys_test.go
index 9f7bb9e4..2cef7522 100644
--- a/github/users_gpg_keys_test.go
+++ b/github/users_gpg_keys_test.go
@@ -11,6 +11,7 @@ import (
 	"fmt"
 	"net/http"
 	"testing"
+	"time"
 
 	"github.com/google/go-cmp/cmp"
 )
@@ -205,3 +206,57 @@ func TestGPGEmail_Marshal(t *testing.T) {
 
 	testJSONMarshal(t, u, want)
 }
+
+func TestGPGKey_Marshal(t *testing.T) {
+	testJSONMarshal(t, &GPGKey{}, "{}")
+
+	ti := &time.Time{}
+
+	g := &GPGKey{
+		ID:           Int64(1),
+		PrimaryKeyID: Int64(1),
+		KeyID:        String("someKeyID"),
+		RawKey:       String("someRawKeyID"),
+		PublicKey:    String("somePublicKey"),
+		Emails: []*GPGEmail{
+			{
+				Email:    String("someEmail"),
+				Verified: Bool(true),
+			},
+		},
+		Subkeys: []*GPGKey{
+			{},
+		},
+		CanSign:           Bool(true),
+		CanEncryptComms:   Bool(true),
+		CanEncryptStorage: Bool(true),
+		CanCertify:        Bool(true),
+		CreatedAt:         ti,
+		ExpiresAt:         ti,
+	}
+
+	want := `{
+			"id":1,
+			"primary_key_id":1,
+			"key_id":"someKeyID",
+			"raw_key":"someRawKeyID",
+			"public_key":"somePublicKey",
+			"emails":[
+				{
+					"email":"someEmail",
+					"verified":true
+				}
+			],
+			"subkeys":[
+				{}
+			],
+			"can_sign":true,
+			"can_encrypt_comms":true,
+			"can_encrypt_storage":true,
+			"can_certify":true,
+			"created_at":"0001-01-01T00:00:00Z",
+			"expires_at":"0001-01-01T00:00:00Z"
+		}`
+
+	testJSONMarshal(t, g, want)
+}
diff --git a/github/users_keys_test.go b/github/users_keys_test.go
index 14effc1b..b3e5fc9e 100644
--- a/github/users_keys_test.go
+++ b/github/users_keys_test.go
@@ -180,3 +180,29 @@ func TestUsersService_DeleteKey(t *testing.T) {
 		return client.Users.DeleteKey(ctx, 1)
 	})
 }
+
+func TestKey_Marshal(t *testing.T) {
+	testJSONMarshal(t, &Key{}, "{}")
+
+	u := &Key{
+		ID:        Int64(1),
+		Key:       String("abc"),
+		URL:       String("url"),
+		Title:     String("title"),
+		ReadOnly:  Bool(true),
+		Verified:  Bool(true),
+		CreatedAt: &Timestamp{referenceTime},
+	}
+
+	want := `{
+		"id": 1,
+		"key": "abc",
+		"url": "url",
+		"title": "title",
+		"read_only": true,
+		"verified": true,
+		"created_at": ` + referenceTimeStr + `
+	}`
+
+	testJSONMarshal(t, u, want)
+}
diff --git a/github/users_projects_test.go b/github/users_projects_test.go
index 4f4b5e26..28c46107 100644
--- a/github/users_projects_test.go
+++ b/github/users_projects_test.go
@@ -92,3 +92,19 @@ func TestUsersService_CreateProject(t *testing.T) {
 		return resp, err
 	})
 }
+
+func TestCreateUserProjectOptions_Marshal(t *testing.T) {
+	testJSONMarshal(t, &CreateUserProjectOptions{}, `{}`)
+
+	c := CreateUserProjectOptions{
+		Name: "SomeProject",
+		Body: String("SomeProjectBody"),
+	}
+
+	want := `{
+			"name": "SomeProject",
+			"body": "SomeProjectBody"
+		}`
+
+	testJSONMarshal(t, c, want)
+}
diff --git a/github/users_test.go b/github/users_test.go
index c53672d0..8acd529c 100644
--- a/github/users_test.go
+++ b/github/users_test.go
@@ -383,3 +383,47 @@ func TestUserContext_Marshal(t *testing.T) {
 
 	testJSONMarshal(t, u, want)
 }
+
+func TestHovercard_Marshal(t *testing.T) {
+	testJSONMarshal(t, &Hovercard{}, "{}")
+
+	h := &Hovercard{
+		Contexts: []*UserContext{
+			{
+				Message: String("someMessage"),
+				Octicon: String("someOcticon"),
+			},
+		},
+	}
+
+	want := `{
+		"contexts" : [
+			{
+				"message" : "someMessage",
+				"octicon" : "someOcticon"
+			}
+		]
+	}`
+
+	testJSONMarshal(t, h, want)
+}
+
+func TestUserListOptions_Marshal(t *testing.T) {
+	testJSONMarshal(t, &UserListOptions{}, "{}")
+
+	u := &UserListOptions{
+		Since: int64(1900),
+		ListOptions: ListOptions{
+			Page:    int(1),
+			PerPage: int(10),
+		},
+	}
+
+	want := `{
+		"since" : 1900,
+		"page": 1,
+		"perPage": 10
+	}`
+
+	testJSONMarshal(t, u, want)
+}