package command import ( "bytes" "encoding/json" "io/ioutil" "regexp" "testing" "github.com/cli/cli/pkg/prompt" "github.com/cli/cli/test" "github.com/stretchr/testify/assert" ) func TestPRReview_validation(t *testing.T) { initBlankContext("", "OWNER/REPO", "master") http := initFakeHTTP() for _, cmd := range []string{ `pr review --approve --comment 123`, `pr review --approve --comment -b"hey" 123`, } { http.StubRepoResponse("OWNER", "REPO") http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { "pullRequest": { "number": 123 } } } } `)) _, err := RunCommand(cmd) if err == nil { t.Fatal("expected error") } eq(t, err.Error(), "did not understand desired review action: need exactly one of --approve, --request-changes, or --comment") } } func TestPRReview_bad_body(t *testing.T) { initBlankContext("", "OWNER/REPO", "master") http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { "pullRequest": { "number": 123 } } } } `)) _, err := RunCommand(`pr review 123 -b "radical"`) if err == nil { t.Fatal("expected error") } eq(t, err.Error(), "did not understand desired review action: --body unsupported without --approve, --request-changes, or --comment") } func TestPRReview_url_arg(t *testing.T) { initBlankContext("", "OWNER/REPO", "master") defer stubTerminal(true)() http := initFakeHTTP() http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { "pullRequest": { "id": "foobar123", "number": 123, "headRefName": "feature", "headRepositoryOwner": { "login": "hubot" }, "headRepository": { "name": "REPO", "defaultBranchRef": { "name": "master" } }, "isCrossRepository": false, "maintainerCanModify": false } } } } `)) http.StubResponse(200, bytes.NewBufferString(`{"data": {} }`)) output, err := RunCommand("pr review --approve https://github.com/OWNER/REPO/pull/123") if err != nil { t.Fatalf("error running pr review: %s", err) } test.ExpectLines(t, output.Stderr(), "Approved pull request #123") bodyBytes, _ := ioutil.ReadAll(http.Requests[1].Body) reqBody := struct { Variables struct { Input struct { PullRequestID string Event string Body string } } }{} _ = json.Unmarshal(bodyBytes, &reqBody) eq(t, reqBody.Variables.Input.PullRequestID, "foobar123") eq(t, reqBody.Variables.Input.Event, "APPROVE") eq(t, reqBody.Variables.Input.Body, "") } func TestPRReview_number_arg(t *testing.T) { initBlankContext("", "OWNER/REPO", "master") defer stubTerminal(true)() http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { "pullRequest": { "id": "foobar123", "number": 123, "headRefName": "feature", "headRepositoryOwner": { "login": "hubot" }, "headRepository": { "name": "REPO", "defaultBranchRef": { "name": "master" } }, "isCrossRepository": false, "maintainerCanModify": false } } } } `)) http.StubResponse(200, bytes.NewBufferString(`{"data": {} }`)) output, err := RunCommand("pr review --approve 123") if err != nil { t.Fatalf("error running pr review: %s", err) } test.ExpectLines(t, output.Stderr(), "Approved pull request #123") bodyBytes, _ := ioutil.ReadAll(http.Requests[2].Body) reqBody := struct { Variables struct { Input struct { PullRequestID string Event string Body string } } }{} _ = json.Unmarshal(bodyBytes, &reqBody) eq(t, reqBody.Variables.Input.PullRequestID, "foobar123") eq(t, reqBody.Variables.Input.Event, "APPROVE") eq(t, reqBody.Variables.Input.Body, "") } func TestPRReview_no_arg(t *testing.T) { initBlankContext("", "OWNER/REPO", "feature") defer stubTerminal(true)() http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { "pullRequests": { "nodes": [ { "url": "https://github.com/OWNER/REPO/pull/123", "number": 123, "id": "foobar123", "headRefName": "feature", "baseRefName": "master" } ] } } } }`)) http.StubResponse(200, bytes.NewBufferString(`{"data": {} }`)) output, err := RunCommand(`pr review --comment -b "cool story"`) if err != nil { t.Fatalf("error running pr review: %s", err) } test.ExpectLines(t, output.Stderr(), "Reviewed pull request #123") bodyBytes, _ := ioutil.ReadAll(http.Requests[2].Body) reqBody := struct { Variables struct { Input struct { PullRequestID string Event string Body string } } }{} _ = json.Unmarshal(bodyBytes, &reqBody) eq(t, reqBody.Variables.Input.PullRequestID, "foobar123") eq(t, reqBody.Variables.Input.Event, "COMMENT") eq(t, reqBody.Variables.Input.Body, "cool story") } func TestPRReview_blank_comment(t *testing.T) { initBlankContext("", "OWNER/REPO", "master") http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { "pullRequest": { "number": 123 } } } } `)) _, err := RunCommand(`pr review --comment 123`) eq(t, err.Error(), "did not understand desired review action: body cannot be blank for comment review") } func TestPRReview_blank_request_changes(t *testing.T) { initBlankContext("", "OWNER/REPO", "master") http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { "pullRequest": { "number": 123 } } } } `)) _, err := RunCommand(`pr review -r 123`) eq(t, err.Error(), "did not understand desired review action: body cannot be blank for request-changes review") } func TestPRReview(t *testing.T) { type c struct { Cmd string ExpectedEvent string ExpectedBody string } cases := []c{ {`pr review --request-changes -b"bad"`, "REQUEST_CHANGES", "bad"}, {`pr review --approve`, "APPROVE", ""}, {`pr review --approve -b"hot damn"`, "APPROVE", "hot damn"}, {`pr review --comment --body "i donno"`, "COMMENT", "i donno"}, } for _, kase := range cases { initBlankContext("", "OWNER/REPO", "feature") http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { "pullRequests": { "nodes": [ { "url": "https://github.com/OWNER/REPO/pull/123", "id": "foobar123", "headRefName": "feature", "baseRefName": "master" } ] } } } } `)) http.StubResponse(200, bytes.NewBufferString(`{"data": {} }`)) _, err := RunCommand(kase.Cmd) if err != nil { t.Fatalf("got unexpected error running %s: %s", kase.Cmd, err) } bodyBytes, _ := ioutil.ReadAll(http.Requests[2].Body) reqBody := struct { Variables struct { Input struct { Event string Body string } } }{} _ = json.Unmarshal(bodyBytes, &reqBody) eq(t, reqBody.Variables.Input.Event, kase.ExpectedEvent) eq(t, reqBody.Variables.Input.Body, kase.ExpectedBody) } } func TestPRReview_nontty_insufficient_flags(t *testing.T) { initBlankContext("", "OWNER/REPO", "feature") defer stubTerminal(false)() http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { "pullRequests": { "nodes": [ { "url": "https://github.com/OWNER/REPO/pull/123", "number": 123, "id": "foobar123", "headRefName": "feature", "baseRefName": "master" } ] } } } } `)) output, err := RunCommand("pr review") if err == nil { t.Fatal("expected error") } assert.Equal(t, "", output.String()) assert.Equal(t, "did not understand desired review action: --approve, --request-changes, or --comment required when not attached to a tty", err.Error()) } func TestPRReview_nontty(t *testing.T) { initBlankContext("", "OWNER/REPO", "feature") defer stubTerminal(false)() http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { "pullRequests": { "nodes": [ { "url": "https://github.com/OWNER/REPO/pull/123", "number": 123, "id": "foobar123", "headRefName": "feature", "baseRefName": "master" } ] } } } } `)) http.StubResponse(200, bytes.NewBufferString(`{"data": {} }`)) output, err := RunCommand("pr review -c -bcool") if err != nil { t.Fatalf("unexpected error running command: %s", err) } assert.Equal(t, "", output.String()) assert.Equal(t, "", output.Stderr()) bodyBytes, _ := ioutil.ReadAll(http.Requests[2].Body) reqBody := struct { Variables struct { Input struct { Event string Body string } } }{} _ = json.Unmarshal(bodyBytes, &reqBody) assert.Equal(t, "COMMENT", reqBody.Variables.Input.Event) assert.Equal(t, "cool", reqBody.Variables.Input.Body) } func TestPRReview_interactive(t *testing.T) { initBlankContext("", "OWNER/REPO", "feature") defer stubTerminal(true)() http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { "pullRequests": { "nodes": [ { "url": "https://github.com/OWNER/REPO/pull/123", "number": 123, "id": "foobar123", "headRefName": "feature", "baseRefName": "master" } ] } } } } `)) http.StubResponse(200, bytes.NewBufferString(`{"data": {} }`)) as, teardown := prompt.InitAskStubber() defer teardown() as.Stub([]*prompt.QuestionStub{ { Name: "reviewType", Value: "Approve", }, }) as.Stub([]*prompt.QuestionStub{ { Name: "body", Value: "cool story", }, }) as.Stub([]*prompt.QuestionStub{ { Name: "confirm", Value: true, }, }) output, err := RunCommand(`pr review`) if err != nil { t.Fatalf("got unexpected error running pr review: %s", err) } test.ExpectLines(t, output.Stderr(), "Approved pull request #123") test.ExpectLines(t, output.String(), "Got:", "cool.*story") bodyBytes, _ := ioutil.ReadAll(http.Requests[2].Body) reqBody := struct { Variables struct { Input struct { Event string Body string } } }{} _ = json.Unmarshal(bodyBytes, &reqBody) eq(t, reqBody.Variables.Input.Event, "APPROVE") eq(t, reqBody.Variables.Input.Body, "cool story") } func TestPRReview_interactive_no_body(t *testing.T) { initBlankContext("", "OWNER/REPO", "feature") defer stubTerminal(true)() http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { "pullRequests": { "nodes": [ { "url": "https://github.com/OWNER/REPO/pull/123", "id": "foobar123", "headRefName": "feature", "baseRefName": "master" } ] } } } } `)) http.StubResponse(200, bytes.NewBufferString(`{"data": {} }`)) as, teardown := prompt.InitAskStubber() defer teardown() as.Stub([]*prompt.QuestionStub{ { Name: "reviewType", Value: "Request changes", }, }) as.Stub([]*prompt.QuestionStub{ { Name: "body", Default: true, }, }) as.Stub([]*prompt.QuestionStub{ { Name: "confirm", Value: true, }, }) _, err := RunCommand(`pr review`) if err == nil { t.Fatal("expected error") } eq(t, err.Error(), "this type of review cannot be blank") } func TestPRReview_interactive_blank_approve(t *testing.T) { initBlankContext("", "OWNER/REPO", "feature") defer stubTerminal(true)() http := initFakeHTTP() http.StubRepoResponse("OWNER", "REPO") http.StubResponse(200, bytes.NewBufferString(` { "data": { "repository": { "pullRequests": { "nodes": [ { "url": "https://github.com/OWNER/REPO/pull/123", "number": 123, "id": "foobar123", "headRefName": "feature", "baseRefName": "master" } ] } } } } `)) http.StubResponse(200, bytes.NewBufferString(`{"data": {} }`)) as, teardown := prompt.InitAskStubber() defer teardown() as.Stub([]*prompt.QuestionStub{ { Name: "reviewType", Value: "Approve", }, }) as.Stub([]*prompt.QuestionStub{ { Name: "body", Default: true, }, }) as.Stub([]*prompt.QuestionStub{ { Name: "confirm", Value: true, }, }) output, err := RunCommand(`pr review`) if err != nil { t.Fatalf("got unexpected error running pr review: %s", err) } unexpect := regexp.MustCompile("Got:") if unexpect.MatchString(output.String()) { t.Errorf("did not expect to see body printed in %s", output.String()) } test.ExpectLines(t, output.Stderr(), "Approved pull request #123") bodyBytes, _ := ioutil.ReadAll(http.Requests[2].Body) reqBody := struct { Variables struct { Input struct { Event string Body string } } }{} _ = json.Unmarshal(bodyBytes, &reqBody) eq(t, reqBody.Variables.Input.Event, "APPROVE") eq(t, reqBody.Variables.Input.Body, "") }