mirror of https://gitlab.com/gitlab-org/cli.git
387 lines
11 KiB
Go
387 lines
11 KiB
Go
package list
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitlab.com/gitlab-org/cli/pkg/iostreams"
|
|
|
|
"gitlab.com/gitlab-org/cli/commands/cmdtest"
|
|
|
|
"github.com/MakeNowJust/heredoc"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/xanzy/go-gitlab"
|
|
"gitlab.com/gitlab-org/cli/api"
|
|
"gitlab.com/gitlab-org/cli/commands/cmdutils"
|
|
"gitlab.com/gitlab-org/cli/internal/config"
|
|
"gitlab.com/gitlab-org/cli/internal/glrepo"
|
|
"gitlab.com/gitlab-org/cli/pkg/httpmock"
|
|
"gitlab.com/gitlab-org/cli/test"
|
|
)
|
|
|
|
func runCommand(rt http.RoundTripper, isTTY bool, cli string, runE func(opts *ListOptions) error, doHyperlinks string) (*test.CmdOut, error) {
|
|
ios, _, stdout, stderr := cmdtest.InitIOStreams(isTTY, doHyperlinks)
|
|
factory := cmdtest.InitFactory(ios, rt)
|
|
|
|
// TODO: shouldn't be there but the stub doesn't work without it
|
|
_, _ = factory.HttpClient()
|
|
|
|
cmd := NewCmdList(factory, runE)
|
|
|
|
return cmdtest.ExecuteCommand(cmd, cli, stdout, stderr)
|
|
}
|
|
|
|
func TestNewCmdList(t *testing.T) {
|
|
ios, _, _, _ := iostreams.Test()
|
|
ios.IsaTTY = true
|
|
ios.IsInTTY = true
|
|
ios.IsErrTTY = true
|
|
|
|
fakeHTTP := httpmock.New()
|
|
defer fakeHTTP.Verify(t)
|
|
|
|
factory := &cmdutils.Factory{
|
|
IO: ios,
|
|
HttpClient: func() (*gitlab.Client, error) {
|
|
a, err := api.TestClient(&http.Client{Transport: fakeHTTP}, "", "", false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return a.Lab(), err
|
|
},
|
|
Config: func() (config.Config, error) {
|
|
return config.NewBlankConfig(), nil
|
|
},
|
|
BaseRepo: func() (glrepo.Interface, error) {
|
|
return glrepo.New("OWNER", "REPO"), nil
|
|
},
|
|
}
|
|
t.Run("MergeRequest_NewCmdList", func(t *testing.T) {
|
|
gotOpts := &ListOptions{}
|
|
err := NewCmdList(factory, func(opts *ListOptions) error {
|
|
gotOpts = opts
|
|
return nil
|
|
}).Execute()
|
|
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, factory.IO, gotOpts.IO)
|
|
|
|
gotBaseRepo, _ := gotOpts.BaseRepo()
|
|
expectedBaseRepo, _ := factory.BaseRepo()
|
|
assert.Equal(t, gotBaseRepo, expectedBaseRepo)
|
|
})
|
|
}
|
|
|
|
func TestMergeRequestList_tty(t *testing.T) {
|
|
fakeHTTP := httpmock.New()
|
|
defer fakeHTTP.Verify(t)
|
|
|
|
fakeHTTP.RegisterResponder(http.MethodGet, "/projects/OWNER/REPO/merge_requests",
|
|
httpmock.NewStringResponse(http.StatusOK, `
|
|
[
|
|
{
|
|
"state" : "opened",
|
|
"description" : "a description here",
|
|
"project_id" : 1,
|
|
"updated_at" : "2016-01-04T15:31:51.081Z",
|
|
"id" : 76,
|
|
"title" : "MergeRequest one",
|
|
"created_at" : "2016-01-04T15:31:51.081Z",
|
|
"iid" : 6,
|
|
"labels" : ["foo", "bar"],
|
|
"target_branch": "master",
|
|
"source_branch": "test1",
|
|
"web_url": "http://gitlab.com/OWNER/REPO/merge_requests/6",
|
|
"references": {
|
|
"full": "OWNER/REPO/merge_requests/6",
|
|
"relative": "#6",
|
|
"short": "#6"
|
|
}
|
|
},
|
|
{
|
|
"state" : "opened",
|
|
"description" : "description two here",
|
|
"project_id" : 1,
|
|
"updated_at" : "2016-01-04T15:31:51.081Z",
|
|
"id" : 77,
|
|
"title" : "MergeRequest two",
|
|
"created_at" : "2016-01-04T15:31:51.081Z",
|
|
"iid" : 7,
|
|
"target_branch": "master",
|
|
"source_branch": "test2",
|
|
"labels" : ["fooz", "baz"],
|
|
"web_url": "http://gitlab.com/OWNER/REPO/merge_requests/7",
|
|
"references": {
|
|
"full": "OWNER/REPO/merge_requests/7",
|
|
"relative": "#7",
|
|
"short": "#7"
|
|
}
|
|
}
|
|
]
|
|
`))
|
|
|
|
output, err := runCommand(fakeHTTP, true, "", nil, "")
|
|
if err != nil {
|
|
t.Errorf("error running command `issue list`: %v", err)
|
|
}
|
|
|
|
assert.Equal(t, heredoc.Doc(`
|
|
Showing 2 open merge requests on OWNER/REPO (Page 1)
|
|
|
|
!6 OWNER/REPO/merge_requests/6 MergeRequest one (master) ← (test1)
|
|
!7 OWNER/REPO/merge_requests/7 MergeRequest two (master) ← (test2)
|
|
|
|
`), output.String())
|
|
assert.Equal(t, ``, output.Stderr())
|
|
}
|
|
|
|
func TestMergeRequestList_tty_withFlags(t *testing.T) {
|
|
t.Run("repo", func(t *testing.T) {
|
|
fakeHTTP := httpmock.New()
|
|
defer fakeHTTP.Verify(t)
|
|
|
|
fakeHTTP.RegisterResponder(http.MethodGet, "/projects/OWNER/REPO/merge_requests",
|
|
httpmock.NewStringResponse(http.StatusOK, `[]`))
|
|
|
|
fakeHTTP.RegisterResponder(http.MethodGet, "/users",
|
|
httpmock.NewStringResponse(http.StatusOK, `[{"id" : 1, "iid" : 1, "username": "john_smith"}]`))
|
|
|
|
output, err := runCommand(fakeHTTP, true, "--opened -P1 -p100 -a someuser -l bug -m1", nil, "")
|
|
if err != nil {
|
|
t.Errorf("error running command `issue list`: %v", err)
|
|
}
|
|
|
|
cmdtest.Eq(t, output.Stderr(), "")
|
|
cmdtest.Eq(t, output.String(), `No open merge requests match your search in OWNER/REPO
|
|
|
|
|
|
`)
|
|
})
|
|
t.Run("group", func(t *testing.T) {
|
|
fakeHTTP := httpmock.New()
|
|
defer fakeHTTP.Verify(t)
|
|
|
|
fakeHTTP.RegisterResponder(http.MethodGet, "/groups/GROUP/merge_requests",
|
|
httpmock.NewStringResponse(http.StatusOK, `[]`))
|
|
|
|
output, err := runCommand(fakeHTTP, true, "--group GROUP", nil, "")
|
|
if err != nil {
|
|
t.Errorf("error running command `mr list`: %v", err)
|
|
}
|
|
|
|
cmdtest.Eq(t, output.Stderr(), "")
|
|
cmdtest.Eq(t, output.String(), `No open merge requests available on GROUP
|
|
|
|
`)
|
|
})
|
|
}
|
|
|
|
func makeHyperlink(linkText, targetURL string) string {
|
|
return fmt.Sprintf("\x1b]8;;%s\x1b\\%s\x1b]8;;\x1b\\", targetURL, linkText)
|
|
}
|
|
|
|
func TestMergeRequestList_hyperlinks(t *testing.T) {
|
|
noHyperlinkCells := [][]string{
|
|
{"!6", "OWNER/REPO/merge_requests/6", "MergeRequest one", "(master) ← (test1)"},
|
|
{"!7", "OWNER/REPO/merge_requests/7", "MergeRequest two", "(master) ← (test2)"},
|
|
}
|
|
|
|
hyperlinkCells := [][]string{
|
|
{makeHyperlink("!6", "http://gitlab.com/OWNER/REPO/merge_requests/6"), "OWNER/REPO/merge_requests/6", "MergeRequest one", "(master) ← (test1)"},
|
|
{makeHyperlink("!7", "http://gitlab.com/OWNER/REPO/merge_requests/7"), "OWNER/REPO/merge_requests/7", "MergeRequest two", "(master) ← (test2)"},
|
|
}
|
|
|
|
type hyperlinkTest struct {
|
|
forceHyperlinksEnv string
|
|
displayHyperlinksConfig string
|
|
isTTY bool
|
|
|
|
expectedCells [][]string
|
|
}
|
|
|
|
tests := []hyperlinkTest{
|
|
// FORCE_HYPERLINKS causes hyperlinks to be output, whether or not we're talking to a TTY
|
|
{forceHyperlinksEnv: "1", isTTY: true, expectedCells: hyperlinkCells},
|
|
{forceHyperlinksEnv: "1", isTTY: false, expectedCells: hyperlinkCells},
|
|
|
|
// empty/missing display_hyperlinks in config defaults to *not* outputting hyperlinks
|
|
{displayHyperlinksConfig: "", isTTY: true, expectedCells: noHyperlinkCells},
|
|
{displayHyperlinksConfig: "", isTTY: false, expectedCells: noHyperlinkCells},
|
|
|
|
// display_hyperlinks: false in config prevents outputting hyperlinks
|
|
{displayHyperlinksConfig: "false", isTTY: true, expectedCells: noHyperlinkCells},
|
|
{displayHyperlinksConfig: "false", isTTY: false, expectedCells: noHyperlinkCells},
|
|
|
|
// display_hyperlinks: true in config only outputs hyperlinks if we're talking to a TTY
|
|
{displayHyperlinksConfig: "true", isTTY: true, expectedCells: hyperlinkCells},
|
|
{displayHyperlinksConfig: "true", isTTY: false, expectedCells: noHyperlinkCells},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run("", func(t *testing.T) {
|
|
fakeHTTP := httpmock.New()
|
|
defer fakeHTTP.Verify(t)
|
|
|
|
fakeHTTP.RegisterResponder(http.MethodGet, "/projects/OWNER/REPO/merge_requests",
|
|
httpmock.NewStringResponse(http.StatusOK, `
|
|
[
|
|
{
|
|
"state" : "opened",
|
|
"description" : "a description here",
|
|
"project_id" : 1,
|
|
"updated_at" : "2016-01-04T15:31:51.081Z",
|
|
"id" : 76,
|
|
"title" : "MergeRequest one",
|
|
"created_at" : "2016-01-04T15:31:51.081Z",
|
|
"iid" : 6,
|
|
"labels" : ["foo", "bar"],
|
|
"target_branch": "master",
|
|
"source_branch": "test1",
|
|
"web_url": "http://gitlab.com/OWNER/REPO/merge_requests/6",
|
|
"references": {
|
|
"full": "OWNER/REPO/merge_requests/6",
|
|
"relative": "#6",
|
|
"short": "#6"
|
|
}
|
|
},
|
|
{
|
|
"state" : "opened",
|
|
"description" : "description two here",
|
|
"project_id" : 1,
|
|
"updated_at" : "2016-01-04T15:31:51.081Z",
|
|
"id" : 77,
|
|
"title" : "MergeRequest two",
|
|
"created_at" : "2016-01-04T15:31:51.081Z",
|
|
"iid" : 7,
|
|
"target_branch": "master",
|
|
"source_branch": "test2",
|
|
"labels" : ["fooz", "baz"],
|
|
"web_url": "http://gitlab.com/OWNER/REPO/merge_requests/7",
|
|
"references": {
|
|
"full": "OWNER/REPO/merge_requests/7",
|
|
"relative": "#7",
|
|
"short": "#7"
|
|
}
|
|
}
|
|
]
|
|
`))
|
|
|
|
doHyperlinks := "never"
|
|
if test.forceHyperlinksEnv == "1" {
|
|
doHyperlinks = "always"
|
|
} else if test.displayHyperlinksConfig == "true" {
|
|
doHyperlinks = "auto"
|
|
}
|
|
|
|
output, err := runCommand(fakeHTTP, test.isTTY, "", nil, doHyperlinks)
|
|
if err != nil {
|
|
t.Errorf("error running command `mr list`: %v", err)
|
|
}
|
|
|
|
out := output.String()
|
|
|
|
lines := strings.Split(strings.TrimRight(out, "\n"), "\n")
|
|
|
|
// first two lines have the header and some separating whitespace, so skip those
|
|
for lineNum, line := range lines[2:] {
|
|
gotCells := strings.Split(line, "\t")
|
|
expectedCells := test.expectedCells[lineNum]
|
|
|
|
assert.Equal(t, len(expectedCells), len(gotCells))
|
|
|
|
for cellNum, gotCell := range gotCells {
|
|
expectedCell := expectedCells[cellNum]
|
|
|
|
assert.Equal(t, expectedCell, strings.Trim(gotCell, " "))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMergeRequestList_labels(t *testing.T) {
|
|
type labelTest struct {
|
|
cli string
|
|
expectedQuery string
|
|
}
|
|
|
|
tests := []labelTest{
|
|
{cli: "--label foo", expectedQuery: "labels=foo&page=1&per_page=30&state=opened"},
|
|
{cli: "--not-label fooz", expectedQuery: "not%5Blabels%5D=fooz&page=1&per_page=30&state=opened"},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run("", func(t *testing.T) {
|
|
fakeHTTP := &httpmock.Mocker{
|
|
MatchURL: httpmock.PathAndQuerystring,
|
|
}
|
|
defer fakeHTTP.Verify(t)
|
|
|
|
path := fmt.Sprintf("/api/v4/projects/OWNER/REPO/merge_requests?%s", test.expectedQuery)
|
|
fakeHTTP.RegisterResponder(http.MethodGet, path,
|
|
httpmock.NewStringResponse(http.StatusOK, `
|
|
[
|
|
{
|
|
"state" : "opened",
|
|
"description" : "a description here",
|
|
"project_id" : 1,
|
|
"updated_at" : "2016-01-04T15:31:51.081Z",
|
|
"id" : 76,
|
|
"title" : "MergeRequest one",
|
|
"created_at" : "2016-01-04T15:31:51.081Z",
|
|
"iid" : 6,
|
|
"labels" : ["foo", "bar"],
|
|
"target_branch": "master",
|
|
"source_branch": "test1",
|
|
"web_url": "http://gitlab.com/OWNER/REPO/merge_requests/6",
|
|
"references": {
|
|
"full": "OWNER/REPO/merge_requests/6",
|
|
"relative": "#6",
|
|
"short": "#6"
|
|
}
|
|
}
|
|
]
|
|
`))
|
|
output, err := runCommand(fakeHTTP, true, test.cli, nil, "")
|
|
if err != nil {
|
|
t.Errorf("error running command `issue list %s`: %v", test.cli, err)
|
|
}
|
|
|
|
assert.Contains(t, output.String(), "!6 OWNER/REPO/merge_requests/6")
|
|
assert.Empty(t, output.Stderr())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMrListJSON(t *testing.T) {
|
|
fakeHTTP := httpmock.New()
|
|
fakeHTTP.MatchURL = httpmock.PathAndQuerystring
|
|
defer fakeHTTP.Verify(t)
|
|
|
|
fakeHTTP.RegisterResponder(http.MethodGet, "/api/v4/projects/OWNER/REPO/merge_requests?page=1&per_page=30&state=opened",
|
|
httpmock.NewFileResponse(http.StatusOK, "./testdata/mrList.json"))
|
|
|
|
output, err := runCommand(fakeHTTP, true, "-F json", nil, "")
|
|
if err != nil {
|
|
t.Errorf("error running command `mr list -F json`: %v", err)
|
|
}
|
|
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
b, err := os.ReadFile("./testdata/mrList.json")
|
|
if err != nil {
|
|
fmt.Print(err)
|
|
}
|
|
|
|
expectedOut := string(b)
|
|
|
|
assert.JSONEq(t, expectedOut, output.String())
|
|
assert.Empty(t, output.Stderr())
|
|
}
|