cli/command/pr_test.go
2020-07-16 15:21:50 +02:00

1409 lines
44 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package command
import (
"bytes"
"os/exec"
"reflect"
"regexp"
"strings"
"testing"
"github.com/cli/cli/api"
"github.com/cli/cli/internal/run"
"github.com/cli/cli/pkg/httpmock"
"github.com/cli/cli/test"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
)
func eq(t *testing.T, got interface{}, expected interface{}) {
t.Helper()
if !reflect.DeepEqual(got, expected) {
t.Errorf("expected: %v, got: %v", expected, got)
}
}
func TestPRStatus(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(httpmock.GraphQL(`query PullRequestStatus\b`), httpmock.FileResponse("../test/fixtures/prStatus.json"))
output, err := RunCommand("pr status")
if err != nil {
t.Errorf("error running command `pr status`: %v", err)
}
expectedPrs := []*regexp.Regexp{
regexp.MustCompile(`#8.*\[strawberries\]`),
regexp.MustCompile(`#9.*\[apples\]`),
regexp.MustCompile(`#10.*\[blueberries\]`),
regexp.MustCompile(`#11.*\[figs\]`),
}
for _, r := range expectedPrs {
if !r.MatchString(output.String()) {
t.Errorf("output did not match regexp /%s/", r)
}
}
}
func TestPRStatus_fork(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubForkedRepoResponse("OWNER/REPO", "PARENT/REPO")
http.Register(httpmock.GraphQL(`query PullRequestStatus\b`), httpmock.FileResponse("../test/fixtures/prStatusFork.json"))
defer run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
switch strings.Join(cmd.Args, " ") {
case `git config --get-regexp ^branch\.blueberries\.(remote|merge)$`:
return &test.OutputStub{Out: []byte(`branch.blueberries.remote origin
branch.blueberries.merge refs/heads/blueberries`)}
default:
panic("not implemented")
}
})()
output, err := RunCommand("pr status")
if err != nil {
t.Fatalf("error running command `pr status`: %v", err)
}
branchRE := regexp.MustCompile(`#10.*\[OWNER:blueberries\]`)
if !branchRE.MatchString(output.String()) {
t.Errorf("did not match current branch:\n%v", output.String())
}
}
func TestPRStatus_reviewsAndChecks(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(httpmock.GraphQL(`query PullRequestStatus\b`), httpmock.FileResponse("../test/fixtures/prStatusChecks.json"))
output, err := RunCommand("pr status")
if err != nil {
t.Errorf("error running command `pr status`: %v", err)
}
expected := []string{
"✓ Checks passing + Changes requested",
"- Checks pending ✓ Approved",
"× 1/3 checks failing - Review required",
}
for _, line := range expected {
if !strings.Contains(output.String(), line) {
t.Errorf("output did not contain %q: %q", line, output.String())
}
}
}
func TestPRStatus_currentBranch_showTheMostRecentPR(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(httpmock.GraphQL(`query PullRequestStatus\b`), httpmock.FileResponse("../test/fixtures/prStatusCurrentBranch.json"))
output, err := RunCommand("pr status")
if err != nil {
t.Errorf("error running command `pr status`: %v", err)
}
expectedLine := regexp.MustCompile(`#10 Blueberries are certainly a good fruit \[blueberries\]`)
if !expectedLine.MatchString(output.String()) {
t.Errorf("output did not match regexp /%s/\n> output\n%s\n", expectedLine, output)
return
}
unexpectedLines := []*regexp.Regexp{
regexp.MustCompile(`#9 Blueberries are a good fruit \[blueberries\] - Merged`),
regexp.MustCompile(`#8 Blueberries are probably a good fruit \[blueberries\] - Closed`),
}
for _, r := range unexpectedLines {
if r.MatchString(output.String()) {
t.Errorf("output unexpectedly match regexp /%s/\n> output\n%s\n", r, output)
return
}
}
}
func TestPRStatus_currentBranch_defaultBranch(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponseWithDefaultBranch("OWNER", "REPO", "blueberries")
http.Register(httpmock.GraphQL(`query PullRequestStatus\b`), httpmock.FileResponse("../test/fixtures/prStatusCurrentBranch.json"))
output, err := RunCommand("pr status")
if err != nil {
t.Errorf("error running command `pr status`: %v", err)
}
expectedLine := regexp.MustCompile(`#10 Blueberries are certainly a good fruit \[blueberries\]`)
if !expectedLine.MatchString(output.String()) {
t.Errorf("output did not match regexp /%s/\n> output\n%s\n", expectedLine, output)
return
}
}
func TestPRStatus_currentBranch_defaultBranch_repoFlag(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.Register(httpmock.GraphQL(`query PullRequestStatus\b`), httpmock.FileResponse("../test/fixtures/prStatusCurrentBranchClosedOnDefaultBranch.json"))
output, err := RunCommand("pr status -R OWNER/REPO")
if err != nil {
t.Errorf("error running command `pr status`: %v", err)
}
expectedLine := regexp.MustCompile(`#8 Blueberries are a good fruit \[blueberries\]`)
if expectedLine.MatchString(output.String()) {
t.Errorf("output not expected to match regexp /%s/\n> output\n%s\n", expectedLine, output)
return
}
}
func TestPRStatus_currentBranch_Closed(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(httpmock.GraphQL(`query PullRequestStatus\b`), httpmock.FileResponse("../test/fixtures/prStatusCurrentBranchClosed.json"))
output, err := RunCommand("pr status")
if err != nil {
t.Errorf("error running command `pr status`: %v", err)
}
expectedLine := regexp.MustCompile(`#8 Blueberries are a good fruit \[blueberries\] - Closed`)
if !expectedLine.MatchString(output.String()) {
t.Errorf("output did not match regexp /%s/\n> output\n%s\n", expectedLine, output)
return
}
}
func TestPRStatus_currentBranch_Closed_defaultBranch(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponseWithDefaultBranch("OWNER", "REPO", "blueberries")
http.Register(httpmock.GraphQL(`query PullRequestStatus\b`), httpmock.FileResponse("../test/fixtures/prStatusCurrentBranchClosedOnDefaultBranch.json"))
output, err := RunCommand("pr status")
if err != nil {
t.Errorf("error running command `pr status`: %v", err)
}
expectedLine := regexp.MustCompile(`There is no pull request associated with \[blueberries\]`)
if !expectedLine.MatchString(output.String()) {
t.Errorf("output did not match regexp /%s/\n> output\n%s\n", expectedLine, output)
return
}
}
func TestPRStatus_currentBranch_Merged(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(httpmock.GraphQL(`query PullRequestStatus\b`), httpmock.FileResponse("../test/fixtures/prStatusCurrentBranchMerged.json"))
output, err := RunCommand("pr status")
if err != nil {
t.Errorf("error running command `pr status`: %v", err)
}
expectedLine := regexp.MustCompile(`#8 Blueberries are a good fruit \[blueberries\] - Merged`)
if !expectedLine.MatchString(output.String()) {
t.Errorf("output did not match regexp /%s/\n> output\n%s\n", expectedLine, output)
return
}
}
func TestPRStatus_currentBranch_Merged_defaultBranch(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponseWithDefaultBranch("OWNER", "REPO", "blueberries")
http.Register(httpmock.GraphQL(`query PullRequestStatus\b`), httpmock.FileResponse("../test/fixtures/prStatusCurrentBranchMergedOnDefaultBranch.json"))
output, err := RunCommand("pr status")
if err != nil {
t.Errorf("error running command `pr status`: %v", err)
}
expectedLine := regexp.MustCompile(`There is no pull request associated with \[blueberries\]`)
if !expectedLine.MatchString(output.String()) {
t.Errorf("output did not match regexp /%s/\n> output\n%s\n", expectedLine, output)
return
}
}
func TestPRStatus_blankSlate(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(httpmock.GraphQL(`query PullRequestStatus\b`), httpmock.StringResponse(`{"data": {}}`))
output, err := RunCommand("pr status")
if err != nil {
t.Errorf("error running command `pr status`: %v", err)
}
expected := `
Relevant pull requests in OWNER/REPO
Current branch
There is no pull request associated with [blueberries]
Created by you
You have no open pull requests
Requesting a code review from you
You have no pull requests to review
`
if output.String() != expected {
t.Errorf("expected %q, got %q", expected, output.String())
}
}
func TestPRStatus_detachedHead(t *testing.T) {
initBlankContext("", "OWNER/REPO", "")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(httpmock.GraphQL(`query PullRequestStatus\b`), httpmock.StringResponse(`{"data": {}}`))
output, err := RunCommand("pr status")
if err != nil {
t.Errorf("error running command `pr status`: %v", err)
}
expected := `
Relevant pull requests in OWNER/REPO
Current branch
There is no current branch
Created by you
You have no open pull requests
Requesting a code review from you
You have no pull requests to review
`
if output.String() != expected {
t.Errorf("expected %q, got %q", expected, output.String())
}
}
func TestPRList(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(httpmock.GraphQL(`query PullRequestList\b`), httpmock.FileResponse("../test/fixtures/prList.json"))
output, err := RunCommand("pr list")
if err != nil {
t.Fatal(err)
}
eq(t, output.Stderr(), `
Showing 3 of 3 pull requests in OWNER/REPO
`)
eq(t, output.String(), `32 New feature feature
29 Fixed bad bug hubot:bug-fix
28 Improve documentation docs
`)
}
func TestPRList_filtering(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(
httpmock.GraphQL(`query PullRequestList\b`),
httpmock.GraphQLQuery(`{}`, func(_ string, params map[string]interface{}) {
assert.Equal(t, []interface{}{"OPEN", "CLOSED", "MERGED"}, params["state"].([]interface{}))
assert.Equal(t, []interface{}{"one", "two", "three"}, params["labels"].([]interface{}))
}))
output, err := RunCommand(`pr list -s all -l one,two -l three`)
if err != nil {
t.Fatal(err)
}
eq(t, output.String(), "")
eq(t, output.Stderr(), `
No pull requests match your search in OWNER/REPO
`)
}
func TestPRList_filteringRemoveDuplicate(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(
httpmock.GraphQL(`query PullRequestList\b`),
httpmock.FileResponse("../test/fixtures/prListWithDuplicates.json"))
output, err := RunCommand("pr list -l one,two")
if err != nil {
t.Fatal(err)
}
eq(t, output.String(), `32 New feature feature
29 Fixed bad bug hubot:bug-fix
28 Improve documentation docs
`)
}
func TestPRList_filteringClosed(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(
httpmock.GraphQL(`query PullRequestList\b`),
httpmock.GraphQLQuery(`{}`, func(_ string, params map[string]interface{}) {
assert.Equal(t, []interface{}{"CLOSED", "MERGED"}, params["state"].([]interface{}))
}))
_, err := RunCommand(`pr list -s closed`)
if err != nil {
t.Fatal(err)
}
}
func TestPRList_filteringAssignee(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(
httpmock.GraphQL(`query PullRequestList\b`),
httpmock.GraphQLQuery(`{}`, func(_ string, params map[string]interface{}) {
assert.Equal(t, `repo:OWNER/REPO assignee:hubot is:pr sort:created-desc is:merged label:"needs tests" base:"develop"`, params["q"].(string))
}))
_, err := RunCommand(`pr list -s merged -l "needs tests" -a hubot -B develop`)
if err != nil {
t.Fatal(err)
}
}
func TestPRList_filteringAssigneeLabels(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
initFakeHTTP()
_, err := RunCommand(`pr list -l one,two -a hubot`)
if err == nil && err.Error() != "multiple labels with --assignee are not supported" {
t.Fatal(err)
}
}
func TestPRList_withInvalidLimitFlag(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
initFakeHTTP()
_, err := RunCommand(`pr list --limit=0`)
if err == nil && err.Error() != "invalid limit: 0" {
t.Errorf("error running command `issue list`: %v", err)
}
}
func TestPRList_web(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
var seenCmd *exec.Cmd
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
seenCmd = cmd
return &test.OutputStub{}
})
defer restoreCmd()
output, err := RunCommand("pr list --web -a peter -l bug -l docs -L 10 -s merged -B trunk")
if err != nil {
t.Errorf("error running command `pr list` with `--web` flag: %v", err)
}
expectedURL := "https://github.com/OWNER/REPO/pulls?q=is%3Apr+is%3Amerged+assignee%3Apeter+label%3Abug+label%3Adocs+base%3Atrunk"
eq(t, output.String(), "")
eq(t, output.Stderr(), "Opening github.com/OWNER/REPO/pulls in your browser.\n")
if seenCmd == nil {
t.Fatal("expected a command to run")
}
url := seenCmd.Args[len(seenCmd.Args)-1]
eq(t, url, expectedURL)
}
func TestPRView_Preview(t *testing.T) {
tests := map[string]struct {
ownerRepo string
args string
fixture string
expectedOutputs []string
}{
"Open PR without metadata": {
ownerRepo: "master",
args: "pr view 12",
fixture: "../test/fixtures/prViewPreview.json",
expectedOutputs: []string{
`Blueberries are from a fork`,
`Open • nobody wants to merge 12 commits into master from blueberries`,
`blueberries taste good`,
`View this pull request on GitHub: https://github.com/OWNER/REPO/pull/12`,
},
},
"Open PR with metadata by number": {
ownerRepo: "master",
args: "pr view 12",
fixture: "../test/fixtures/prViewPreviewWithMetadataByNumber.json",
expectedOutputs: []string{
`Blueberries are from a fork`,
`Open • nobody wants to merge 12 commits into master from blueberries`,
`Reviewers: 2 \(Approved\), 3 \(Commented\), 1 \(Requested\)\n`,
`Assignees: marseilles, monaco\n`,
`Labels: one, two, three, four, five\n`,
`Projects: Project 1 \(column A\), Project 2 \(column B\), Project 3 \(column C\), Project 4 \(Awaiting triage\)\n`,
`Milestone: uluru\n`,
`blueberries taste good`,
`View this pull request on GitHub: https://github.com/OWNER/REPO/pull/12\n`,
},
},
"Open PR with reviewers by number": {
ownerRepo: "master",
args: "pr view 12",
fixture: "../test/fixtures/prViewPreviewWithReviewersByNumber.json",
expectedOutputs: []string{
`Blueberries are from a fork`,
`Reviewers: DEF \(Commented\), def \(Changes requested\), ghost \(Approved\), hubot \(Commented\), xyz \(Approved\), 123 \(Requested\), Team 1 \(Requested\), abc \(Requested\)\n`,
`blueberries taste good`,
`View this pull request on GitHub: https://github.com/OWNER/REPO/pull/12\n`,
},
},
"Open PR with metadata by branch": {
ownerRepo: "master",
args: "pr view blueberries",
fixture: "../test/fixtures/prViewPreviewWithMetadataByBranch.json",
expectedOutputs: []string{
`Blueberries are a good fruit`,
`Open • nobody wants to merge 8 commits into master from blueberries`,
`Assignees: marseilles, monaco\n`,
`Labels: one, two, three, four, five\n`,
`Projects: Project 1 \(column A\), Project 2 \(column B\), Project 3 \(column C\)\n`,
`Milestone: uluru\n`,
`blueberries taste good`,
`View this pull request on GitHub: https://github.com/OWNER/REPO/pull/10\n`,
},
},
"Open PR for the current branch": {
ownerRepo: "blueberries",
args: "pr view",
fixture: "../test/fixtures/prView.json",
expectedOutputs: []string{
`Blueberries are a good fruit`,
`Open • nobody wants to merge 8 commits into master from blueberries`,
`blueberries taste good`,
`View this pull request on GitHub: https://github.com/OWNER/REPO/pull/10`,
},
},
"Open PR wth empty body for the current branch": {
ownerRepo: "blueberries",
args: "pr view",
fixture: "../test/fixtures/prView_EmptyBody.json",
expectedOutputs: []string{
`Blueberries are a good fruit`,
`Open • nobody wants to merge 8 commits into master from blueberries`,
`View this pull request on GitHub: https://github.com/OWNER/REPO/pull/10`,
},
},
"Closed PR": {
ownerRepo: "master",
args: "pr view 12",
fixture: "../test/fixtures/prViewPreviewClosedState.json",
expectedOutputs: []string{
`Blueberries are from a fork`,
`Closed • nobody wants to merge 12 commits into master from blueberries`,
`blueberries taste good`,
`View this pull request on GitHub: https://github.com/OWNER/REPO/pull/12`,
},
},
"Merged PR": {
ownerRepo: "master",
args: "pr view 12",
fixture: "../test/fixtures/prViewPreviewMergedState.json",
expectedOutputs: []string{
`Blueberries are from a fork`,
`Merged • nobody wants to merge 12 commits into master from blueberries`,
`blueberries taste good`,
`View this pull request on GitHub: https://github.com/OWNER/REPO/pull/12`,
},
},
"Draft PR": {
ownerRepo: "master",
args: "pr view 12",
fixture: "../test/fixtures/prViewPreviewDraftState.json",
expectedOutputs: []string{
`Blueberries are from a fork`,
`Draft • nobody wants to merge 12 commits into master from blueberries`,
`blueberries taste good`,
`View this pull request on GitHub: https://github.com/OWNER/REPO/pull/12`,
},
},
"Draft PR by branch": {
ownerRepo: "master",
args: "pr view blueberries",
fixture: "../test/fixtures/prViewPreviewDraftStatebyBranch.json",
expectedOutputs: []string{
`Blueberries are a good fruit`,
`Draft • nobody wants to merge 8 commits into master from blueberries`,
`blueberries taste good`,
`View this pull request on GitHub: https://github.com/OWNER/REPO/pull/10`,
},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
initBlankContext("", "OWNER/REPO", tc.ownerRepo)
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(httpmock.GraphQL(`query PullRequest(ByNumber|ForBranch)\b`), httpmock.FileResponse(tc.fixture))
output, err := RunCommand(tc.args)
if err != nil {
t.Errorf("error running command `%v`: %v", tc.args, err)
}
eq(t, output.Stderr(), "")
test.ExpectLines(t, output.String(), tc.expectedOutputs...)
})
}
}
func TestPRView_web_currentBranch(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(httpmock.GraphQL(`query PullRequestForBranch\b`), httpmock.FileResponse("../test/fixtures/prView.json"))
var seenCmd *exec.Cmd
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
switch strings.Join(cmd.Args, " ") {
case `git config --get-regexp ^branch\.blueberries\.(remote|merge)$`:
return &test.OutputStub{}
default:
seenCmd = cmd
return &test.OutputStub{}
}
})
defer restoreCmd()
output, err := RunCommand("pr view -w")
if err != nil {
t.Errorf("error running command `pr view`: %v", err)
}
eq(t, output.String(), "")
eq(t, output.Stderr(), "Opening https://github.com/OWNER/REPO/pull/10 in your browser.\n")
if seenCmd == nil {
t.Fatal("expected a command to run")
}
url := seenCmd.Args[len(seenCmd.Args)-1]
if url != "https://github.com/OWNER/REPO/pull/10" {
t.Errorf("got: %q", url)
}
}
func TestPRView_web_noResultsForBranch(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.Register(httpmock.GraphQL(`query PullRequestForBranch\b`), httpmock.FileResponse("../test/fixtures/prView_NoActiveBranch.json"))
var seenCmd *exec.Cmd
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
switch strings.Join(cmd.Args, " ") {
case `git config --get-regexp ^branch\.blueberries\.(remote|merge)$`:
return &test.OutputStub{}
default:
seenCmd = cmd
return &test.OutputStub{}
}
})
defer restoreCmd()
_, err := RunCommand("pr view -w")
if err == nil || err.Error() != `no open pull requests found for branch "blueberries"` {
t.Errorf("error running command `pr view`: %v", err)
}
if seenCmd != nil {
t.Fatalf("unexpected command: %v", seenCmd.Args)
}
}
func TestPRView_web_numberArg(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": { "pullRequest": {
"url": "https://github.com/OWNER/REPO/pull/23"
} } } }
`))
var seenCmd *exec.Cmd
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
seenCmd = cmd
return &test.OutputStub{}
})
defer restoreCmd()
output, err := RunCommand("pr view -w 23")
if err != nil {
t.Errorf("error running command `pr view`: %v", err)
}
eq(t, output.String(), "")
if seenCmd == nil {
t.Fatal("expected a command to run")
}
url := seenCmd.Args[len(seenCmd.Args)-1]
eq(t, url, "https://github.com/OWNER/REPO/pull/23")
}
func TestPRView_web_numberArgWithHash(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": { "pullRequest": {
"url": "https://github.com/OWNER/REPO/pull/23"
} } } }
`))
var seenCmd *exec.Cmd
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
seenCmd = cmd
return &test.OutputStub{}
})
defer restoreCmd()
output, err := RunCommand("pr view -w \"#23\"")
if err != nil {
t.Errorf("error running command `pr view`: %v", err)
}
eq(t, output.String(), "")
if seenCmd == nil {
t.Fatal("expected a command to run")
}
url := seenCmd.Args[len(seenCmd.Args)-1]
eq(t, url, "https://github.com/OWNER/REPO/pull/23")
}
func TestPRView_web_urlArg(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": { "pullRequest": {
"url": "https://github.com/OWNER/REPO/pull/23"
} } } }
`))
var seenCmd *exec.Cmd
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
seenCmd = cmd
return &test.OutputStub{}
})
defer restoreCmd()
output, err := RunCommand("pr view -w https://github.com/OWNER/REPO/pull/23/files")
if err != nil {
t.Errorf("error running command `pr view`: %v", err)
}
eq(t, output.String(), "")
if seenCmd == nil {
t.Fatal("expected a command to run")
}
url := seenCmd.Args[len(seenCmd.Args)-1]
eq(t, url, "https://github.com/OWNER/REPO/pull/23")
}
func TestPRView_web_branchArg(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": { "pullRequests": { "nodes": [
{ "headRefName": "blueberries",
"isCrossRepository": false,
"url": "https://github.com/OWNER/REPO/pull/23" }
] } } } }
`))
var seenCmd *exec.Cmd
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
seenCmd = cmd
return &test.OutputStub{}
})
defer restoreCmd()
output, err := RunCommand("pr view -w blueberries")
if err != nil {
t.Errorf("error running command `pr view`: %v", err)
}
eq(t, output.String(), "")
if seenCmd == nil {
t.Fatal("expected a command to run")
}
url := seenCmd.Args[len(seenCmd.Args)-1]
eq(t, url, "https://github.com/OWNER/REPO/pull/23")
}
func TestPRView_web_branchWithOwnerArg(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": { "pullRequests": { "nodes": [
{ "headRefName": "blueberries",
"isCrossRepository": true,
"headRepositoryOwner": { "login": "hubot" },
"url": "https://github.com/hubot/REPO/pull/23" }
] } } } }
`))
var seenCmd *exec.Cmd
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
seenCmd = cmd
return &test.OutputStub{}
})
defer restoreCmd()
output, err := RunCommand("pr view -w hubot:blueberries")
if err != nil {
t.Errorf("error running command `pr view`: %v", err)
}
eq(t, output.String(), "")
if seenCmd == nil {
t.Fatal("expected a command to run")
}
url := seenCmd.Args[len(seenCmd.Args)-1]
eq(t, url, "https://github.com/hubot/REPO/pull/23")
}
func TestReplaceExcessiveWhitespace(t *testing.T) {
eq(t, replaceExcessiveWhitespace("hello\ngoodbye"), "hello goodbye")
eq(t, replaceExcessiveWhitespace(" hello goodbye "), "hello goodbye")
eq(t, replaceExcessiveWhitespace("hello goodbye"), "hello goodbye")
eq(t, replaceExcessiveWhitespace(" hello \n goodbye "), "hello goodbye")
}
func TestPrStateTitleWithColor(t *testing.T) {
tests := map[string]struct {
pr api.PullRequest
want string
}{
"Format OPEN state": {pr: api.PullRequest{State: "OPEN", IsDraft: false}, want: "Open"},
"Format OPEN state for Draft PR": {pr: api.PullRequest{State: "OPEN", IsDraft: true}, want: "Draft"},
"Format CLOSED state": {pr: api.PullRequest{State: "CLOSED", IsDraft: false}, want: "Closed"},
"Format MERGED state": {pr: api.PullRequest{State: "MERGED", IsDraft: false}, want: "Merged"},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
got := prStateTitleWithColor(tc.pr)
diff := cmp.Diff(tc.want, got)
if diff != "" {
t.Fatalf(diff)
}
})
}
}
func TestPrClose(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": {
"pullRequest": { "number": 96, "title": "The title of the PR" }
} } }
`))
http.StubResponse(200, bytes.NewBufferString(`{"id": "THE-ID"}`))
output, err := RunCommand("pr close 96")
if err != nil {
t.Fatalf("error running command `pr close`: %v", err)
}
r := regexp.MustCompile(`Closed pull request #96 \(The title of the PR\)`)
if !r.MatchString(output.Stderr()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.Stderr())
}
}
func TestPrClose_alreadyClosed(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": {
"pullRequest": { "number": 101, "title": "The title of the PR", "closed": true }
} } }
`))
http.StubResponse(200, bytes.NewBufferString(`{"id": "THE-ID"}`))
output, err := RunCommand("pr close 101")
if err != nil {
t.Fatalf("error running command `pr close`: %v", err)
}
r := regexp.MustCompile(`Pull request #101 \(The title of the PR\) is already closed`)
if !r.MatchString(output.Stderr()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.Stderr())
}
}
func TestPRReopen(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": {
"pullRequest": { "number": 666, "title": "The title of the PR", "closed": true}
} } }
`))
http.StubResponse(200, bytes.NewBufferString(`{"id": "THE-ID"}`))
output, err := RunCommand("pr reopen 666")
if err != nil {
t.Fatalf("error running command `pr reopen`: %v", err)
}
r := regexp.MustCompile(`Reopened pull request #666 \(The title of the PR\)`)
if !r.MatchString(output.Stderr()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.Stderr())
}
}
func TestPRReopen_alreadyOpen(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": {
"pullRequest": { "number": 666, "title": "The title of the PR", "closed": false}
} } }
`))
http.StubResponse(200, bytes.NewBufferString(`{"id": "THE-ID"}`))
output, err := RunCommand("pr reopen 666")
if err != nil {
t.Fatalf("error running command `pr reopen`: %v", err)
}
r := regexp.MustCompile(`Pull request #666 \(The title of the PR\) is already open`)
if !r.MatchString(output.Stderr()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.Stderr())
}
}
func TestPRReopen_alreadyMerged(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": {
"pullRequest": { "number": 666, "title": "The title of the PR", "closed": true, "state": "MERGED"}
} } }
`))
http.StubResponse(200, bytes.NewBufferString(`{"id": "THE-ID"}`))
output, err := RunCommand("pr reopen 666")
if err == nil {
t.Fatalf("expected an error running command `pr reopen`: %v", err)
}
r := regexp.MustCompile(`Pull request #666 \(The title of the PR\) can't be reopened because it was already merged`)
if !r.MatchString(err.Error()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.Stderr())
}
}
func TestPrMerge(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
defer http.Verify(t)
http.StubRepoResponse("OWNER", "REPO")
http.Register(
httpmock.GraphQL(`query PullRequestByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "pullRequest": {
"id": "THE-ID",
"number": 1,
"title": "The title of the PR",
"state": "OPEN",
"headRefName": "blueberries",
"headRepositoryOwner": {"login": "OWNER"}
} } } }`))
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
assert.Equal(t, "MERGE", input["mergeMethod"].(string))
}))
http.Register(
httpmock.REST("DELETE", "repos/OWNER/REPO/git/refs/heads/blueberries"),
httpmock.StringResponse(`{}`))
cs, cmdTeardown := test.InitCmdStubber()
defer cmdTeardown()
cs.Stub("branch.blueberries.remote origin\nbranch.blueberries.merge refs/heads/blueberries") // git config --get-regexp ^branch\.master\.(remote|merge)
cs.Stub("") // git config --get-regexp ^branch\.blueberries\.(remote|merge)$
cs.Stub("") // git symbolic-ref --quiet --short HEAD
cs.Stub("") // git checkout master
cs.Stub("")
output, err := RunCommand("pr merge 1 --merge")
if err != nil {
t.Fatalf("error running command `pr merge`: %v", err)
}
r := regexp.MustCompile(`Merged pull request #1 \(The title of the PR\)`)
if !r.MatchString(output.String()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.String())
}
}
func TestPrMerge_withRepoFlag(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query PullRequestByNumber\b`),
httpmock.GraphQLQuery(`
{ "data": { "repository": {
"pullRequest": { "number": 1, "title": "The title of the PR", "state": "OPEN", "id": "THE-ID"}
} } }`, func(_ string, params map[string]interface{}) {
assert.Equal(t, "stinky", params["owner"].(string))
assert.Equal(t, "boi", params["repo"].(string))
}))
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
assert.Equal(t, "MERGE", input["mergeMethod"].(string))
}))
cs, cmdTeardown := test.InitCmdStubber()
defer cmdTeardown()
eq(t, len(cs.Calls), 0)
output, err := RunCommand("pr merge 1 --merge -R stinky/boi")
if err != nil {
t.Fatalf("error running command `pr merge`: %v", err)
}
r := regexp.MustCompile(`Merged pull request #1 \(The title of the PR\)`)
if !r.MatchString(output.String()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.String())
}
}
func TestPrMerge_deleteBranch(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
defer http.Verify(t)
http.StubRepoResponse("OWNER", "REPO")
http.Register(
httpmock.GraphQL(`query PullRequestForBranch\b`),
httpmock.FileResponse("../test/fixtures/prViewPreviewWithMetadataByBranch.json"))
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, "PR_10", input["pullRequestId"].(string))
assert.Equal(t, "MERGE", input["mergeMethod"].(string))
}))
http.Register(
httpmock.REST("DELETE", "repos/OWNER/REPO/git/refs/heads/blueberries"),
httpmock.StringResponse(`{}`))
cs, cmdTeardown := test.InitCmdStubber()
defer cmdTeardown()
cs.Stub("") // git config --get-regexp ^branch\.blueberries\.(remote|merge)$
cs.Stub("") // git checkout master
cs.Stub("") // git rev-parse --verify blueberries`
cs.Stub("") // git branch -d
cs.Stub("") // git push origin --delete blueberries
output, err := RunCommand(`pr merge --merge --delete-branch`)
if err != nil {
t.Fatalf("Got unexpected error running `pr merge` %s", err)
}
test.ExpectLines(t, output.String(), `Merged pull request #10 \(Blueberries are a good fruit\)`, "Deleted branch blueberries")
}
func TestPrMerge_deleteNonCurrentBranch(t *testing.T) {
initBlankContext("", "OWNER/REPO", "another-branch")
http := initFakeHTTP()
defer http.Verify(t)
http.StubRepoResponse("OWNER", "REPO")
http.Register(
httpmock.GraphQL(`query PullRequestForBranch\b`),
httpmock.FileResponse("../test/fixtures/prViewPreviewWithMetadataByBranch.json"))
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, "PR_10", input["pullRequestId"].(string))
assert.Equal(t, "MERGE", input["mergeMethod"].(string))
}))
http.Register(
httpmock.REST("DELETE", "repos/OWNER/REPO/git/refs/heads/blueberries"),
httpmock.StringResponse(`{}`))
cs, cmdTeardown := test.InitCmdStubber()
defer cmdTeardown()
// We don't expect the default branch to be checked out, just that blueberries is deleted
cs.Stub("") // git rev-parse --verify blueberries
cs.Stub("") // git branch -d blueberries
cs.Stub("") // git push origin --delete blueberries
output, err := RunCommand(`pr merge --merge --delete-branch blueberries`)
if err != nil {
t.Fatalf("Got unexpected error running `pr merge` %s", err)
}
test.ExpectLines(t, output.String(), `Merged pull request #10 \(Blueberries are a good fruit\)`, "Deleted branch blueberries")
}
func TestPrMerge_noPrNumberGiven(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
defer http.Verify(t)
http.StubRepoResponse("OWNER", "REPO")
http.Register(
httpmock.GraphQL(`query PullRequestForBranch\b`),
httpmock.FileResponse("../test/fixtures/prViewPreviewWithMetadataByBranch.json"))
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, "PR_10", input["pullRequestId"].(string))
assert.Equal(t, "MERGE", input["mergeMethod"].(string))
}))
http.Register(
httpmock.REST("DELETE", "repos/OWNER/REPO/git/refs/heads/blueberries"),
httpmock.StringResponse(`{}`))
cs, cmdTeardown := test.InitCmdStubber()
defer cmdTeardown()
cs.Stub("branch.blueberries.remote origin\nbranch.blueberries.merge refs/heads/blueberries") // git config --get-regexp ^branch\.master\.(remote|merge)
cs.Stub("") // git config --get-regexp ^branch\.blueberries\.(remote|merge)$
cs.Stub("") // git symbolic-ref --quiet --short HEAD
cs.Stub("") // git checkout master
cs.Stub("") // git branch -d
output, err := RunCommand("pr merge --merge")
if err != nil {
t.Fatalf("error running command `pr merge`: %v", err)
}
r := regexp.MustCompile(`Merged pull request #10 \(Blueberries are a good fruit\)`)
if !r.MatchString(output.String()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.String())
}
}
func TestPrMerge_rebase(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
defer http.Verify(t)
http.StubRepoResponse("OWNER", "REPO")
http.Register(
httpmock.GraphQL(`query PullRequestByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "pullRequest": {
"id": "THE-ID",
"number": 2,
"title": "The title of the PR",
"state": "OPEN",
"headRefName": "blueberries",
"headRepositoryOwner": {"login": "OWNER"}
} } } }`))
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
assert.Equal(t, "REBASE", input["mergeMethod"].(string))
}))
http.Register(
httpmock.REST("DELETE", "repos/OWNER/REPO/git/refs/heads/blueberries"),
httpmock.StringResponse(`{}`))
cs, cmdTeardown := test.InitCmdStubber()
defer cmdTeardown()
cs.Stub("") // git config --get-regexp ^branch\.blueberries\.(remote|merge)$
cs.Stub("") // git symbolic-ref --quiet --short HEAD
cs.Stub("") // git checkout master
cs.Stub("") // git branch -d
output, err := RunCommand("pr merge 2 --rebase")
if err != nil {
t.Fatalf("error running command `pr merge`: %v", err)
}
r := regexp.MustCompile(`Rebased and merged pull request #2 \(The title of the PR\)`)
if !r.MatchString(output.String()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.String())
}
}
func TestPrMerge_squash(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
defer http.Verify(t)
http.StubRepoResponse("OWNER", "REPO")
http.Register(
httpmock.GraphQL(`query PullRequestByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "pullRequest": {
"id": "THE-ID",
"number": 3,
"title": "The title of the PR",
"state": "OPEN",
"headRefName": "blueberries",
"headRepositoryOwner": {"login": "OWNER"}
} } } }`))
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
assert.Equal(t, "SQUASH", input["mergeMethod"].(string))
}))
http.Register(
httpmock.REST("DELETE", "repos/OWNER/REPO/git/refs/heads/blueberries"),
httpmock.StringResponse(`{}`))
cs, cmdTeardown := test.InitCmdStubber()
defer cmdTeardown()
cs.Stub("") // git config --get-regexp ^branch\.blueberries\.(remote|merge)$
cs.Stub("") // git symbolic-ref --quiet --short HEAD
cs.Stub("") // git checkout master
cs.Stub("") // git branch -d
output, err := RunCommand("pr merge 3 --squash")
if err != nil {
t.Fatalf("error running command `pr merge`: %v", err)
}
expected := "✔ Squashed and merged pull request #3 (The title of the PR)\n✔ Deleted branch blueberries\n"
assert.Equal(t, expected, output.String())
}
func TestPrMerge_alreadyMerged(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
defer http.Verify(t)
http.StubRepoResponse("OWNER", "REPO")
http.Register(
httpmock.GraphQL(`query PullRequestByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"pullRequest": { "number": 4, "title": "The title of the PR", "state": "MERGED"}
} } }`))
cs, cmdTeardown := test.InitCmdStubber()
defer cmdTeardown()
cs.Stub("") // git config --get-regexp ^branch\.blueberries\.(remote|merge)$
cs.Stub("") // git symbolic-ref --quiet --short HEAD
cs.Stub("") // git checkout master
cs.Stub("") // git branch -d
output, err := RunCommand("pr merge 4")
if err == nil {
t.Fatalf("expected an error running command `pr merge`: %v", err)
}
r := regexp.MustCompile(`Pull request #4 \(The title of the PR\) was already merged`)
if !r.MatchString(err.Error()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.Stderr())
}
}
func TestPRMerge_interactive(t *testing.T) {
initBlankContext("", "OWNER/REPO", "blueberries")
http := initFakeHTTP()
defer http.Verify(t)
http.StubRepoResponse("OWNER", "REPO")
http.Register(
httpmock.GraphQL(`query PullRequestForBranch\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "pullRequests": { "nodes": [{
"headRefName": "blueberries",
"headRepositoryOwner": {"login": "OWNER"},
"id": "THE-ID",
"number": 3
}] } } } }`))
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
assert.Equal(t, "MERGE", input["mergeMethod"].(string))
}))
http.Register(
httpmock.REST("DELETE", "repos/OWNER/REPO/git/refs/heads/blueberries"),
httpmock.StringResponse(`{}`))
cs, cmdTeardown := test.InitCmdStubber()
defer cmdTeardown()
cs.Stub("") // git config --get-regexp ^branch\.blueberries\.(remote|merge)$
cs.Stub("") // git symbolic-ref --quiet --short HEAD
cs.Stub("") // git checkout master
cs.Stub("") // git push origin --delete blueberries
cs.Stub("") // git branch -d
as, surveyTeardown := initAskStubber()
defer surveyTeardown()
as.Stub([]*QuestionStub{
{
Name: "mergeMethod",
Value: 0,
},
{
Name: "deleteBranch",
Value: true,
},
})
output, err := RunCommand(`pr merge`)
if err != nil {
t.Fatalf("Got unexpected error running `pr merge` %s", err)
}
test.ExpectLines(t, output.String(), "Merged pull request #3", "Deleted branch blueberries")
}
func TestPrMerge_multipleMergeMethods(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
_, err := RunCommand("pr merge 1 --merge --squash")
if err == nil {
t.Fatal("expected error running `pr merge` with multiple merge methods")
}
}
func TestPRReady(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": {
"pullRequest": { "number": 444, "closed": false, "isDraft": true}
} } }
`))
http.StubResponse(200, bytes.NewBufferString(`{"id": "THE-ID"}`))
output, err := RunCommand("pr ready 444")
if err != nil {
t.Fatalf("error running command `pr ready`: %v", err)
}
r := regexp.MustCompile(`Pull request #444 is marked as "ready for review"`)
if !r.MatchString(output.Stderr()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.Stderr())
}
}
func TestPRReady_alreadyReady(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": {
"pullRequest": { "number": 445, "closed": false, "isDraft": false}
} } }
`))
http.StubResponse(200, bytes.NewBufferString(`{"id": "THE-ID"}`))
output, err := RunCommand("pr ready 445")
if err != nil {
t.Fatalf("error running command `pr ready`: %v", err)
}
r := regexp.MustCompile(`Pull request #445 is already "ready for review"`)
if !r.MatchString(output.Stderr()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.Stderr())
}
}
func TestPRReady_closed(t *testing.T) {
initBlankContext("", "OWNER/REPO", "master")
http := initFakeHTTP()
http.StubRepoResponse("OWNER", "REPO")
http.StubResponse(200, bytes.NewBufferString(`
{ "data": { "repository": {
"pullRequest": { "number": 446, "closed": true, "isDraft": true}
} } }
`))
http.StubResponse(200, bytes.NewBufferString(`{"id": "THE-ID"}`))
_, err := RunCommand("pr ready 446")
if err == nil {
t.Fatalf("expected an error running command `pr ready` on a review that is closed!: %v", err)
}
r := regexp.MustCompile(`Pull request #446 is closed. Only draft pull requests can be marked as "ready for review"`)
if !r.MatchString(err.Error()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, err.Error())
}
}