Merge branch 'kw/gh-cli-909-911-assign-actors-to-prs' into kw/gh-cli-916-special-actor-assignee-names
This commit is contained in:
commit
51b1e6cd6f
8 changed files with 539 additions and 43 deletions
|
|
@ -701,7 +701,7 @@ func (m *RepoMetadataResult) MembersToIDs(names []string) ([]string, error) {
|
|||
for _, assigneeLogin := range names {
|
||||
found := false
|
||||
for _, u := range m.AssignableUsers {
|
||||
if strings.EqualFold(assigneeLogin, (u.Login())) {
|
||||
if strings.EqualFold(assigneeLogin, u.Login()) {
|
||||
ids = append(ids, u.ID())
|
||||
found = true
|
||||
break
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ package prompter_test
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -54,6 +55,73 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
assert.Equal(t, 0, selectValue)
|
||||
})
|
||||
|
||||
t.Run("Select - blank input returns default value", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
dummyDefaultValue := "12345abcdefg"
|
||||
options := []string{"1", "2", dummyDefaultValue}
|
||||
|
||||
go func() {
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Input a number between 1 and 3:")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Just press enter to accept the default
|
||||
_, err = console.SendLine("")
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
selectValue, err := p.Select("Select a number", dummyDefaultValue, options)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedIndex := slices.Index(options, dummyDefaultValue)
|
||||
assert.Equal(t, expectedIndex, selectValue)
|
||||
})
|
||||
|
||||
t.Run("Select - default value is in prompt and in readable format", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
dummyDefaultValue := "12345abcdefg"
|
||||
options := []string{"1", "2", dummyDefaultValue}
|
||||
|
||||
go func() {
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Select a number (default: 12345abcdefg)")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Just press enter to accept the default
|
||||
_, err = console.SendLine("")
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
selectValue, err := p.Select("Select a number", dummyDefaultValue, options)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedIndex := slices.Index(options, dummyDefaultValue)
|
||||
assert.Equal(t, expectedIndex, selectValue)
|
||||
})
|
||||
|
||||
t.Run("Select - invalid defaults are excluded from prompt", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
dummyDefaultValue := "foo"
|
||||
options := []string{"1", "2"}
|
||||
|
||||
go func() {
|
||||
// Wait for prompt to appear without the invalid default value
|
||||
_, err := console.ExpectString("Select a number \r\n")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Select option 2
|
||||
_, err = console.SendLine("2")
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
selectValue, err := p.Select("Select a number", dummyDefaultValue, options)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, selectValue)
|
||||
})
|
||||
|
||||
t.Run("MultiSelect", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
|
|
@ -100,6 +168,62 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
assert.Equal(t, []int{1}, multiSelectValue)
|
||||
})
|
||||
|
||||
t.Run("MultiSelect - default value is in prompt and in readable format", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
dummyDefaultValues := []string{"foo", "bar"}
|
||||
options := []string{"1", "2"}
|
||||
options = append(options, dummyDefaultValues...)
|
||||
|
||||
go func() {
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Select a number (defaults: foo, bar)")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Don't select anything because the defaults should be selected.
|
||||
|
||||
// This confirms selections
|
||||
_, err = console.SendLine("0")
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
multiSelectValues, err := p.MultiSelect("Select a number", dummyDefaultValues, options)
|
||||
require.NoError(t, err)
|
||||
var expectedIndices []int
|
||||
|
||||
// Get the indices of the default values within the options slice
|
||||
// as that's what we expect the prompter to return when no selections are made.
|
||||
for _, defaultValue := range dummyDefaultValues {
|
||||
expectedIndices = append(expectedIndices, slices.Index(options, defaultValue))
|
||||
}
|
||||
assert.Equal(t, expectedIndices, multiSelectValues)
|
||||
})
|
||||
|
||||
t.Run("MultiSelect - invalid defaults are excluded from prompt", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
dummyDefaultValues := []string{"foo", "bar"}
|
||||
options := []string{"1", "2"}
|
||||
|
||||
go func() {
|
||||
// Wait for prompt to appear without the invalid default values
|
||||
_, err := console.ExpectString("Select a number \r\n")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Not selecting anything will fail because there are no defaults.
|
||||
_, err = console.SendLine("2")
|
||||
require.NoError(t, err)
|
||||
|
||||
// This confirms selections
|
||||
_, err = console.SendLine("0")
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
multiSelectValues, err := p.MultiSelect("Select a number", dummyDefaultValues, options)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []int{1}, multiSelectValues)
|
||||
})
|
||||
|
||||
t.Run("Input", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
|
|
@ -140,6 +264,26 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
assert.Equal(t, dummyDefaultValue, inputValue)
|
||||
})
|
||||
|
||||
t.Run("Input - default value is in prompt and in readable format", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
dummyDefaultValue := "12345abcdefg"
|
||||
|
||||
go func() {
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Enter some characters (default: 12345abcdefg)")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Enter nothing
|
||||
_, err = console.SendLine("")
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
inputValue, err := p.Input("Enter some characters", dummyDefaultValue)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, dummyDefaultValue, inputValue)
|
||||
})
|
||||
|
||||
t.Run("Password", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
|
|
@ -164,7 +308,12 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
// Ensure the dummy password is not printed to the screen,
|
||||
// asserting that echo mode is disabled.
|
||||
_, err = console.ExpectString(" \r\n\r\n")
|
||||
//
|
||||
// Note that since console.ExpectString returns successful if the
|
||||
// expected string matches any part of the stream, we have to use an
|
||||
// anchored regexp (i.e., with ^ and $) to make sure the password/token
|
||||
// is not printed at all.
|
||||
_, err = console.Expect(expect.RegexpPattern("^ \r\n\r\n$"))
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
|
|
@ -206,6 +355,26 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
require.Equal(t, false, confirmValue)
|
||||
})
|
||||
|
||||
t.Run("Confirm - default value is in prompt and in readable format", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
defaultValue := true
|
||||
|
||||
go func() {
|
||||
// Wait for prompt to appear
|
||||
_, err := console.ExpectString("Are you sure (default: yes)")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Enter nothing
|
||||
_, err = console.SendLine("")
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
confirmValue, err := p.Confirm("Are you sure", defaultValue)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, defaultValue, confirmValue)
|
||||
})
|
||||
|
||||
t.Run("AuthToken", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
|
|
@ -230,7 +399,12 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
// Ensure the dummy password is not printed to the screen,
|
||||
// asserting that echo mode is disabled.
|
||||
_, err = console.ExpectString(" \r\n\r\n")
|
||||
//
|
||||
// Note that since console.ExpectString returns successful if the
|
||||
// expected string matches any part of the stream, we have to use an
|
||||
// anchored regexp (i.e., with ^ and $) to make sure the password/token
|
||||
// is not printed at all.
|
||||
_, err = console.Expect(expect.RegexpPattern("^ \r\n\r\n$"))
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
|
|
@ -252,6 +426,10 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
_, err = console.ExpectString("token is required")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wait for the retry prompt
|
||||
_, err = console.ExpectString("Paste your authentication token:")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Wait to ensure huh has time to set the echo mode
|
||||
time.Sleep(beforePasswordSendTimeout)
|
||||
|
||||
|
|
@ -266,7 +444,12 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
// Ensure the dummy password is not printed to the screen,
|
||||
// asserting that echo mode is disabled.
|
||||
_, err = console.ExpectString(" \r\n\r\n")
|
||||
//
|
||||
// Note that since console.ExpectString returns successful if the
|
||||
// expected string matches any part of the stream, we have to use an
|
||||
// anchored regexp (i.e., with ^ and $) to make sure the password/token
|
||||
// is not printed at all.
|
||||
_, err = console.Expect(expect.RegexpPattern("^ \r\n\r\n$"))
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -77,10 +77,40 @@ func (p *accessiblePrompter) newForm(groups ...*huh.Group) *huh.Form {
|
|||
WithOutput(p.stdout)
|
||||
}
|
||||
|
||||
func (p *accessiblePrompter) Select(prompt, _ string, options []string) (int, error) {
|
||||
// addDefaultsToPrompt adds default values to the prompt string.
|
||||
func (p *accessiblePrompter) addDefaultsToPrompt(prompt string, defaultValues []string) string {
|
||||
// Removing empty defaults from the slice.
|
||||
defaultValues = slices.DeleteFunc(defaultValues, func(s string) bool {
|
||||
return s == ""
|
||||
})
|
||||
|
||||
// Pluralizing the prompt if there are multiple default values.
|
||||
if len(defaultValues) == 1 {
|
||||
prompt = fmt.Sprintf("%s (default: %s)", prompt, defaultValues[0])
|
||||
} else if len(defaultValues) > 1 {
|
||||
prompt = fmt.Sprintf("%s (defaults: %s)", prompt, strings.Join(defaultValues, ", "))
|
||||
}
|
||||
|
||||
// Zero-length defaultValues means return prompt unchanged.
|
||||
return prompt
|
||||
}
|
||||
|
||||
func (p *accessiblePrompter) Select(prompt, defaultValue string, options []string) (int, error) {
|
||||
var result int
|
||||
|
||||
// Remove invalid default values from the defaults slice.
|
||||
if !slices.Contains(options, defaultValue) {
|
||||
defaultValue = ""
|
||||
}
|
||||
|
||||
prompt = p.addDefaultsToPrompt(prompt, []string{defaultValue})
|
||||
formOptions := []huh.Option[int]{}
|
||||
for i, o := range options {
|
||||
// If this option is the default value, assign its index
|
||||
// to the result variable. huh will treat it as a default selection.
|
||||
if defaultValue == o {
|
||||
result = i
|
||||
}
|
||||
formOptions = append(formOptions, huh.NewOption(o, i))
|
||||
}
|
||||
|
||||
|
|
@ -99,12 +129,18 @@ func (p *accessiblePrompter) Select(prompt, _ string, options []string) (int, er
|
|||
|
||||
func (p *accessiblePrompter) MultiSelect(prompt string, defaults []string, options []string) ([]int, error) {
|
||||
var result []int
|
||||
|
||||
// Remove invalid default values from the defaults slice.
|
||||
defaults = slices.DeleteFunc(defaults, func(s string) bool {
|
||||
return !slices.Contains(options, s)
|
||||
})
|
||||
|
||||
prompt = p.addDefaultsToPrompt(prompt, defaults)
|
||||
formOptions := make([]huh.Option[int], len(options))
|
||||
for i, o := range options {
|
||||
// If this option is in the defaults slice,
|
||||
// let's add its index to the result slice and huh
|
||||
// will treat it as a default selection.
|
||||
// TODO: does an invalid default value constitute a panic?
|
||||
if slices.Contains(defaults, o) {
|
||||
result = append(result, i)
|
||||
}
|
||||
|
|
@ -131,7 +167,7 @@ func (p *accessiblePrompter) MultiSelect(prompt string, defaults []string, optio
|
|||
|
||||
func (p *accessiblePrompter) Input(prompt, defaultValue string) (string, error) {
|
||||
result := defaultValue
|
||||
prompt = fmt.Sprintf("%s (%s)", prompt, defaultValue)
|
||||
prompt = p.addDefaultsToPrompt(prompt, []string{defaultValue})
|
||||
form := p.newForm(
|
||||
huh.NewGroup(
|
||||
huh.NewInput().
|
||||
|
|
@ -167,6 +203,13 @@ func (p *accessiblePrompter) Password(prompt string) (string, error) {
|
|||
|
||||
func (p *accessiblePrompter) Confirm(prompt string, defaultValue bool) (bool, error) {
|
||||
result := defaultValue
|
||||
|
||||
if defaultValue {
|
||||
prompt = p.addDefaultsToPrompt(prompt, []string{"yes"})
|
||||
} else {
|
||||
prompt = p.addDefaultsToPrompt(prompt, []string{"no"})
|
||||
}
|
||||
|
||||
form := p.newForm(
|
||||
huh.NewGroup(
|
||||
huh.NewConfirm().
|
||||
|
|
@ -174,6 +217,7 @@ func (p *accessiblePrompter) Confirm(prompt string, defaultValue bool) (bool, er
|
|||
Value(&result),
|
||||
),
|
||||
)
|
||||
|
||||
if err := form.Run(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,6 +198,7 @@ func editRun(opts *EditOptions) error {
|
|||
findOptions := shared.FindOptions{
|
||||
Selector: opts.SelectorArg,
|
||||
Fields: []string{"id", "url", "title", "body", "baseRefName", "reviewRequests", "labels", "projectCards", "projectItems", "milestone"},
|
||||
Detector: opts.Detector,
|
||||
}
|
||||
|
||||
if opts.Detector == nil {
|
||||
|
|
|
|||
|
|
@ -758,3 +758,73 @@ func (s testSurveyor) EditFields(e *shared.Editable, _ string) error {
|
|||
func (t testEditorRetriever) Retrieve() (string, error) {
|
||||
return "vim", nil
|
||||
}
|
||||
|
||||
// TODO projectsV1Deprecation
|
||||
// Remove this test.
|
||||
func TestProjectsV1Deprecation(t *testing.T) {
|
||||
t.Run("when projects v1 is supported, is included in query", func(t *testing.T) {
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
|
||||
reg := &httpmock.Registry{}
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`projectCards`),
|
||||
// Simulate a GraphQL error to early exit the test.
|
||||
httpmock.StatusStringResponse(500, ""),
|
||||
)
|
||||
|
||||
f := &cmdutil.Factory{
|
||||
IOStreams: ios,
|
||||
HttpClient: func() (*http.Client, error) {
|
||||
return &http.Client{Transport: reg}, nil
|
||||
},
|
||||
}
|
||||
|
||||
// Ignore the error because we have no way to really stub it without
|
||||
// fully stubbing a GQL error structure in the request body.
|
||||
_ = editRun(&EditOptions{
|
||||
IO: ios,
|
||||
HttpClient: func() (*http.Client, error) {
|
||||
return &http.Client{Transport: reg}, nil
|
||||
},
|
||||
Detector: &fd.EnabledDetectorMock{},
|
||||
|
||||
Finder: shared.NewFinder(f),
|
||||
|
||||
SelectorArg: "https://github.com/cli/cli/pull/123",
|
||||
})
|
||||
|
||||
// Verify that our request contained projectCards
|
||||
reg.Verify(t)
|
||||
})
|
||||
|
||||
t.Run("when projects v1 is not supported, is not included in query", func(t *testing.T) {
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
|
||||
reg := &httpmock.Registry{}
|
||||
reg.Exclude(t, httpmock.GraphQL(`projectCards`))
|
||||
|
||||
f := &cmdutil.Factory{
|
||||
IOStreams: ios,
|
||||
HttpClient: func() (*http.Client, error) {
|
||||
return &http.Client{Transport: reg}, nil
|
||||
},
|
||||
}
|
||||
|
||||
// Ignore the error because we have no way to really stub it without
|
||||
// fully stubbing a GQL error structure in the request body.
|
||||
_ = editRun(&EditOptions{
|
||||
IO: ios,
|
||||
HttpClient: func() (*http.Client, error) {
|
||||
return &http.Client{Transport: reg}, nil
|
||||
},
|
||||
Detector: &fd.DisabledDetectorMock{},
|
||||
|
||||
Finder: shared.NewFinder(f),
|
||||
|
||||
SelectorArg: "https://github.com/cli/cli/pull/123",
|
||||
})
|
||||
|
||||
// Verify that our request did not contain projectCards
|
||||
reg.Verify(t)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ func replaceActorAssigneesForEditable(apiClient *api.Client, repo ghrepo.Interfa
|
|||
|
||||
var mutation struct {
|
||||
ReplaceActorsForAssignable struct {
|
||||
Typename string `graphql:"__typename"`
|
||||
TypeName string `graphql:"__typename"`
|
||||
} `graphql:"replaceActorsForAssignable(input: $input)"`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -165,10 +165,24 @@ func downloadRun(opts *DownloadOptions) error {
|
|||
var toDownload []shared.ReleaseAsset
|
||||
isArchive := false
|
||||
if opts.ArchiveType != "" {
|
||||
var archiveURL = release.ZipballURL
|
||||
var archiveURL string
|
||||
if opts.ArchiveType == "tar.gz" {
|
||||
archiveURL = release.TarballURL
|
||||
} else {
|
||||
archiveURL = release.ZipballURL
|
||||
}
|
||||
|
||||
if archiveURL == "" {
|
||||
errMessage := fmt.Sprintf(
|
||||
"release %q with tag %q, does not have a %q archive asset.",
|
||||
release.Name, release.TagName, opts.ArchiveType,
|
||||
)
|
||||
if release.IsDraft {
|
||||
errMessage += " Most likely, this is because it is a draft."
|
||||
}
|
||||
return errors.New(errMessage)
|
||||
}
|
||||
|
||||
// create pseudo-Asset with no name and pointing to ZipBallURL or TarBallURL
|
||||
toDownload = append(toDownload, shared.ReleaseAsset{APIURL: archiveURL})
|
||||
isArchive = true
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ func Test_downloadRun(t *testing.T) {
|
|||
name string
|
||||
isTTY bool
|
||||
opts DownloadOptions
|
||||
httpStubs func(*httpmock.Registry)
|
||||
wantErr string
|
||||
wantStdout string
|
||||
wantStderr string
|
||||
|
|
@ -196,6 +197,24 @@ func Test_downloadRun(t *testing.T) {
|
|||
Destination: ".",
|
||||
Concurrency: 2,
|
||||
},
|
||||
httpStubs: func(reg *httpmock.Registry) {
|
||||
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
|
||||
"assets": [
|
||||
{ "name": "windows-32bit.zip", "size": 12,
|
||||
"url": "https://api.github.com/assets/1234" },
|
||||
{ "name": "windows-64bit.zip", "size": 34,
|
||||
"url": "https://api.github.com/assets/3456" },
|
||||
{ "name": "linux.tgz", "size": 56,
|
||||
"url": "https://api.github.com/assets/5678" }
|
||||
],
|
||||
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
|
||||
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
|
||||
}`)
|
||||
|
||||
reg.Register(httpmock.REST("GET", "assets/1234"), httpmock.StringResponse(`1234`))
|
||||
reg.Register(httpmock.REST("GET", "assets/3456"), httpmock.StringResponse(`3456`))
|
||||
reg.Register(httpmock.REST("GET", "assets/5678"), httpmock.StringResponse(`5678`))
|
||||
},
|
||||
wantStdout: ``,
|
||||
wantStderr: ``,
|
||||
wantFiles: []string{
|
||||
|
|
@ -213,6 +232,23 @@ func Test_downloadRun(t *testing.T) {
|
|||
Destination: "tmp/assets",
|
||||
Concurrency: 2,
|
||||
},
|
||||
httpStubs: func(reg *httpmock.Registry) {
|
||||
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
|
||||
"assets": [
|
||||
{ "name": "windows-32bit.zip", "size": 12,
|
||||
"url": "https://api.github.com/assets/1234" },
|
||||
{ "name": "windows-64bit.zip", "size": 34,
|
||||
"url": "https://api.github.com/assets/3456" },
|
||||
{ "name": "linux.tgz", "size": 56,
|
||||
"url": "https://api.github.com/assets/5678" }
|
||||
],
|
||||
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
|
||||
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
|
||||
}`)
|
||||
|
||||
reg.Register(httpmock.REST("GET", "assets/1234"), httpmock.StringResponse(`1234`))
|
||||
reg.Register(httpmock.REST("GET", "assets/3456"), httpmock.StringResponse(`3456`))
|
||||
},
|
||||
wantStdout: ``,
|
||||
wantStderr: ``,
|
||||
wantFiles: []string{
|
||||
|
|
@ -229,6 +265,20 @@ func Test_downloadRun(t *testing.T) {
|
|||
Destination: ".",
|
||||
Concurrency: 2,
|
||||
},
|
||||
httpStubs: func(reg *httpmock.Registry) {
|
||||
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
|
||||
"assets": [
|
||||
{ "name": "windows-32bit.zip", "size": 12,
|
||||
"url": "https://api.github.com/assets/1234" },
|
||||
{ "name": "windows-64bit.zip", "size": 34,
|
||||
"url": "https://api.github.com/assets/3456" },
|
||||
{ "name": "linux.tgz", "size": 56,
|
||||
"url": "https://api.github.com/assets/5678" }
|
||||
],
|
||||
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
|
||||
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
|
||||
}`)
|
||||
},
|
||||
wantStdout: ``,
|
||||
wantStderr: ``,
|
||||
wantErr: "no assets match the file pattern",
|
||||
|
|
@ -242,6 +292,30 @@ func Test_downloadRun(t *testing.T) {
|
|||
Destination: "tmp/packages",
|
||||
Concurrency: 2,
|
||||
},
|
||||
httpStubs: func(reg *httpmock.Registry) {
|
||||
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
|
||||
"assets": [
|
||||
{ "name": "windows-32bit.zip", "size": 12,
|
||||
"url": "https://api.github.com/assets/1234" },
|
||||
{ "name": "windows-64bit.zip", "size": 34,
|
||||
"url": "https://api.github.com/assets/3456" },
|
||||
{ "name": "linux.tgz", "size": 56,
|
||||
"url": "https://api.github.com/assets/5678" }
|
||||
],
|
||||
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
|
||||
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
|
||||
}`)
|
||||
|
||||
reg.Register(
|
||||
httpmock.REST(
|
||||
"GET",
|
||||
"repos/OWNER/REPO/zipball/v1.2.3",
|
||||
),
|
||||
httpmock.WithHeader(
|
||||
httpmock.StringResponse("somedata"), "content-disposition", "attachment; filename=zipball.zip",
|
||||
),
|
||||
)
|
||||
},
|
||||
wantStdout: ``,
|
||||
wantStderr: ``,
|
||||
wantFiles: []string{
|
||||
|
|
@ -257,6 +331,30 @@ func Test_downloadRun(t *testing.T) {
|
|||
Destination: "tmp/packages",
|
||||
Concurrency: 2,
|
||||
},
|
||||
httpStubs: func(reg *httpmock.Registry) {
|
||||
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
|
||||
"assets": [
|
||||
{ "name": "windows-32bit.zip", "size": 12,
|
||||
"url": "https://api.github.com/assets/1234" },
|
||||
{ "name": "windows-64bit.zip", "size": 34,
|
||||
"url": "https://api.github.com/assets/3456" },
|
||||
{ "name": "linux.tgz", "size": 56,
|
||||
"url": "https://api.github.com/assets/5678" }
|
||||
],
|
||||
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
|
||||
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
|
||||
}`)
|
||||
|
||||
reg.Register(
|
||||
httpmock.REST(
|
||||
"GET",
|
||||
"repos/OWNER/REPO/tarball/v1.2.3",
|
||||
),
|
||||
httpmock.WithHeader(
|
||||
httpmock.StringResponse("somedata"), "content-disposition", "attachment; filename=tarball.tgz",
|
||||
),
|
||||
)
|
||||
},
|
||||
wantStdout: ``,
|
||||
wantStderr: ``,
|
||||
wantFiles: []string{
|
||||
|
|
@ -273,6 +371,30 @@ func Test_downloadRun(t *testing.T) {
|
|||
Concurrency: 2,
|
||||
ArchiveType: "tar.gz",
|
||||
},
|
||||
httpStubs: func(reg *httpmock.Registry) {
|
||||
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
|
||||
"assets": [
|
||||
{ "name": "windows-32bit.zip", "size": 12,
|
||||
"url": "https://api.github.com/assets/1234" },
|
||||
{ "name": "windows-64bit.zip", "size": 34,
|
||||
"url": "https://api.github.com/assets/3456" },
|
||||
{ "name": "linux.tgz", "size": 56,
|
||||
"url": "https://api.github.com/assets/5678" }
|
||||
],
|
||||
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
|
||||
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
|
||||
}`)
|
||||
|
||||
reg.Register(
|
||||
httpmock.REST(
|
||||
"GET",
|
||||
"repos/OWNER/REPO/tarball/v1.2.3",
|
||||
),
|
||||
httpmock.WithHeader(
|
||||
httpmock.StringResponse("somedata"), "content-disposition", "attachment; filename=tarball.tgz",
|
||||
),
|
||||
)
|
||||
},
|
||||
wantStdout: ``,
|
||||
wantStderr: ``,
|
||||
wantFiles: []string{
|
||||
|
|
@ -289,6 +411,22 @@ func Test_downloadRun(t *testing.T) {
|
|||
Concurrency: 2,
|
||||
FilePatterns: []string{"*windows-32bit.zip"},
|
||||
},
|
||||
httpStubs: func(reg *httpmock.Registry) {
|
||||
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
|
||||
"assets": [
|
||||
{ "name": "windows-32bit.zip", "size": 12,
|
||||
"url": "https://api.github.com/assets/1234" },
|
||||
{ "name": "windows-64bit.zip", "size": 34,
|
||||
"url": "https://api.github.com/assets/3456" },
|
||||
{ "name": "linux.tgz", "size": 56,
|
||||
"url": "https://api.github.com/assets/5678" }
|
||||
],
|
||||
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
|
||||
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
|
||||
}`)
|
||||
|
||||
reg.Register(httpmock.REST("GET", "assets/1234"), httpmock.StringResponse(`1234`))
|
||||
},
|
||||
wantStdout: ``,
|
||||
wantStderr: ``,
|
||||
wantFiles: []string{
|
||||
|
|
@ -305,9 +443,85 @@ func Test_downloadRun(t *testing.T) {
|
|||
Concurrency: 2,
|
||||
FilePatterns: []string{"*windows-32bit.zip"},
|
||||
},
|
||||
httpStubs: func(reg *httpmock.Registry) {
|
||||
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
|
||||
"assets": [
|
||||
{ "name": "windows-32bit.zip", "size": 12,
|
||||
"url": "https://api.github.com/assets/1234" },
|
||||
{ "name": "windows-64bit.zip", "size": 34,
|
||||
"url": "https://api.github.com/assets/3456" },
|
||||
{ "name": "linux.tgz", "size": 56,
|
||||
"url": "https://api.github.com/assets/5678" }
|
||||
],
|
||||
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
|
||||
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
|
||||
}`)
|
||||
|
||||
reg.Register(httpmock.REST("GET", "assets/1234"), httpmock.StringResponse(`1234`))
|
||||
},
|
||||
wantStdout: `1234`,
|
||||
wantStderr: ``,
|
||||
},
|
||||
{
|
||||
name: "draft release with null tarball_url and zipball_url",
|
||||
isTTY: true,
|
||||
opts: DownloadOptions{
|
||||
TagName: "v1.2.3",
|
||||
ArchiveType: "tar.gz",
|
||||
Destination: "tmp/packages",
|
||||
Concurrency: 2,
|
||||
},
|
||||
httpStubs: func(reg *httpmock.Registry) {
|
||||
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
|
||||
"tag_name": "v1.2.3",
|
||||
"name": "patch-36",
|
||||
"assets": [
|
||||
{ "name": "windows-32bit.zip", "size": 12,
|
||||
"url": "https://api.github.com/assets/1234" },
|
||||
{ "name": "windows-64bit.zip", "size": 34,
|
||||
"url": "https://api.github.com/assets/3456" },
|
||||
{ "name": "linux.tgz", "size": 56,
|
||||
"url": "https://api.github.com/assets/5678" }
|
||||
],
|
||||
"tarball_url": null,
|
||||
"zipball_url": null,
|
||||
"draft": true
|
||||
}`)
|
||||
},
|
||||
wantStdout: ``,
|
||||
wantStderr: ``,
|
||||
wantErr: "release \"patch-36\" with tag \"v1.2.3\", does not have a \"tar.gz\" archive asset. Most likely, this is because it is a draft.",
|
||||
},
|
||||
{
|
||||
name: "non-draft release with null tarball_url and zipball_url",
|
||||
isTTY: true,
|
||||
opts: DownloadOptions{
|
||||
TagName: "v1.2.3",
|
||||
ArchiveType: "tar.gz",
|
||||
Destination: "tmp/packages",
|
||||
Concurrency: 2,
|
||||
},
|
||||
httpStubs: func(reg *httpmock.Registry) {
|
||||
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
|
||||
"tag_name": "v1.2.3",
|
||||
"name": "patch-36",
|
||||
"assets": [
|
||||
{ "name": "windows-32bit.zip", "size": 12,
|
||||
"url": "https://api.github.com/assets/1234" },
|
||||
{ "name": "windows-64bit.zip", "size": 34,
|
||||
"url": "https://api.github.com/assets/3456" },
|
||||
{ "name": "linux.tgz", "size": 56,
|
||||
"url": "https://api.github.com/assets/5678" }
|
||||
],
|
||||
"tarball_url": null,
|
||||
"zipball_url": null,
|
||||
"draft": false
|
||||
}`)
|
||||
},
|
||||
wantStdout: ``,
|
||||
wantStderr: ``,
|
||||
wantErr: "release \"patch-36\" with tag \"v1.2.3\", does not have a \"tar.gz\" archive asset.",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
@ -324,41 +538,11 @@ func Test_downloadRun(t *testing.T) {
|
|||
ios.SetStderrTTY(tt.isTTY)
|
||||
|
||||
fakeHTTP := &httpmock.Registry{}
|
||||
shared.StubFetchRelease(t, fakeHTTP, "OWNER", "REPO", tt.opts.TagName, `{
|
||||
"assets": [
|
||||
{ "name": "windows-32bit.zip", "size": 12,
|
||||
"url": "https://api.github.com/assets/1234" },
|
||||
{ "name": "windows-64bit.zip", "size": 34,
|
||||
"url": "https://api.github.com/assets/3456" },
|
||||
{ "name": "linux.tgz", "size": 56,
|
||||
"url": "https://api.github.com/assets/5678" }
|
||||
],
|
||||
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
|
||||
"zipball_url": "https://api.github.com/repos/OWNER/REPO/zipball/v1.2.3"
|
||||
}`)
|
||||
fakeHTTP.Register(httpmock.REST("GET", "assets/1234"), httpmock.StringResponse(`1234`))
|
||||
fakeHTTP.Register(httpmock.REST("GET", "assets/3456"), httpmock.StringResponse(`3456`))
|
||||
fakeHTTP.Register(httpmock.REST("GET", "assets/5678"), httpmock.StringResponse(`5678`))
|
||||
defer fakeHTTP.Verify(t)
|
||||
|
||||
fakeHTTP.Register(
|
||||
httpmock.REST(
|
||||
"GET",
|
||||
"repos/OWNER/REPO/tarball/v1.2.3",
|
||||
),
|
||||
httpmock.WithHeader(
|
||||
httpmock.StringResponse("somedata"), "content-disposition", "attachment; filename=tarball.tgz",
|
||||
),
|
||||
)
|
||||
|
||||
fakeHTTP.Register(
|
||||
httpmock.REST(
|
||||
"GET",
|
||||
"repos/OWNER/REPO/zipball/v1.2.3",
|
||||
),
|
||||
httpmock.WithHeader(
|
||||
httpmock.StringResponse("somedata"), "content-disposition", "attachment; filename=zipball.zip",
|
||||
),
|
||||
)
|
||||
if tt.httpStubs != nil {
|
||||
tt.httpStubs(fakeHTTP)
|
||||
}
|
||||
|
||||
tt.opts.IO = ios
|
||||
tt.opts.HttpClient = func() (*http.Client, error) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue