Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 79 additions & 2 deletions pkg/cmd/gpg-key/add/add_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,93 @@ package add

import (
"net/http"
"strings"
"testing"

"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/gh"
"github.com/cli/cli/v2/pkg/httpmock"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func Test_gpgKeyUploadScopesMissing(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.REST("POST", "user/gpg_keys"),
httpmock.StatusStringResponse(http.StatusNotFound, `{"message":"Not Found"}`),
)

err := gpgKeyUpload(&http.Client{Transport: reg}, "github.com", strings.NewReader("-----BEGIN PGP PUBLIC KEY BLOCK-----"), "")

require.Same(t, errScopesMissing, err)
}

func Test_gpgKeyUploadDuplicateKeyBeforeWrongFormat(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.REST("POST", "user/gpg_keys"),
httpmock.WithHeader(httpmock.StatusStringResponse(http.StatusUnprocessableEntity, `{
"message": "Validation Failed",
"errors": [{
"resource": "GpgKey",
"code": "custom",
"field": "key_id",
"message": "key_id already exists"
}]
}`), "Content-Type", "application/json"),
)

err := gpgKeyUpload(&http.Client{Transport: reg}, "github.com", strings.NewReader("binary-key"), "")

require.Same(t, errDuplicateKey, err)
}

func Test_gpgKeyUploadWrongFormat(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.REST("POST", "user/gpg_keys"),
httpmock.StatusStringResponse(http.StatusUnprocessableEntity, `{
"message": "Validation Failed",
"errors": [{
"resource": "GpgKey",
"code": "custom",
"message": "We got an error doing that."
}]
}`),
)

err := gpgKeyUpload(&http.Client{Transport: reg}, "github.com", strings.NewReader("binary-key"), "")

require.Same(t, errWrongFormat, err)
}

func Test_gpgKeyUploadHTTPError(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.REST("POST", "user/gpg_keys"),
httpmock.WithHeader(
httpmock.StatusStringResponse(http.StatusInternalServerError, `{"message":"Internal Server Error"}`),
"Content-Type", "application/json",
),
)

err := gpgKeyUpload(&http.Client{Transport: reg}, "github.com", strings.NewReader("-----BEGIN PGP PUBLIC KEY BLOCK-----"), "")

var httpErr api.HTTPError
require.ErrorAs(t, err, &httpErr)
assert.Equal(t, http.StatusInternalServerError, httpErr.StatusCode)
assert.Contains(t, err.Error(), "HTTP 500")
assert.EqualError(t, err, "HTTP 500: Internal Server Error (https://api.github.com/user/gpg_keys)")
}

func Test_runAdd(t *testing.T) {
tests := []struct {
name string
Expand All @@ -28,7 +105,7 @@ func Test_runAdd(t *testing.T) {
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("POST", "user/gpg_keys"),
httpmock.RESTPayload(200, ``, func(payload map[string]interface{}) {
httpmock.RESTPayload(200, `{}`, func(payload map[string]interface{}) {
assert.Contains(t, payload, "armored_public_key")
assert.NotContains(t, payload, "title")
}))
Expand All @@ -44,7 +121,7 @@ func Test_runAdd(t *testing.T) {
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("POST", "user/gpg_keys"),
httpmock.RESTPayload(200, ``, func(payload map[string]interface{}) {
httpmock.RESTPayload(200, `{}`, func(payload map[string]interface{}) {
assert.Contains(t, payload, "armored_public_key")
assert.Contains(t, payload, "name")
}))
Expand Down
37 changes: 14 additions & 23 deletions pkg/cmd/gpg-key/add/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"net/http"

"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/ghinstance"
"github.com/cli/cli/v2/internal/safeurl"
)

Expand All @@ -17,11 +16,6 @@ var errDuplicateKey = errors.New("key already exists")
var errWrongFormat = errors.New("key in wrong format")

func gpgKeyUpload(httpClient *http.Client, hostname string, keyFile io.Reader, title string) error {
u, err := safeurl.JoinPathWithHostPrefix(ghinstance.RESTPrefix(hostname), "user", "gpg_keys")
if err != nil {
return err
}

keyBytes, err := io.ReadAll(keyFile)
if err != nil {
return err
Expand All @@ -39,36 +33,33 @@ func gpgKeyUpload(httpClient *http.Client, hostname string, keyFile io.Reader, t
return err
}

req, err := http.NewRequest("POST", u.String(), bytes.NewBuffer(payloadBytes))
path, err := safeurl.JoinPath("user", "gpg_keys")
if err != nil {
return err
}

resp, err := httpClient.Do(req)
// TODO(api-client-rollout)
// This line of code is part of a mechanical roll out of the api client.
// As a follow up, consider whether the api client can be injected to this call site, rather than constructed
apiClient := api.NewClientFromHTTP(httpClient)
err = apiClient.REST(hostname, "POST", path.String(), bytes.NewBuffer(payloadBytes), nil)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode == 404 {
return errScopesMissing
} else if resp.StatusCode > 299 {
err := api.HandleHTTPError(resp)
var httpError api.HTTPError
if errors.As(err, &httpError) {
if httpError, ok := errors.AsType[api.HTTPError](err); ok {
if httpError.StatusCode == 404 {
return errScopesMissing
}
for _, e := range httpError.Errors {
if resp.StatusCode == 422 && e.Field == "key_id" && e.Message == "key_id already exists" {
if httpError.StatusCode == 422 && e.Field == "key_id" && e.Message == "key_id already exists" {
return errDuplicateKey
}
}
}
if resp.StatusCode == 422 && !isGpgKeyArmored(keyBytes) {
return errWrongFormat
if httpError.StatusCode == 422 && !isGpgKeyArmored(keyBytes) {
return errWrongFormat
}
}
return err
}

_, _ = io.Copy(io.Discard, resp.Body)
return nil
}

Expand Down
48 changes: 46 additions & 2 deletions pkg/cmd/gpg-key/delete/delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,63 @@ package delete
import (
"bytes"
"net/http"
"net/url"
"testing"

"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/gh"
"github.com/cli/cli/v2/internal/prompter"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/httpmock"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/go-gh/v2/pkg/api"
ghAPI "github.com/cli/go-gh/v2/pkg/api"
"github.com/google/shlex"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func Test_deleteGPGKeyHTTPError(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.REST("DELETE", "user/gpg_keys/123"),
httpmock.WithHeader(
httpmock.StatusStringResponse(http.StatusInternalServerError, `{"message":"Internal Server Error"}`),
"Content-Type", "application/json",
),
)

err := deleteGPGKey(&http.Client{Transport: reg}, "github.com", "123")

var httpErr api.HTTPError
require.ErrorAs(t, err, &httpErr)
assert.Equal(t, http.StatusInternalServerError, httpErr.StatusCode)
assert.Contains(t, err.Error(), "HTTP 500")
assert.EqualError(t, err, "HTTP 500: Internal Server Error (https://api.github.com/user/gpg_keys/123)")
}

func Test_getGPGKeysHTTPError(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.QueryMatcher("GET", "user/gpg_keys", url.Values{"per_page": []string{"100"}}),
httpmock.WithHeader(
httpmock.StatusStringResponse(http.StatusInternalServerError, `{"message":"Internal Server Error"}`),
"Content-Type", "application/json",
),
)

keys, err := getGPGKeys(&http.Client{Transport: reg}, "github.com")

assert.Nil(t, keys)
var httpErr api.HTTPError
require.ErrorAs(t, err, &httpErr)
assert.Equal(t, http.StatusInternalServerError, httpErr.StatusCode)
assert.Contains(t, err.Error(), "HTTP 500")
assert.EqualError(t, err, "HTTP 500: Internal Server Error (https://api.github.com/user/gpg_keys?per_page=100)")
}

func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -177,7 +221,7 @@ func Test_deleteRun(t *testing.T) {
opts: DeleteOptions{KeyID: "ABC123", Confirmed: true},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "user/gpg_keys"), httpmock.StatusStringResponse(200, keysResp))
reg.Register(httpmock.REST("DELETE", "user/gpg_keys/123"), httpmock.JSONErrorResponse(404, api.HTTPError{
reg.Register(httpmock.REST("DELETE", "user/gpg_keys/123"), httpmock.JSONErrorResponse(404, ghAPI.HTTPError{
Comment thread
williammartin marked this conversation as resolved.
StatusCode: 404,
Message: "GPG key 123 not found",
}))
Expand Down
52 changes: 10 additions & 42 deletions pkg/cmd/gpg-key/delete/http.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package delete

import (
"encoding/json"
"io"
"net/http"

"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/ghinstance"
"github.com/cli/cli/v2/internal/safeurl"
)

Expand All @@ -16,59 +13,30 @@ type gpgKey struct {
}

func deleteGPGKey(httpClient *http.Client, host, id string) error {
url, err := safeurl.JoinPathWithHostPrefix(ghinstance.RESTPrefix(host), "user", "gpg_keys", id)
path, err := safeurl.JoinPath("user", "gpg_keys", id)
if err != nil {
return err
}
req, err := http.NewRequest("DELETE", url.String(), nil)
if err != nil {
return err
}

resp, err := httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode > 299 {
return api.HandleHTTPError(resp)
}

return nil
// TODO(api-client-rollout)
// This line of code is part of a mechanical roll out of the api client.
// As a follow up, consider whether the api client can be injected to this call site, rather than constructed
return api.NewClientFromHTTP(httpClient).REST(host, "DELETE", path.String(), nil, nil)
}

func getGPGKeys(httpClient *http.Client, host string) ([]gpgKey, error) {
u, err := safeurl.JoinPathWithHostPrefix(ghinstance.RESTPrefix(host), "user", "gpg_keys")
u, err := safeurl.JoinPath("user", "gpg_keys")
if err != nil {
return nil, err
}
u.SetQuery("per_page", "100")
req, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
return nil, err
}

resp, err := httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode > 299 {
return nil, api.HandleHTTPError(resp)
}

b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

var keys []gpgKey
err = json.Unmarshal(b, &keys)
// TODO(api-client-rollout)
// This line of code is part of a mechanical roll out of the api client.
// As a follow up, consider whether the api client can be injected to this call site, rather than constructed
err = api.NewClientFromHTTP(httpClient).REST(host, "GET", u.String(), nil, &keys)
if err != nil {
return nil, err
}

return keys, nil
}
Loading