From 44226b1bff17eb641375fa56e22d8e6b406111bb Mon Sep 17 00:00:00 2001 From: Lizeth Vera <47796851+veralizeth@users.noreply.github.com> Date: Mon, 26 Oct 2026 16:47:07 -0700 Subject: [PATCH 1/2] Add typed issue artifact REST support Add IssuesService artifact CRUD methods, typed version history, and httptest coverage. Preserve optional update fields and document pagination without Link headers. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- github/gen-iterators.go | 5 + github/gen-stringify-test.go | 4 +- github/github-accessors.go | 208 +++++++++++++++++++ github/github-accessors_test.go | 241 ++++++++++++++++++++++ github/github-stringify_test.go | 38 ++++ github/issues_artifacts.go | 188 +++++++++++++++++ github/issues_artifacts_test.go | 347 ++++++++++++++++++++++++++++++++ openapi_operations.yaml | 5 + 8 files changed, 1035 insertions(+), 1 deletion(-) create mode 100644 github/issues_artifacts.go create mode 100644 github/issues_artifacts_test.go diff --git a/github/gen-iterators.go b/github/gen-iterators.go index 9a062d1d536..82e520d6b3e 100644 --- a/github/gen-iterators.go +++ b/github/gen-iterators.go @@ -325,6 +325,11 @@ func (t *templateData) processMethods(f *ast.File) error { continue } + // Issue artifact lists have no Link headers for the iterator to follow. + if methodKey == "IssuesService.ListArtifacts" { + continue + } + if fd.Type.Results == nil || len(fd.Type.Results.List) != 3 { continue } diff --git a/github/gen-stringify-test.go b/github/gen-stringify-test.go index 26425a161eb..09637486123 100644 --- a/github/gen-stringify-test.go +++ b/github/gen-stringify-test.go @@ -45,7 +45,9 @@ var ( skipStructMethods = map[string]bool{} // skipStructs lists structs to skip. skipStructs = map[string]bool{ - "RateLimits": true, + // Its nested artifact has required value fields; tested manually. + "IssueArtifactWithVersions": true, + "RateLimits": true, } funcMap = template.FuncMap{ diff --git a/github/github-accessors.go b/github/github-accessors.go index d99ad2e5c99..e45435ec115 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -13182,6 +13182,30 @@ func (c *CreateHostedRunnerRequest) GetSize() string { return c.Size } +// GetBody returns the Body field. +func (c *CreateIssueArtifactRequest) GetBody() string { + if c == nil { + return "" + } + return c.Body +} + +// GetName returns the Name field. +func (c *CreateIssueArtifactRequest) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (c *CreateIssueArtifactRequest) GetType() string { + if c == nil || c.Type == nil { + return "" + } + return *c.Type +} + // GetColor returns the Color field if it's non-nil, zero value otherwise. func (c *CreateIssueLabelRequest) GetColor() string { if c == nil || c.Color == nil { @@ -23310,6 +23334,174 @@ func (i *Issue) GetUser() *User { return i.User } +// GetBody returns the Body field. +func (i *IssueArtifact) GetBody() string { + if i == nil { + return "" + } + return i.Body +} + +// GetBodyHTML returns the BodyHTML field if it's non-nil, zero value otherwise. +func (i *IssueArtifact) GetBodyHTML() string { + if i == nil || i.BodyHTML == nil { + return "" + } + return *i.BodyHTML +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *IssueArtifact) GetCreatedAt() Timestamp { + if i == nil || i.CreatedAt == nil { + return Timestamp{} + } + return *i.CreatedAt +} + +// GetCreator returns the Creator field. +func (i *IssueArtifact) GetCreator() *User { + if i == nil { + return nil + } + return i.Creator +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (i *IssueArtifact) GetDescription() string { + if i == nil || i.Description == nil { + return "" + } + return *i.Description +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (i *IssueArtifact) GetHTMLURL() string { + if i == nil || i.HTMLURL == nil { + return "" + } + return *i.HTMLURL +} + +// GetID returns the ID field. +func (i *IssueArtifact) GetID() int64 { + if i == nil { + return 0 + } + return i.ID +} + +// GetName returns the Name field. +func (i *IssueArtifact) GetName() string { + if i == nil { + return "" + } + return i.Name +} + +// GetNumber returns the Number field. +func (i *IssueArtifact) GetNumber() int { + if i == nil { + return 0 + } + return i.Number +} + +// GetType returns the Type field. +func (i *IssueArtifact) GetType() string { + if i == nil { + return "" + } + return i.Type +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (i *IssueArtifact) GetUpdatedAt() Timestamp { + if i == nil || i.UpdatedAt == nil { + return Timestamp{} + } + return *i.UpdatedAt +} + +// GetUpdatedByActor returns the UpdatedByActor field. +func (i *IssueArtifact) GetUpdatedByActor() *User { + if i == nil { + return nil + } + return i.UpdatedByActor +} + +// GetType returns the Type field. +func (i *IssueArtifactListOptions) GetType() string { + if i == nil { + return "" + } + return i.Type +} + +// GetActor returns the Actor field. +func (i *IssueArtifactVersion) GetActor() *User { + if i == nil { + return nil + } + return i.Actor +} + +// GetBody returns the Body field. +func (i *IssueArtifactVersion) GetBody() string { + if i == nil { + return "" + } + return i.Body +} + +// GetBodyHTML returns the BodyHTML field if it's non-nil, zero value otherwise. +func (i *IssueArtifactVersion) GetBodyHTML() string { + if i == nil || i.BodyHTML == nil { + return "" + } + return *i.BodyHTML +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *IssueArtifactVersion) GetCreatedAt() Timestamp { + if i == nil || i.CreatedAt == nil { + return Timestamp{} + } + return *i.CreatedAt +} + +// GetName returns the Name field. +func (i *IssueArtifactVersion) GetName() string { + if i == nil { + return "" + } + return i.Name +} + +// GetVersion returns the Version field. +func (i *IssueArtifactVersion) GetVersion() int { + if i == nil { + return 0 + } + return i.Version +} + +// GetArtifact returns the Artifact field. +func (i *IssueArtifactWithVersions) GetArtifact() *IssueArtifact { + if i == nil { + return nil + } + return i.Artifact +} + +// GetVersions returns the Versions slice if it's non-nil, nil otherwise. +func (i *IssueArtifactWithVersions) GetVersions() []*IssueArtifactVersion { + if i == nil || i.Versions == nil { + return nil + } + return i.Versions +} + // GetAuthorAssociation returns the AuthorAssociation field if it's non-nil, zero value otherwise. func (i *IssueComment) GetAuthorAssociation() string { if i == nil || i.AuthorAssociation == nil { @@ -47198,6 +47390,22 @@ func (u *UpdateHostedRunnerRequest) GetSize() string { return *u.Size } +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (u *UpdateIssueArtifactRequest) GetBody() string { + if u == nil || u.Body == nil { + return "" + } + return *u.Body +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (u *UpdateIssueArtifactRequest) GetName() string { + if u == nil || u.Name == nil { + return "" + } + return *u.Name +} + // GetColor returns the Color field if it's non-nil, zero value otherwise. func (u *UpdateIssueLabelRequest) GetColor() string { if u == nil || u.Color == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 51b88ad3f3c..8cecaf87cef 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -16567,6 +16567,33 @@ func TestCreateHostedRunnerRequest_GetSize(tt *testing.T) { c.GetSize() } +func TestCreateIssueArtifactRequest_GetBody(tt *testing.T) { + tt.Parallel() + c := &CreateIssueArtifactRequest{} + c.GetBody() + c = nil + c.GetBody() +} + +func TestCreateIssueArtifactRequest_GetName(tt *testing.T) { + tt.Parallel() + c := &CreateIssueArtifactRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateIssueArtifactRequest_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateIssueArtifactRequest{Type: &zeroValue} + c.GetType() + c = &CreateIssueArtifactRequest{} + c.GetType() + c = nil + c.GetType() +} + func TestCreateIssueLabelRequest_GetColor(tt *testing.T) { tt.Parallel() var zeroValue string @@ -29254,6 +29281,198 @@ func TestIssue_GetUser(tt *testing.T) { i.GetUser() } +func TestIssueArtifact_GetBody(tt *testing.T) { + tt.Parallel() + i := &IssueArtifact{} + i.GetBody() + i = nil + i.GetBody() +} + +func TestIssueArtifact_GetBodyHTML(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueArtifact{BodyHTML: &zeroValue} + i.GetBodyHTML() + i = &IssueArtifact{} + i.GetBodyHTML() + i = nil + i.GetBodyHTML() +} + +func TestIssueArtifact_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &IssueArtifact{CreatedAt: &zeroValue} + i.GetCreatedAt() + i = &IssueArtifact{} + i.GetCreatedAt() + i = nil + i.GetCreatedAt() +} + +func TestIssueArtifact_GetCreator(tt *testing.T) { + tt.Parallel() + i := &IssueArtifact{} + i.GetCreator() + i = nil + i.GetCreator() +} + +func TestIssueArtifact_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueArtifact{Description: &zeroValue} + i.GetDescription() + i = &IssueArtifact{} + i.GetDescription() + i = nil + i.GetDescription() +} + +func TestIssueArtifact_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueArtifact{HTMLURL: &zeroValue} + i.GetHTMLURL() + i = &IssueArtifact{} + i.GetHTMLURL() + i = nil + i.GetHTMLURL() +} + +func TestIssueArtifact_GetID(tt *testing.T) { + tt.Parallel() + i := &IssueArtifact{} + i.GetID() + i = nil + i.GetID() +} + +func TestIssueArtifact_GetName(tt *testing.T) { + tt.Parallel() + i := &IssueArtifact{} + i.GetName() + i = nil + i.GetName() +} + +func TestIssueArtifact_GetNumber(tt *testing.T) { + tt.Parallel() + i := &IssueArtifact{} + i.GetNumber() + i = nil + i.GetNumber() +} + +func TestIssueArtifact_GetType(tt *testing.T) { + tt.Parallel() + i := &IssueArtifact{} + i.GetType() + i = nil + i.GetType() +} + +func TestIssueArtifact_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &IssueArtifact{UpdatedAt: &zeroValue} + i.GetUpdatedAt() + i = &IssueArtifact{} + i.GetUpdatedAt() + i = nil + i.GetUpdatedAt() +} + +func TestIssueArtifact_GetUpdatedByActor(tt *testing.T) { + tt.Parallel() + i := &IssueArtifact{} + i.GetUpdatedByActor() + i = nil + i.GetUpdatedByActor() +} + +func TestIssueArtifactListOptions_GetType(tt *testing.T) { + tt.Parallel() + i := &IssueArtifactListOptions{} + i.GetType() + i = nil + i.GetType() +} + +func TestIssueArtifactVersion_GetActor(tt *testing.T) { + tt.Parallel() + i := &IssueArtifactVersion{} + i.GetActor() + i = nil + i.GetActor() +} + +func TestIssueArtifactVersion_GetBody(tt *testing.T) { + tt.Parallel() + i := &IssueArtifactVersion{} + i.GetBody() + i = nil + i.GetBody() +} + +func TestIssueArtifactVersion_GetBodyHTML(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueArtifactVersion{BodyHTML: &zeroValue} + i.GetBodyHTML() + i = &IssueArtifactVersion{} + i.GetBodyHTML() + i = nil + i.GetBodyHTML() +} + +func TestIssueArtifactVersion_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &IssueArtifactVersion{CreatedAt: &zeroValue} + i.GetCreatedAt() + i = &IssueArtifactVersion{} + i.GetCreatedAt() + i = nil + i.GetCreatedAt() +} + +func TestIssueArtifactVersion_GetName(tt *testing.T) { + tt.Parallel() + i := &IssueArtifactVersion{} + i.GetName() + i = nil + i.GetName() +} + +func TestIssueArtifactVersion_GetVersion(tt *testing.T) { + tt.Parallel() + i := &IssueArtifactVersion{} + i.GetVersion() + i = nil + i.GetVersion() +} + +func TestIssueArtifactWithVersions_GetArtifact(tt *testing.T) { + tt.Parallel() + i := &IssueArtifactWithVersions{} + i.GetArtifact() + i = nil + i.GetArtifact() +} + +func TestIssueArtifactWithVersions_GetVersions(tt *testing.T) { + tt.Parallel() + zeroValue := []*IssueArtifactVersion{} + i := &IssueArtifactWithVersions{Versions: zeroValue} + i.GetVersions() + i = &IssueArtifactWithVersions{} + i.GetVersions() + i = nil + i.GetVersions() +} + func TestIssueComment_GetAuthorAssociation(tt *testing.T) { tt.Parallel() var zeroValue string @@ -58884,6 +59103,28 @@ func TestUpdateHostedRunnerRequest_GetSize(tt *testing.T) { u.GetSize() } +func TestUpdateIssueArtifactRequest_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateIssueArtifactRequest{Body: &zeroValue} + u.GetBody() + u = &UpdateIssueArtifactRequest{} + u.GetBody() + u = nil + u.GetBody() +} + +func TestUpdateIssueArtifactRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateIssueArtifactRequest{Name: &zeroValue} + u.GetName() + u = &UpdateIssueArtifactRequest{} + u.GetName() + u = nil + u.GetName() +} + func TestUpdateIssueLabelRequest_GetColor(tt *testing.T) { tt.Parallel() var zeroValue string diff --git a/github/github-stringify_test.go b/github/github-stringify_test.go index fdcbc0e33ba..5064e20354f 100644 --- a/github/github-stringify_test.go +++ b/github/github-stringify_test.go @@ -1063,6 +1063,44 @@ func TestIssue_String(t *testing.T) { } } +func TestIssueArtifact_String(t *testing.T) { + t.Parallel() + v := IssueArtifact{ + ID: 0, + Number: 0, + Type: "", + Name: "", + Body: "", + BodyHTML: new(""), + Description: new(""), + HTMLURL: new(""), + Creator: &User{}, + UpdatedByActor: &User{}, + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + } + want := `github.IssueArtifact{ID:0, Number:0, Type:"", Name:"", Body:"", BodyHTML:"", Description:"", HTMLURL:"", Creator:github.User{}, UpdatedByActor:github.User{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` + if got := v.String(); got != want { + t.Errorf("IssueArtifact.String = %v, want %v", got, want) + } +} + +func TestIssueArtifactVersion_String(t *testing.T) { + t.Parallel() + v := IssueArtifactVersion{ + Version: 0, + Name: "", + Body: "", + BodyHTML: new(""), + CreatedAt: &Timestamp{}, + Actor: &User{}, + } + want := `github.IssueArtifactVersion{Version:0, Name:"", Body:"", BodyHTML:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Actor:github.User{}}` + if got := v.String(); got != want { + t.Errorf("IssueArtifactVersion.String = %v, want %v", got, want) + } +} + func TestIssueComment_String(t *testing.T) { t.Parallel() v := IssueComment{ diff --git a/github/issues_artifacts.go b/github/issues_artifacts.go new file mode 100644 index 00000000000..3c652252ead --- /dev/null +++ b/github/issues_artifacts.go @@ -0,0 +1,188 @@ +// Copyright 2026 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" +) + +// IssueArtifact represents an artifact attached to an issue. +// Number identifies the artifact within its issue; ID is its global identifier. +type IssueArtifact struct { + ID int64 `json:"id"` + Number int `json:"number"` + Type string `json:"type"` + Name string `json:"name"` + Body string `json:"body"` + BodyHTML *string `json:"body_html,omitempty"` + Description *string `json:"description,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + Creator *User `json:"creator,omitempty"` + UpdatedByActor *User `json:"updated_by_actor,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +func (a IssueArtifact) String() string { + return Stringify(a) +} + +// IssueArtifactVersion represents a saved version of an issue artifact. +type IssueArtifactVersion struct { + Version int `json:"version"` + Name string `json:"name"` + Body string `json:"body"` + BodyHTML *string `json:"body_html,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + Actor *User `json:"actor,omitempty"` +} + +func (v IssueArtifactVersion) String() string { + return Stringify(v) +} + +// IssueArtifactWithVersions contains an artifact and its edit history. +type IssueArtifactWithVersions struct { + Artifact *IssueArtifact `json:"artifact"` + Versions []*IssueArtifactVersion `json:"versions"` +} + +func (a IssueArtifactWithVersions) String() string { + return Stringify(a) +} + +// IssueArtifactListOptions specifies optional parameters to +// IssuesService.ListArtifacts. +type IssueArtifactListOptions struct { + Type string `url:"type,omitempty"` + ListOptions +} + +// CreateIssueArtifactRequest specifies an artifact to create. +type CreateIssueArtifactRequest struct { + // Type can be "generic", "plan", "link", or "embedded_html". + Type *string `json:"type,omitempty"` + Name string `json:"name"` + Body string `json:"body"` +} + +// UpdateIssueArtifactRequest specifies changes to an artifact. +// Nil fields preserve existing values; non-nil pointers send even empty strings. +// An artifact's type cannot be changed. +type UpdateIssueArtifactRequest struct { + Name *string `json:"name,omitempty"` + Body *string `json:"body,omitempty"` +} + +// ListArtifacts lists one page of artifacts attached to an issue. +// +// This endpoint does not return pagination links, so Response.NextPage is not +// available. To retrieve all artifacts, set PerPage and request successive pages +// until a page contains fewer than PerPage artifacts. An exactly full final page +// requires a subsequent empty request. Offset pagination has no snapshot guarantee. +// +// Note: ListArtifacts uses the undocumented GitHub API endpoint "GET /repos/{owner}/{repo}/issues/{issue_number}/artifacts". +// +//meta:operation GET /repos/{owner}/{repo}/issues/{issue_number}/artifacts +func (s *IssuesService) ListArtifacts(ctx context.Context, owner, repo string, issueNumber int, opts *IssueArtifactListOptions) ([]*IssueArtifact, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/artifacts", owner, repo, issueNumber) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var artifacts []*IssueArtifact + resp, err := s.client.Do(req, &artifacts) + if err != nil { + return nil, resp, err + } + + return artifacts, resp, nil +} + +// GetArtifact gets an artifact and its edit history using its issue-scoped number. +// +// Note: GetArtifact uses the undocumented GitHub API endpoint "GET /repos/{owner}/{repo}/issues/{issue_number}/artifacts/{artifact_number}". +// +//meta:operation GET /repos/{owner}/{repo}/issues/{issue_number}/artifacts/{artifact_number} +func (s *IssuesService) GetArtifact(ctx context.Context, owner, repo string, issueNumber, artifactNumber int) (*IssueArtifactWithVersions, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/artifacts/%v", owner, repo, issueNumber, artifactNumber) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var artifact *IssueArtifactWithVersions + resp, err := s.client.Do(req, &artifact) + if err != nil { + return nil, resp, err + } + + return artifact, resp, nil +} + +// CreateArtifact creates an artifact attached to an issue. +// +// Note: CreateArtifact uses the undocumented GitHub API endpoint "POST /repos/{owner}/{repo}/issues/{issue_number}/artifacts". +// +//meta:operation POST /repos/{owner}/{repo}/issues/{issue_number}/artifacts +func (s *IssuesService) CreateArtifact(ctx context.Context, owner, repo string, issueNumber int, body CreateIssueArtifactRequest) (*IssueArtifact, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/artifacts", owner, repo, issueNumber) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var artifact *IssueArtifact + resp, err := s.client.Do(req, &artifact) + if err != nil { + return nil, resp, err + } + + return artifact, resp, nil +} + +// UpdateArtifact updates an artifact using its issue-scoped number. +// +// Note: UpdateArtifact uses the undocumented GitHub API endpoint "PATCH /repos/{owner}/{repo}/issues/{issue_number}/artifacts/{artifact_number}". +// +//meta:operation PATCH /repos/{owner}/{repo}/issues/{issue_number}/artifacts/{artifact_number} +func (s *IssuesService) UpdateArtifact(ctx context.Context, owner, repo string, issueNumber, artifactNumber int, body UpdateIssueArtifactRequest) (*IssueArtifact, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/artifacts/%v", owner, repo, issueNumber, artifactNumber) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var artifact *IssueArtifact + resp, err := s.client.Do(req, &artifact) + if err != nil { + return nil, resp, err + } + + return artifact, resp, nil +} + +// DeleteArtifact deletes an artifact using its issue-scoped number. +// +// Note: DeleteArtifact uses the undocumented GitHub API endpoint "DELETE /repos/{owner}/{repo}/issues/{issue_number}/artifacts/{artifact_number}". +// +//meta:operation DELETE /repos/{owner}/{repo}/issues/{issue_number}/artifacts/{artifact_number} +func (s *IssuesService) DeleteArtifact(ctx context.Context, owner, repo string, issueNumber, artifactNumber int) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/artifacts/%v", owner, repo, issueNumber, artifactNumber) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/issues_artifacts_test.go b/github/issues_artifacts_test.go new file mode 100644 index 00000000000..f57f7307b38 --- /dev/null +++ b/github/issues_artifacts_test.go @@ -0,0 +1,347 @@ +// Copyright 2026 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 ( + "errors" + "fmt" + "net/http" + "testing" + "time" + + "github.com/google/go-cmp/cmp" +) + +func TestIssueArtifactWithVersions_String(t *testing.T) { + t.Parallel() + v := IssueArtifactWithVersions{Artifact: &IssueArtifact{}} + want := `github.IssueArtifactWithVersions{Artifact:github.IssueArtifact{ID:0, Number:0, Type:"", Name:"", Body:""}}` + if got := v.String(); got != want { + t.Errorf("IssueArtifactWithVersions.String = %v, want %v", got, want) + } +} + +func TestIssuesService_ListArtifacts(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/issues/42/artifacts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2", "per_page": "50", "type": "link"}) + fmt.Fprint(w, `[{"id":9001,"number":3,"type":"link","name":"Runbook","body":"https://example.com"}]`) + }) + + opts := &IssueArtifactListOptions{Type: "link", ListOptions: ListOptions{Page: 2, PerPage: 50}} + got, resp, err := client.Issues.ListArtifacts(t.Context(), "o", "r", 42, opts) + if err != nil { + t.Fatalf("Issues.ListArtifacts returned error: %v", err) + } + want := []*IssueArtifact{{ID: 9001, Number: 3, Type: "link", Name: "Runbook", Body: "https://example.com"}} + if !cmp.Equal(got, want) { + t.Errorf("Issues.ListArtifacts = %+v, want %+v", got, want) + } + if resp.StatusCode != http.StatusOK || resp.NextPage != 0 { + t.Errorf("response status = %v, NextPage = %v; want 200, 0", resp.StatusCode, resp.NextPage) + } + + const methodName = "ListArtifacts" + testBadOptions(t, methodName, func() error { + _, _, err := client.Issues.ListArtifacts(t.Context(), "\n", "\n", 42, opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListArtifacts(t.Context(), "o", "r", 42, opts) + if got != nil { + t.Errorf("%v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestIssuesService_ListArtifacts_Pages(t *testing.T) { + t.Parallel() + for _, tt := range []struct { + name string + body string + opts *IssueArtifactListOptions + want int + }{ + {name: "empty", body: `[]`, want: 0}, + {name: "full", body: `[{"id":9001,"number":3}]`, opts: &IssueArtifactListOptions{ListOptions: ListOptions{PerPage: 1}}, want: 1}, + } { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + requests := 0 + mux.HandleFunc("/repos/o/r/issues/42/artifacts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + query := values{} + if tt.opts != nil { + query["per_page"] = "1" + } + testFormValues(t, r, query) + requests++ + fmt.Fprint(w, tt.body) + }) + got, resp, err := client.Issues.ListArtifacts(t.Context(), "o", "r", 42, tt.opts) + if err != nil { + t.Fatalf("Issues.ListArtifacts returned error: %v", err) + } + if got == nil || len(got) != tt.want || requests != 1 || resp.NextPage != 0 { + t.Errorf("got %v, requests = %v, NextPage = %v; want non-nil slice of length %v, 1, 0", got, requests, resp.NextPage, tt.want) + } + }) + } +} + +func TestIssuesService_GetArtifact(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/repos/o/r/issues/42/artifacts/3", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{}) + fmt.Fprint(w, `{ + "artifact":{ + "id":9001,"number":3,"type":"plan","name":"Plan","body":"# Plan", + "body_html":"