github.com/secman-team/gh-api@v1.8.2/pkg/cmd/repo/view/view_test.go (about) 1 package view 2 3 import ( 4 "bytes" 5 "fmt" 6 "net/http" 7 "testing" 8 9 "github.com/MakeNowJust/heredoc" 10 "github.com/secman-team/gh-api/core/ghrepo" 11 "github.com/secman-team/gh-api/core/run" 12 "github.com/secman-team/gh-api/pkg/cmdutil" 13 "github.com/secman-team/gh-api/pkg/httpmock" 14 "github.com/secman-team/gh-api/pkg/iostreams" 15 "github.com/google/shlex" 16 "github.com/stretchr/testify/assert" 17 ) 18 19 func TestNewCmdView(t *testing.T) { 20 tests := []struct { 21 name string 22 cli string 23 wants ViewOptions 24 wantsErr bool 25 }{ 26 { 27 name: "no args", 28 cli: "", 29 wants: ViewOptions{ 30 RepoArg: "", 31 Web: false, 32 }, 33 }, 34 { 35 name: "sets repo arg", 36 cli: "some/repo", 37 wants: ViewOptions{ 38 RepoArg: "some/repo", 39 Web: false, 40 }, 41 }, 42 { 43 name: "sets web", 44 cli: "-w", 45 wants: ViewOptions{ 46 RepoArg: "", 47 Web: true, 48 }, 49 }, 50 { 51 name: "sets branch", 52 cli: "-b feat/awesome", 53 wants: ViewOptions{ 54 RepoArg: "", 55 Branch: "feat/awesome", 56 }, 57 }, 58 } 59 60 for _, tt := range tests { 61 t.Run(tt.name, func(t *testing.T) { 62 io, _, _, _ := iostreams.Test() 63 64 f := &cmdutil.Factory{ 65 IOStreams: io, 66 } 67 68 // THOUGHT: this seems ripe for cmdutil. It's almost identical to the set up for the same test 69 // in gist create. 70 argv, err := shlex.Split(tt.cli) 71 assert.NoError(t, err) 72 73 var gotOpts *ViewOptions 74 cmd := NewCmdView(f, func(opts *ViewOptions) error { 75 gotOpts = opts 76 return nil 77 }) 78 cmd.SetArgs(argv) 79 cmd.SetIn(&bytes.Buffer{}) 80 cmd.SetOut(&bytes.Buffer{}) 81 cmd.SetErr(&bytes.Buffer{}) 82 83 _, err = cmd.ExecuteC() 84 if tt.wantsErr { 85 assert.Error(t, err) 86 return 87 } 88 assert.NoError(t, err) 89 90 assert.Equal(t, tt.wants.Web, gotOpts.Web) 91 assert.Equal(t, tt.wants.Branch, gotOpts.Branch) 92 assert.Equal(t, tt.wants.RepoArg, gotOpts.RepoArg) 93 }) 94 } 95 } 96 97 func Test_RepoView_Web(t *testing.T) { 98 tests := []struct { 99 name string 100 stdoutTTY bool 101 wantStderr string 102 wantBrowse string 103 }{ 104 { 105 name: "tty", 106 stdoutTTY: true, 107 wantStderr: "Opening github.com/OWNER/REPO in your browser.\n", 108 wantBrowse: "https://github.com/OWNER/REPO", 109 }, 110 { 111 name: "nontty", 112 stdoutTTY: false, 113 wantStderr: "", 114 wantBrowse: "https://github.com/OWNER/REPO", 115 }, 116 } 117 118 for _, tt := range tests { 119 reg := &httpmock.Registry{} 120 reg.StubRepoInfoResponse("OWNER", "REPO", "main") 121 122 browser := &cmdutil.TestBrowser{} 123 opts := &ViewOptions{ 124 Web: true, 125 HttpClient: func() (*http.Client, error) { 126 return &http.Client{Transport: reg}, nil 127 }, 128 BaseRepo: func() (ghrepo.Interface, error) { 129 return ghrepo.New("OWNER", "REPO"), nil 130 }, 131 Browser: browser, 132 } 133 134 io, _, stdout, stderr := iostreams.Test() 135 136 opts.IO = io 137 138 t.Run(tt.name, func(t *testing.T) { 139 io.SetStdoutTTY(tt.stdoutTTY) 140 141 _, teardown := run.Stub() 142 defer teardown(t) 143 144 if err := viewRun(opts); err != nil { 145 t.Errorf("viewRun() error = %v", err) 146 } 147 assert.Equal(t, "", stdout.String()) 148 assert.Equal(t, tt.wantStderr, stderr.String()) 149 reg.Verify(t) 150 browser.Verify(t, tt.wantBrowse) 151 }) 152 } 153 } 154 155 func Test_ViewRun(t *testing.T) { 156 tests := []struct { 157 name string 158 opts *ViewOptions 159 repoName string 160 stdoutTTY bool 161 wantOut string 162 wantStderr string 163 wantErr bool 164 }{ 165 { 166 name: "nontty", 167 wantOut: heredoc.Doc(` 168 name: OWNER/REPO 169 description: social distancing 170 -- 171 # truly cool readme check it out 172 `), 173 }, 174 { 175 name: "url arg", 176 repoName: "jill/valentine", 177 opts: &ViewOptions{ 178 RepoArg: "https://github.com/jill/valentine", 179 }, 180 stdoutTTY: true, 181 wantOut: heredoc.Doc(` 182 jill/valentine 183 social distancing 184 185 186 # truly cool readme check it out 187 188 189 190 View this repository on GitHub: https://github.com/jill/valentine 191 `), 192 }, 193 { 194 name: "name arg", 195 repoName: "jill/valentine", 196 opts: &ViewOptions{ 197 RepoArg: "jill/valentine", 198 }, 199 stdoutTTY: true, 200 wantOut: heredoc.Doc(` 201 jill/valentine 202 social distancing 203 204 205 # truly cool readme check it out 206 207 208 209 View this repository on GitHub: https://github.com/jill/valentine 210 `), 211 }, 212 { 213 name: "branch arg", 214 opts: &ViewOptions{ 215 Branch: "feat/awesome", 216 }, 217 stdoutTTY: true, 218 wantOut: heredoc.Doc(` 219 OWNER/REPO 220 social distancing 221 222 223 # truly cool readme check it out 224 225 226 227 View this repository on GitHub: https://github.com/OWNER/REPO/tree/feat%2Fawesome 228 `), 229 }, 230 { 231 name: "no args", 232 stdoutTTY: true, 233 wantOut: heredoc.Doc(` 234 OWNER/REPO 235 social distancing 236 237 238 # truly cool readme check it out 239 240 241 242 View this repository on GitHub: https://github.com/OWNER/REPO 243 `), 244 }, 245 } 246 for _, tt := range tests { 247 if tt.opts == nil { 248 tt.opts = &ViewOptions{} 249 } 250 251 if tt.repoName == "" { 252 tt.repoName = "OWNER/REPO" 253 } 254 255 tt.opts.BaseRepo = func() (ghrepo.Interface, error) { 256 repo, _ := ghrepo.FromFullName(tt.repoName) 257 return repo, nil 258 } 259 260 reg := &httpmock.Registry{} 261 reg.Register( 262 httpmock.GraphQL(`query RepositoryInfo\b`), 263 httpmock.StringResponse(` 264 { "data": { 265 "repository": { 266 "description": "social distancing" 267 } } }`)) 268 reg.Register( 269 httpmock.REST("GET", fmt.Sprintf("repos/%s/readme", tt.repoName)), 270 httpmock.StringResponse(` 271 { "name": "readme.md", 272 "content": "IyB0cnVseSBjb29sIHJlYWRtZSBjaGVjayBpdCBvdXQ="}`)) 273 274 tt.opts.HttpClient = func() (*http.Client, error) { 275 return &http.Client{Transport: reg}, nil 276 } 277 278 io, _, stdout, stderr := iostreams.Test() 279 tt.opts.IO = io 280 281 t.Run(tt.name, func(t *testing.T) { 282 io.SetStdoutTTY(tt.stdoutTTY) 283 284 if err := viewRun(tt.opts); (err != nil) != tt.wantErr { 285 t.Errorf("viewRun() error = %v, wantErr %v", err, tt.wantErr) 286 } 287 assert.Equal(t, tt.wantStderr, stderr.String()) 288 assert.Equal(t, tt.wantOut, stdout.String()) 289 reg.Verify(t) 290 }) 291 } 292 } 293 294 func Test_ViewRun_NonMarkdownReadme(t *testing.T) { 295 tests := []struct { 296 name string 297 stdoutTTY bool 298 wantOut string 299 }{ 300 { 301 name: "tty", 302 wantOut: heredoc.Doc(` 303 OWNER/REPO 304 social distancing 305 306 # truly cool readme check it out 307 308 View this repository on GitHub: https://github.com/OWNER/REPO 309 `), 310 stdoutTTY: true, 311 }, 312 { 313 name: "nontty", 314 wantOut: heredoc.Doc(` 315 name: OWNER/REPO 316 description: social distancing 317 -- 318 # truly cool readme check it out 319 `), 320 }, 321 } 322 323 for _, tt := range tests { 324 reg := &httpmock.Registry{} 325 reg.Register( 326 httpmock.GraphQL(`query RepositoryInfo\b`), 327 httpmock.StringResponse(` 328 { "data": { 329 "repository": { 330 "description": "social distancing" 331 } } }`)) 332 reg.Register( 333 httpmock.REST("GET", "repos/OWNER/REPO/readme"), 334 httpmock.StringResponse(` 335 { "name": "readme.org", 336 "content": "IyB0cnVseSBjb29sIHJlYWRtZSBjaGVjayBpdCBvdXQ="}`)) 337 338 opts := &ViewOptions{ 339 HttpClient: func() (*http.Client, error) { 340 return &http.Client{Transport: reg}, nil 341 }, 342 BaseRepo: func() (ghrepo.Interface, error) { 343 return ghrepo.New("OWNER", "REPO"), nil 344 }, 345 } 346 347 io, _, stdout, stderr := iostreams.Test() 348 349 opts.IO = io 350 351 t.Run(tt.name, func(t *testing.T) { 352 io.SetStdoutTTY(tt.stdoutTTY) 353 354 if err := viewRun(opts); err != nil { 355 t.Errorf("viewRun() error = %v", err) 356 } 357 assert.Equal(t, tt.wantOut, stdout.String()) 358 assert.Equal(t, "", stderr.String()) 359 reg.Verify(t) 360 }) 361 } 362 } 363 364 func Test_ViewRun_NoReadme(t *testing.T) { 365 tests := []struct { 366 name string 367 stdoutTTY bool 368 wantOut string 369 }{ 370 { 371 name: "tty", 372 wantOut: heredoc.Doc(` 373 OWNER/REPO 374 social distancing 375 376 This repository does not have a README 377 378 View this repository on GitHub: https://github.com/OWNER/REPO 379 `), 380 stdoutTTY: true, 381 }, 382 { 383 name: "nontty", 384 wantOut: heredoc.Doc(` 385 name: OWNER/REPO 386 description: social distancing 387 `), 388 }, 389 } 390 391 for _, tt := range tests { 392 reg := &httpmock.Registry{} 393 reg.Register( 394 httpmock.GraphQL(`query RepositoryInfo\b`), 395 httpmock.StringResponse(` 396 { "data": { 397 "repository": { 398 "description": "social distancing" 399 } } }`)) 400 reg.Register( 401 httpmock.REST("GET", "repos/OWNER/REPO/readme"), 402 httpmock.StatusStringResponse(404, `{}`)) 403 404 opts := &ViewOptions{ 405 HttpClient: func() (*http.Client, error) { 406 return &http.Client{Transport: reg}, nil 407 }, 408 BaseRepo: func() (ghrepo.Interface, error) { 409 return ghrepo.New("OWNER", "REPO"), nil 410 }, 411 } 412 413 io, _, stdout, stderr := iostreams.Test() 414 415 opts.IO = io 416 417 t.Run(tt.name, func(t *testing.T) { 418 io.SetStdoutTTY(tt.stdoutTTY) 419 420 if err := viewRun(opts); err != nil { 421 t.Errorf("viewRun() error = %v", err) 422 } 423 assert.Equal(t, tt.wantOut, stdout.String()) 424 assert.Equal(t, "", stderr.String()) 425 reg.Verify(t) 426 }) 427 } 428 } 429 430 func Test_ViewRun_NoDescription(t *testing.T) { 431 tests := []struct { 432 name string 433 stdoutTTY bool 434 wantOut string 435 }{ 436 { 437 name: "tty", 438 wantOut: heredoc.Doc(` 439 OWNER/REPO 440 No description provided 441 442 # truly cool readme check it out 443 444 View this repository on GitHub: https://github.com/OWNER/REPO 445 `), 446 stdoutTTY: true, 447 }, 448 { 449 name: "nontty", 450 wantOut: heredoc.Doc(` 451 name: OWNER/REPO 452 description: 453 -- 454 # truly cool readme check it out 455 `), 456 }, 457 } 458 459 for _, tt := range tests { 460 reg := &httpmock.Registry{} 461 reg.Register( 462 httpmock.GraphQL(`query RepositoryInfo\b`), 463 httpmock.StringResponse(` 464 { "data": { 465 "repository": { 466 "description": "" 467 } } }`)) 468 reg.Register( 469 httpmock.REST("GET", "repos/OWNER/REPO/readme"), 470 httpmock.StringResponse(` 471 { "name": "readme.org", 472 "content": "IyB0cnVseSBjb29sIHJlYWRtZSBjaGVjayBpdCBvdXQ="}`)) 473 474 opts := &ViewOptions{ 475 HttpClient: func() (*http.Client, error) { 476 return &http.Client{Transport: reg}, nil 477 }, 478 BaseRepo: func() (ghrepo.Interface, error) { 479 return ghrepo.New("OWNER", "REPO"), nil 480 }, 481 } 482 483 io, _, stdout, stderr := iostreams.Test() 484 485 opts.IO = io 486 487 t.Run(tt.name, func(t *testing.T) { 488 io.SetStdoutTTY(tt.stdoutTTY) 489 490 if err := viewRun(opts); err != nil { 491 t.Errorf("viewRun() error = %v", err) 492 } 493 assert.Equal(t, tt.wantOut, stdout.String()) 494 assert.Equal(t, "", stderr.String()) 495 reg.Verify(t) 496 }) 497 } 498 } 499 500 func Test_ViewRun_WithoutUsername(t *testing.T) { 501 reg := &httpmock.Registry{} 502 reg.Register( 503 httpmock.GraphQL(`query UserCurrent\b`), 504 httpmock.StringResponse(` 505 { "data": { "viewer": { 506 "login": "OWNER" 507 }}}`)) 508 reg.Register( 509 httpmock.GraphQL(`query RepositoryInfo\b`), 510 httpmock.StringResponse(` 511 { "data": { 512 "repository": { 513 "description": "social distancing" 514 } } }`)) 515 reg.Register( 516 httpmock.REST("GET", "repos/OWNER/REPO/readme"), 517 httpmock.StringResponse(` 518 { "name": "readme.md", 519 "content": "IyB0cnVseSBjb29sIHJlYWRtZSBjaGVjayBpdCBvdXQ="}`)) 520 521 io, _, stdout, stderr := iostreams.Test() 522 io.SetStdoutTTY(false) 523 524 opts := &ViewOptions{ 525 RepoArg: "REPO", 526 HttpClient: func() (*http.Client, error) { 527 return &http.Client{Transport: reg}, nil 528 }, 529 IO: io, 530 } 531 532 if err := viewRun(opts); err != nil { 533 t.Errorf("viewRun() error = %v", err) 534 } 535 536 assert.Equal(t, heredoc.Doc(` 537 name: OWNER/REPO 538 description: social distancing 539 -- 540 # truly cool readme check it out 541 `), stdout.String()) 542 assert.Equal(t, "", stderr.String()) 543 reg.Verify(t) 544 } 545 546 func Test_ViewRun_HandlesSpecialCharacters(t *testing.T) { 547 tests := []struct { 548 name string 549 opts *ViewOptions 550 repoName string 551 stdoutTTY bool 552 wantOut string 553 wantStderr string 554 wantErr bool 555 }{ 556 { 557 name: "nontty", 558 wantOut: heredoc.Doc(` 559 name: OWNER/REPO 560 description: Some basic special characters " & / < > ' 561 -- 562 # < is always > than & ' and " 563 `), 564 }, 565 { 566 name: "no args", 567 stdoutTTY: true, 568 wantOut: heredoc.Doc(` 569 OWNER/REPO 570 Some basic special characters " & / < > ' 571 572 573 # < is always > than & ' and " 574 575 576 577 View this repository on GitHub: https://github.com/OWNER/REPO 578 `), 579 }, 580 } 581 for _, tt := range tests { 582 if tt.opts == nil { 583 tt.opts = &ViewOptions{} 584 } 585 586 if tt.repoName == "" { 587 tt.repoName = "OWNER/REPO" 588 } 589 590 tt.opts.BaseRepo = func() (ghrepo.Interface, error) { 591 repo, _ := ghrepo.FromFullName(tt.repoName) 592 return repo, nil 593 } 594 595 reg := &httpmock.Registry{} 596 reg.Register( 597 httpmock.GraphQL(`query RepositoryInfo\b`), 598 httpmock.StringResponse(` 599 { "data": { 600 "repository": { 601 "description": "Some basic special characters \" & / < > '" 602 } } }`)) 603 reg.Register( 604 httpmock.REST("GET", fmt.Sprintf("repos/%s/readme", tt.repoName)), 605 httpmock.StringResponse(` 606 { "name": "readme.md", 607 "content": "IyA8IGlzIGFsd2F5cyA+IHRoYW4gJiAnIGFuZCAi"}`)) 608 609 tt.opts.HttpClient = func() (*http.Client, error) { 610 return &http.Client{Transport: reg}, nil 611 } 612 613 io, _, stdout, stderr := iostreams.Test() 614 tt.opts.IO = io 615 616 t.Run(tt.name, func(t *testing.T) { 617 io.SetStdoutTTY(tt.stdoutTTY) 618 619 if err := viewRun(tt.opts); (err != nil) != tt.wantErr { 620 t.Errorf("viewRun() error = %v, wantErr %v", err, tt.wantErr) 621 } 622 assert.Equal(t, tt.wantStderr, stderr.String()) 623 assert.Equal(t, tt.wantOut, stdout.String()) 624 reg.Verify(t) 625 }) 626 } 627 }