github.com/cli/cli@v1.14.1-0.20210902173923-1af6a669e342/pkg/cmd/pr/close/close_test.go (about) 1 package close 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "net/http" 7 "strings" 8 "testing" 9 10 "github.com/MakeNowJust/heredoc" 11 "github.com/cli/cli/api" 12 "github.com/cli/cli/internal/ghrepo" 13 "github.com/cli/cli/internal/run" 14 "github.com/cli/cli/pkg/cmd/pr/shared" 15 "github.com/cli/cli/pkg/cmdutil" 16 "github.com/cli/cli/pkg/httpmock" 17 "github.com/cli/cli/pkg/iostreams" 18 "github.com/cli/cli/test" 19 "github.com/google/shlex" 20 "github.com/stretchr/testify/assert" 21 ) 22 23 // repo: either "baseOwner/baseRepo" or "baseOwner/baseRepo:defaultBranch" 24 // prHead: "headOwner/headRepo:headBranch" 25 func stubPR(repo, prHead string) (ghrepo.Interface, *api.PullRequest) { 26 defaultBranch := "" 27 if idx := strings.IndexRune(repo, ':'); idx >= 0 { 28 defaultBranch = repo[idx+1:] 29 repo = repo[:idx] 30 } 31 baseRepo, err := ghrepo.FromFullName(repo) 32 if err != nil { 33 panic(err) 34 } 35 if defaultBranch != "" { 36 baseRepo = api.InitRepoHostname(&api.Repository{ 37 Name: baseRepo.RepoName(), 38 Owner: api.RepositoryOwner{Login: baseRepo.RepoOwner()}, 39 DefaultBranchRef: api.BranchRef{Name: defaultBranch}, 40 }, baseRepo.RepoHost()) 41 } 42 43 idx := strings.IndexRune(prHead, ':') 44 headRefName := prHead[idx+1:] 45 headRepo, err := ghrepo.FromFullName(prHead[:idx]) 46 if err != nil { 47 panic(err) 48 } 49 50 return baseRepo, &api.PullRequest{ 51 ID: "THE-ID", 52 Number: 96, 53 State: "OPEN", 54 HeadRefName: headRefName, 55 HeadRepositoryOwner: api.Owner{Login: headRepo.RepoOwner()}, 56 HeadRepository: &api.PRRepository{Name: headRepo.RepoName()}, 57 IsCrossRepository: !ghrepo.IsSame(baseRepo, headRepo), 58 } 59 } 60 61 func runCommand(rt http.RoundTripper, isTTY bool, cli string) (*test.CmdOut, error) { 62 io, _, stdout, stderr := iostreams.Test() 63 io.SetStdoutTTY(isTTY) 64 io.SetStdinTTY(isTTY) 65 io.SetStderrTTY(isTTY) 66 67 factory := &cmdutil.Factory{ 68 IOStreams: io, 69 HttpClient: func() (*http.Client, error) { 70 return &http.Client{Transport: rt}, nil 71 }, 72 Branch: func() (string, error) { 73 return "trunk", nil 74 }, 75 } 76 77 cmd := NewCmdClose(factory, nil) 78 79 argv, err := shlex.Split(cli) 80 if err != nil { 81 return nil, err 82 } 83 cmd.SetArgs(argv) 84 85 cmd.SetIn(&bytes.Buffer{}) 86 cmd.SetOut(ioutil.Discard) 87 cmd.SetErr(ioutil.Discard) 88 89 _, err = cmd.ExecuteC() 90 return &test.CmdOut{ 91 OutBuf: stdout, 92 ErrBuf: stderr, 93 }, err 94 } 95 96 func TestPrClose(t *testing.T) { 97 http := &httpmock.Registry{} 98 defer http.Verify(t) 99 100 baseRepo, pr := stubPR("OWNER/REPO", "OWNER/REPO:feature") 101 pr.Title = "The title of the PR" 102 shared.RunCommandFinder("96", pr, baseRepo) 103 104 http.Register( 105 httpmock.GraphQL(`mutation PullRequestClose\b`), 106 httpmock.GraphQLMutation(`{"id": "THE-ID"}`, 107 func(inputs map[string]interface{}) { 108 assert.Equal(t, inputs["pullRequestId"], "THE-ID") 109 }), 110 ) 111 112 output, err := runCommand(http, true, "96") 113 assert.NoError(t, err) 114 assert.Equal(t, "", output.String()) 115 assert.Equal(t, "✓ Closed pull request #96 (The title of the PR)\n", output.Stderr()) 116 } 117 118 func TestPrClose_alreadyClosed(t *testing.T) { 119 http := &httpmock.Registry{} 120 defer http.Verify(t) 121 122 baseRepo, pr := stubPR("OWNER/REPO", "OWNER/REPO:feature") 123 pr.State = "CLOSED" 124 pr.Title = "The title of the PR" 125 shared.RunCommandFinder("96", pr, baseRepo) 126 127 output, err := runCommand(http, true, "96") 128 assert.NoError(t, err) 129 assert.Equal(t, "", output.String()) 130 assert.Equal(t, "! Pull request #96 (The title of the PR) is already closed\n", output.Stderr()) 131 } 132 133 func TestPrClose_deleteBranch_sameRepo(t *testing.T) { 134 http := &httpmock.Registry{} 135 defer http.Verify(t) 136 137 baseRepo, pr := stubPR("OWNER/REPO", "OWNER/REPO:blueberries") 138 pr.Title = "The title of the PR" 139 shared.RunCommandFinder("96", pr, baseRepo) 140 141 http.Register( 142 httpmock.GraphQL(`mutation PullRequestClose\b`), 143 httpmock.GraphQLMutation(`{"id": "THE-ID"}`, 144 func(inputs map[string]interface{}) { 145 assert.Equal(t, inputs["pullRequestId"], "THE-ID") 146 }), 147 ) 148 http.Register( 149 httpmock.REST("DELETE", "repos/OWNER/REPO/git/refs/heads/blueberries"), 150 httpmock.StringResponse(`{}`)) 151 152 cs, cmdTeardown := run.Stub() 153 defer cmdTeardown(t) 154 155 cs.Register(`git rev-parse --verify refs/heads/blueberries`, 0, "") 156 cs.Register(`git branch -D blueberries`, 0, "") 157 158 output, err := runCommand(http, true, `96 --delete-branch`) 159 assert.NoError(t, err) 160 assert.Equal(t, "", output.String()) 161 assert.Equal(t, heredoc.Doc(` 162 ✓ Closed pull request #96 (The title of the PR) 163 ✓ Deleted branch blueberries 164 `), output.Stderr()) 165 } 166 167 func TestPrClose_deleteBranch_crossRepo(t *testing.T) { 168 http := &httpmock.Registry{} 169 defer http.Verify(t) 170 171 baseRepo, pr := stubPR("OWNER/REPO", "hubot/REPO:blueberries") 172 pr.Title = "The title of the PR" 173 shared.RunCommandFinder("96", pr, baseRepo) 174 175 http.Register( 176 httpmock.GraphQL(`mutation PullRequestClose\b`), 177 httpmock.GraphQLMutation(`{"id": "THE-ID"}`, 178 func(inputs map[string]interface{}) { 179 assert.Equal(t, inputs["pullRequestId"], "THE-ID") 180 }), 181 ) 182 183 cs, cmdTeardown := run.Stub() 184 defer cmdTeardown(t) 185 186 cs.Register(`git rev-parse --verify refs/heads/blueberries`, 0, "") 187 cs.Register(`git branch -D blueberries`, 0, "") 188 189 output, err := runCommand(http, true, `96 --delete-branch`) 190 assert.NoError(t, err) 191 assert.Equal(t, "", output.String()) 192 assert.Equal(t, heredoc.Doc(` 193 ✓ Closed pull request #96 (The title of the PR) 194 ! Skipped deleting the remote branch of a pull request from fork 195 ✓ Deleted branch blueberries 196 `), output.Stderr()) 197 } 198 199 func TestPrClose_deleteBranch_sameBranch(t *testing.T) { 200 http := &httpmock.Registry{} 201 defer http.Verify(t) 202 203 baseRepo, pr := stubPR("OWNER/REPO:main", "OWNER/REPO:trunk") 204 pr.Title = "The title of the PR" 205 shared.RunCommandFinder("96", pr, baseRepo) 206 207 http.Register( 208 httpmock.GraphQL(`mutation PullRequestClose\b`), 209 httpmock.GraphQLMutation(`{"id": "THE-ID"}`, 210 func(inputs map[string]interface{}) { 211 assert.Equal(t, inputs["pullRequestId"], "THE-ID") 212 }), 213 ) 214 http.Register( 215 httpmock.REST("DELETE", "repos/OWNER/REPO/git/refs/heads/trunk"), 216 httpmock.StringResponse(`{}`)) 217 218 cs, cmdTeardown := run.Stub() 219 defer cmdTeardown(t) 220 221 cs.Register(`git checkout main`, 0, "") 222 cs.Register(`git rev-parse --verify refs/heads/trunk`, 0, "") 223 cs.Register(`git branch -D trunk`, 0, "") 224 225 output, err := runCommand(http, true, `96 --delete-branch`) 226 assert.NoError(t, err) 227 assert.Equal(t, "", output.String()) 228 assert.Equal(t, heredoc.Doc(` 229 ✓ Closed pull request #96 (The title of the PR) 230 ✓ Deleted branch trunk and switched to branch main 231 `), output.Stderr()) 232 }