github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/pr/shared/finder_test.go (about) 1 package shared 2 3 import ( 4 "errors" 5 "net/http" 6 "net/url" 7 "testing" 8 9 "github.com/ungtb10d/cli/v2/context" 10 "github.com/ungtb10d/cli/v2/git" 11 "github.com/ungtb10d/cli/v2/internal/ghrepo" 12 "github.com/ungtb10d/cli/v2/pkg/httpmock" 13 ) 14 15 func TestFind(t *testing.T) { 16 type args struct { 17 baseRepoFn func() (ghrepo.Interface, error) 18 branchFn func() (string, error) 19 branchConfig func(string) git.BranchConfig 20 remotesFn func() (context.Remotes, error) 21 selector string 22 fields []string 23 baseBranch string 24 } 25 tests := []struct { 26 name string 27 args args 28 httpStub func(*httpmock.Registry) 29 wantPR int 30 wantRepo string 31 wantErr bool 32 }{ 33 { 34 name: "number argument", 35 args: args{ 36 selector: "13", 37 fields: []string{"id", "number"}, 38 baseRepoFn: func() (ghrepo.Interface, error) { 39 return ghrepo.FromFullName("OWNER/REPO") 40 }, 41 }, 42 httpStub: func(r *httpmock.Registry) { 43 r.Register( 44 httpmock.GraphQL(`query PullRequestByNumber\b`), 45 httpmock.StringResponse(`{"data":{"repository":{ 46 "pullRequest":{"number":13} 47 }}}`)) 48 }, 49 wantPR: 13, 50 wantRepo: "https://github.com/OWNER/REPO", 51 }, 52 { 53 name: "number argument with base branch", 54 args: args{ 55 selector: "13", 56 baseBranch: "main", 57 fields: []string{"id", "number"}, 58 baseRepoFn: func() (ghrepo.Interface, error) { 59 return ghrepo.FromFullName("OWNER/REPO") 60 }, 61 }, 62 httpStub: func(r *httpmock.Registry) { 63 r.Register( 64 httpmock.GraphQL(`query PullRequestForBranch\b`), 65 httpmock.StringResponse(`{"data":{"repository":{ 66 "pullRequests":{"nodes":[ 67 { 68 "number": 123, 69 "state": "OPEN", 70 "baseRefName": "main", 71 "headRefName": "13", 72 "isCrossRepository": false, 73 "headRepositoryOwner": {"login":"OWNER"} 74 } 75 ]} 76 }}}`)) 77 }, 78 wantPR: 123, 79 wantRepo: "https://github.com/OWNER/REPO", 80 }, 81 { 82 name: "baseRepo is error", 83 args: args{ 84 selector: "13", 85 fields: []string{"id", "number"}, 86 baseRepoFn: func() (ghrepo.Interface, error) { 87 return nil, errors.New("baseRepoErr") 88 }, 89 }, 90 wantErr: true, 91 }, 92 { 93 name: "blank fields is error", 94 args: args{ 95 selector: "13", 96 fields: []string{}, 97 }, 98 wantErr: true, 99 }, 100 { 101 name: "number only", 102 args: args{ 103 selector: "13", 104 fields: []string{"number"}, 105 baseRepoFn: func() (ghrepo.Interface, error) { 106 return ghrepo.FromFullName("OWNER/REPO") 107 }, 108 }, 109 httpStub: nil, 110 wantPR: 13, 111 wantRepo: "https://github.com/OWNER/REPO", 112 }, 113 { 114 name: "number with hash argument", 115 args: args{ 116 selector: "#13", 117 fields: []string{"id", "number"}, 118 baseRepoFn: func() (ghrepo.Interface, error) { 119 return ghrepo.FromFullName("OWNER/REPO") 120 }, 121 }, 122 httpStub: func(r *httpmock.Registry) { 123 r.Register( 124 httpmock.GraphQL(`query PullRequestByNumber\b`), 125 httpmock.StringResponse(`{"data":{"repository":{ 126 "pullRequest":{"number":13} 127 }}}`)) 128 }, 129 wantPR: 13, 130 wantRepo: "https://github.com/OWNER/REPO", 131 }, 132 { 133 name: "URL argument", 134 args: args{ 135 selector: "https://example.org/OWNER/REPO/pull/13/files", 136 fields: []string{"id", "number"}, 137 baseRepoFn: nil, 138 }, 139 httpStub: func(r *httpmock.Registry) { 140 r.Register( 141 httpmock.GraphQL(`query PullRequestByNumber\b`), 142 httpmock.StringResponse(`{"data":{"repository":{ 143 "pullRequest":{"number":13} 144 }}}`)) 145 }, 146 wantPR: 13, 147 wantRepo: "https://example.org/OWNER/REPO", 148 }, 149 { 150 name: "branch argument", 151 args: args{ 152 selector: "blueberries", 153 fields: []string{"id", "number"}, 154 baseRepoFn: func() (ghrepo.Interface, error) { 155 return ghrepo.FromFullName("OWNER/REPO") 156 }, 157 }, 158 httpStub: func(r *httpmock.Registry) { 159 r.Register( 160 httpmock.GraphQL(`query PullRequestForBranch\b`), 161 httpmock.StringResponse(`{"data":{"repository":{ 162 "pullRequests":{"nodes":[ 163 { 164 "number": 14, 165 "state": "CLOSED", 166 "baseRefName": "main", 167 "headRefName": "blueberries", 168 "isCrossRepository": false, 169 "headRepositoryOwner": {"login":"OWNER"} 170 }, 171 { 172 "number": 13, 173 "state": "OPEN", 174 "baseRefName": "main", 175 "headRefName": "blueberries", 176 "isCrossRepository": false, 177 "headRepositoryOwner": {"login":"OWNER"} 178 } 179 ]} 180 }}}`)) 181 }, 182 wantPR: 13, 183 wantRepo: "https://github.com/OWNER/REPO", 184 }, 185 { 186 name: "branch argument with base branch", 187 args: args{ 188 selector: "blueberries", 189 baseBranch: "main", 190 fields: []string{"id", "number"}, 191 baseRepoFn: func() (ghrepo.Interface, error) { 192 return ghrepo.FromFullName("OWNER/REPO") 193 }, 194 }, 195 httpStub: func(r *httpmock.Registry) { 196 r.Register( 197 httpmock.GraphQL(`query PullRequestForBranch\b`), 198 httpmock.StringResponse(`{"data":{"repository":{ 199 "pullRequests":{"nodes":[ 200 { 201 "number": 14, 202 "state": "OPEN", 203 "baseRefName": "dev", 204 "headRefName": "blueberries", 205 "isCrossRepository": false, 206 "headRepositoryOwner": {"login":"OWNER"} 207 }, 208 { 209 "number": 13, 210 "state": "OPEN", 211 "baseRefName": "main", 212 "headRefName": "blueberries", 213 "isCrossRepository": false, 214 "headRepositoryOwner": {"login":"OWNER"} 215 } 216 ]} 217 }}}`)) 218 }, 219 wantPR: 13, 220 wantRepo: "https://github.com/OWNER/REPO", 221 }, 222 { 223 name: "no argument reads current branch", 224 args: args{ 225 selector: "", 226 fields: []string{"id", "number"}, 227 baseRepoFn: func() (ghrepo.Interface, error) { 228 return ghrepo.FromFullName("OWNER/REPO") 229 }, 230 branchFn: func() (string, error) { 231 return "blueberries", nil 232 }, 233 branchConfig: func(branch string) (c git.BranchConfig) { 234 return 235 }, 236 }, 237 httpStub: func(r *httpmock.Registry) { 238 r.Register( 239 httpmock.GraphQL(`query PullRequestForBranch\b`), 240 httpmock.StringResponse(`{"data":{"repository":{ 241 "pullRequests":{"nodes":[ 242 { 243 "number": 13, 244 "state": "OPEN", 245 "baseRefName": "main", 246 "headRefName": "blueberries", 247 "isCrossRepository": false, 248 "headRepositoryOwner": {"login":"OWNER"} 249 } 250 ]} 251 }}}`)) 252 }, 253 wantPR: 13, 254 wantRepo: "https://github.com/OWNER/REPO", 255 }, 256 { 257 name: "current branch with merged pr", 258 args: args{ 259 selector: "", 260 fields: []string{"id", "number"}, 261 baseRepoFn: func() (ghrepo.Interface, error) { 262 return ghrepo.FromFullName("OWNER/REPO") 263 }, 264 branchFn: func() (string, error) { 265 return "blueberries", nil 266 }, 267 branchConfig: func(branch string) (c git.BranchConfig) { 268 return 269 }, 270 }, 271 httpStub: func(r *httpmock.Registry) { 272 r.Register( 273 httpmock.GraphQL(`query PullRequestForBranch\b`), 274 httpmock.StringResponse(`{"data":{"repository":{ 275 "pullRequests":{"nodes":[ 276 { 277 "number": 13, 278 "state": "MERGED", 279 "baseRefName": "main", 280 "headRefName": "blueberries", 281 "isCrossRepository": false, 282 "headRepositoryOwner": {"login":"OWNER"} 283 } 284 ]}, 285 "defaultBranchRef":{ 286 "name": "blueberries" 287 } 288 }}}`)) 289 }, 290 wantErr: true, 291 }, 292 { 293 name: "current branch is error", 294 args: args{ 295 selector: "", 296 fields: []string{"id", "number"}, 297 baseRepoFn: func() (ghrepo.Interface, error) { 298 return ghrepo.FromFullName("OWNER/REPO") 299 }, 300 branchFn: func() (string, error) { 301 return "", errors.New("branchErr") 302 }, 303 }, 304 wantErr: true, 305 }, 306 { 307 name: "current branch with upstream configuration", 308 args: args{ 309 selector: "", 310 fields: []string{"id", "number"}, 311 baseRepoFn: func() (ghrepo.Interface, error) { 312 return ghrepo.FromFullName("OWNER/REPO") 313 }, 314 branchFn: func() (string, error) { 315 return "blueberries", nil 316 }, 317 branchConfig: func(branch string) (c git.BranchConfig) { 318 c.MergeRef = "refs/heads/blue-upstream-berries" 319 c.RemoteName = "origin" 320 return 321 }, 322 remotesFn: func() (context.Remotes, error) { 323 return context.Remotes{{ 324 Remote: &git.Remote{Name: "origin"}, 325 Repo: ghrepo.New("UPSTREAMOWNER", "REPO"), 326 }}, nil 327 }, 328 }, 329 httpStub: func(r *httpmock.Registry) { 330 r.Register( 331 httpmock.GraphQL(`query PullRequestForBranch\b`), 332 httpmock.StringResponse(`{"data":{"repository":{ 333 "pullRequests":{"nodes":[ 334 { 335 "number": 13, 336 "state": "OPEN", 337 "baseRefName": "main", 338 "headRefName": "blue-upstream-berries", 339 "isCrossRepository": true, 340 "headRepositoryOwner": {"login":"UPSTREAMOWNER"} 341 } 342 ]} 343 }}}`)) 344 }, 345 wantPR: 13, 346 wantRepo: "https://github.com/OWNER/REPO", 347 }, 348 { 349 name: "current branch with upstream configuration", 350 args: args{ 351 selector: "", 352 fields: []string{"id", "number"}, 353 baseRepoFn: func() (ghrepo.Interface, error) { 354 return ghrepo.FromFullName("OWNER/REPO") 355 }, 356 branchFn: func() (string, error) { 357 return "blueberries", nil 358 }, 359 branchConfig: func(branch string) (c git.BranchConfig) { 360 u, _ := url.Parse("https://github.com/UPSTREAMOWNER/REPO") 361 c.MergeRef = "refs/heads/blue-upstream-berries" 362 c.RemoteURL = u 363 return 364 }, 365 remotesFn: nil, 366 }, 367 httpStub: func(r *httpmock.Registry) { 368 r.Register( 369 httpmock.GraphQL(`query PullRequestForBranch\b`), 370 httpmock.StringResponse(`{"data":{"repository":{ 371 "pullRequests":{"nodes":[ 372 { 373 "number": 13, 374 "state": "OPEN", 375 "baseRefName": "main", 376 "headRefName": "blue-upstream-berries", 377 "isCrossRepository": true, 378 "headRepositoryOwner": {"login":"UPSTREAMOWNER"} 379 } 380 ]} 381 }}}`)) 382 }, 383 wantPR: 13, 384 wantRepo: "https://github.com/OWNER/REPO", 385 }, 386 { 387 name: "current branch made by pr checkout", 388 args: args{ 389 selector: "", 390 fields: []string{"id", "number"}, 391 baseRepoFn: func() (ghrepo.Interface, error) { 392 return ghrepo.FromFullName("OWNER/REPO") 393 }, 394 branchFn: func() (string, error) { 395 return "blueberries", nil 396 }, 397 branchConfig: func(branch string) (c git.BranchConfig) { 398 c.MergeRef = "refs/pull/13/head" 399 return 400 }, 401 }, 402 httpStub: func(r *httpmock.Registry) { 403 r.Register( 404 httpmock.GraphQL(`query PullRequestByNumber\b`), 405 httpmock.StringResponse(`{"data":{"repository":{ 406 "pullRequest":{"number":13} 407 }}}`)) 408 }, 409 wantPR: 13, 410 wantRepo: "https://github.com/OWNER/REPO", 411 }, 412 } 413 for _, tt := range tests { 414 t.Run(tt.name, func(t *testing.T) { 415 reg := &httpmock.Registry{} 416 defer reg.Verify(t) 417 if tt.httpStub != nil { 418 tt.httpStub(reg) 419 } 420 421 f := finder{ 422 httpClient: func() (*http.Client, error) { 423 return &http.Client{Transport: reg}, nil 424 }, 425 baseRepoFn: tt.args.baseRepoFn, 426 branchFn: tt.args.branchFn, 427 branchConfig: tt.args.branchConfig, 428 remotesFn: tt.args.remotesFn, 429 } 430 431 pr, repo, err := f.Find(FindOptions{ 432 Selector: tt.args.selector, 433 Fields: tt.args.fields, 434 BaseBranch: tt.args.baseBranch, 435 }) 436 if (err != nil) != tt.wantErr { 437 t.Errorf("Find() error = %v, wantErr %v", err, tt.wantErr) 438 return 439 } 440 if tt.wantErr { 441 if tt.wantPR > 0 { 442 t.Error("wantPR field is not checked in error case") 443 } 444 if tt.wantRepo != "" { 445 t.Error("wantRepo field is not checked in error case") 446 } 447 return 448 } 449 450 if pr.Number != tt.wantPR { 451 t.Errorf("want pr #%d, got #%d", tt.wantPR, pr.Number) 452 } 453 repoURL := ghrepo.GenerateRepoURL(repo, "") 454 if repoURL != tt.wantRepo { 455 t.Errorf("want repo %s, got %s", tt.wantRepo, repoURL) 456 } 457 }) 458 } 459 }