hub_test.rb 36.5 KB
Newer Older
1
require 'helper'
2
require 'webmock/test_unit'
3
require 'rbconfig'
4
require 'yaml'
5
require 'forwardable'
C
Chris Wanstrath 已提交
6

7 8 9 10 11 12
WebMock::BodyPattern.class_eval do
  undef normalize_hash
  # override normalizing hash since it otherwise requires JSON
  def normalize_hash(hash) hash end
end

C
Chris Wanstrath 已提交
13
class HubTest < Test::Unit::TestCase
14 15
  extend Forwardable

16 17 18 19 20
  if defined? WebMock::API
    include WebMock::API
  else
    include WebMock
  end
C
Chris Wanstrath 已提交
21

22 23
  COMMANDS = []

24 25 26 27
  Hub::Context.class_eval do
    remove_method :which
    define_method :which do |name|
      COMMANDS.include?(name) ? "/usr/bin/#{name}" : nil
28 29 30
    end
  end

31 32 33 34
  attr_reader :git_reader
  include Hub::Context::GitReaderMethods
  def_delegators :git_reader, :stub_config_value, :stub_command_output

35
  def setup
36
    super
37
    COMMANDS.replace %w[open groff]
38
    Hub::Context::PWD.replace '/path/to/hub'
39

40 41 42
    @git_reader = Hub::Context::GitReader.new 'git' do |cache, cmd|
      unless cmd.index('config --get alias.') == 0
        raise ArgumentError, "`git #{cmd}` not stubbed"
M
Mislav Marohnić 已提交
43
      end
44 45 46 47 48 49
    end

    Hub::Commands.instance_variable_set :@git_reader, @git_reader
    Hub::Commands.instance_variable_set :@local_repo, nil

    @git_reader.stub! \
50
      'remote' => "mislav\norigin",
51
      'symbolic-ref -q HEAD' => 'refs/heads/master',
52 53
      'config --get github.user'   => 'tpw',
      'config --get github.token'  => 'abc123',
54 55
      'config --get-all remote.origin.url' => 'git://github.com/defunkt/hub.git',
      'config --get-all remote.mislav.url' => 'git://github.com/mislav/hub.git',
56
      'rev-parse --symbolic-full-name master@{upstream}' => 'refs/remotes/origin/master',
57 58 59
      'config --get --bool hub.http-clone' => 'false',
      'config --get hub.protocol' => nil,
      'rev-parse -q --git-dir' => '.git'
60 61
  end

C
Chris Wanstrath 已提交
62
  def test_private_clone
63 64
    input   = "clone -p rtomayko/ronn"
    command = "git clone git@github.com:rtomayko/ronn.git"
C
Chris Wanstrath 已提交
65
    assert_command input, command
C
Chris Wanstrath 已提交
66 67
  end

68 69 70 71 72 73 74
  def test_https_clone
    stub_https_is_preferred
    input   = "clone rtomayko/ronn"
    command = "git clone https://github.com/rtomayko/ronn.git"
    assert_command input, command
  end

C
Chris Wanstrath 已提交
75
  def test_public_clone
76 77
    input   = "clone rtomayko/ronn"
    command = "git clone git://github.com/rtomayko/ronn.git"
C
Chris Wanstrath 已提交
78
    assert_command input, command
C
Chris Wanstrath 已提交
79 80
  end

81 82 83 84 85 86 87 88 89 90 91 92
  def test_your_private_clone
    input   = "clone -p resque"
    command = "git clone git@github.com:tpw/resque.git"
    assert_command input, command
  end

  def test_your_public_clone
    input   = "clone resque"
    command = "git clone git://github.com/tpw/resque.git"
    assert_command input, command
  end

93 94 95
  def test_clone_with_arguments
    input   = "clone --bare -o master resque"
    command = "git clone --bare -o master git://github.com/tpw/resque.git"
96 97 98
    assert_command input, command
  end

99 100 101 102
  def test_clone_with_arguments_and_destination
    assert_forwarded "clone --template=one/two git://github.com/tpw/resque.git --origin master resquetastic"
  end

103 104
  def test_your_private_clone_fails_without_config
    out = hub("clone -p mustache") do
105
      stub_github_user(nil)
106 107
    end

108
    assert_equal "** No GitHub user set. See http://help.github.com/git-email-settings/\n", out
109 110 111 112
  end

  def test_your_public_clone_fails_without_config
    out = hub("clone mustache") do
113
      stub_github_user(nil)
114 115
    end

116
    assert_equal "** No GitHub user set. See http://help.github.com/git-email-settings/\n", out
117 118
  end

119
  def test_private_clone_left_alone
120
    assert_forwarded "clone git@github.com:rtomayko/ronn.git"
121 122 123
  end

  def test_public_clone_left_alone
124
    assert_forwarded "clone git://github.com/rtomayko/ronn.git"
125
  end
C
Chris Wanstrath 已提交
126 127

  def test_normal_public_clone_with_path
128
    assert_forwarded "clone git://github.com/rtomayko/ronn.git ronn-dev"
C
Chris Wanstrath 已提交
129
  end
130 131

  def test_normal_clone_from_path
132
    assert_forwarded "clone ./test"
133
  end
134

135 136 137 138
  def test_clone_with_host_alias
    assert_forwarded "clone server:git/repo.git"
  end

M
Mislav Marohnić 已提交
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
  def test_alias_expand
    stub_alias 'c', 'clone --bare'
    input   = "c rtomayko/ronn"
    command = "git clone --bare git://github.com/rtomayko/ronn.git"
    assert_command input, command
  end

  def test_alias_expand_advanced
    stub_alias 'c', 'clone --template="white space"'
    input   = "c rtomayko/ronn"
    command = "git clone '--template=white space' git://github.com/rtomayko/ronn.git"
    assert_command input, command
  end

  def test_alias_doesnt_expand_for_unknown_commands
    stub_alias 'c', 'compute --fast'
    assert_forwarded "c rtomayko/ronn"
  end

158 159 160 161 162 163 164 165 166 167 168 169
  def test_remote_origin
    input   = "remote add origin"
    command = "git remote add origin git://github.com/tpw/hub.git"
    assert_command input, command
  end

  def test_private_remote_origin
    input   = "remote add -p origin"
    command = "git remote add origin git@github.com:tpw/hub.git"
    assert_command input, command
  end

170 171 172 173 174 175
  def test_public_remote_origin_as_normal
    input   = "remote add origin http://github.com/defunkt/resque.git"
    command = "git remote add origin http://github.com/defunkt/resque.git"
    assert_command input, command
  end

176
  def test_remote_from_rel_path
177
    assert_forwarded "remote add origin ./path"
178 179 180
  end

  def test_remote_from_abs_path
181
    assert_forwarded "remote add origin /path"
182 183
  end

184
  def test_remote_with_host_alias
185 186 187
    assert_forwarded "remote add origin server:/git/repo.git"
  end

188
  def test_private_remote_origin_as_normal
189
    assert_forwarded "remote add origin git@github.com:defunkt/resque.git"
C
Chris Wanstrath 已提交
190 191
  end

S
Stephen Celis 已提交
192 193
  def test_public_submodule
    input   = "submodule add wycats/bundler vendor/bundler"
J
Justin Ridgewell 已提交
194 195
    command = "git submodule add git://github.com/wycats/bundler.git vendor/bundler"
    assert_command input, command
S
Stephen Celis 已提交
196 197 198 199
  end

  def test_private_submodule
    input   = "submodule add -p grit vendor/grit"
J
Justin Ridgewell 已提交
200 201 202 203 204 205 206 207
    command = "git submodule add git@github.com:tpw/grit.git vendor/grit"
    assert_command input, command
  end

  def test_submodule_branch
    input   = "submodule add -b ryppl ryppl/pip vendor/pip"
    command = "git submodule add -b ryppl git://github.com/ryppl/pip.git vendor/pip"
    assert_command input, command
S
Stephen Celis 已提交
208 209 210 211 212
  end

  def test_submodule_with_args
    input   = "submodule -q add --bare -- grit grit"
    command = "git submodule -q add --bare -- git://github.com/tpw/grit.git grit"
J
Justin Ridgewell 已提交
213
    assert_command input, command
S
Stephen Celis 已提交
214 215
  end

C
Chris Wanstrath 已提交
216
  def test_private_remote
217
    input   = "remote add -p rtomayko"
C
Chris Wanstrath 已提交
218 219
    command = "git remote add rtomayko git@github.com:rtomayko/hub.git"
    assert_command input, command
C
Chris Wanstrath 已提交
220 221
  end

222 223 224 225 226 227 228
  def test_https_protocol_remote
    stub_https_is_preferred
    input   = "remote add rtomayko"
    command = "git remote add rtomayko https://github.com/rtomayko/hub.git"
    assert_command input, command
  end

C
Chris Wanstrath 已提交
229
  def test_public_remote
230
    input   = "remote add rtomayko"
C
Chris Wanstrath 已提交
231 232
    command = "git remote add rtomayko git://github.com/rtomayko/hub.git"
    assert_command input, command
C
Chris Wanstrath 已提交
233 234
  end

C
Chris Wanstrath 已提交
235 236 237 238 239 240
  def test_public_remote_f
    input   = "remote add -f rtomayko"
    command = "git remote add -f rtomayko git://github.com/rtomayko/hub.git"
    assert_command input, command
  end

241 242 243 244 245 246
  def test_named_public_remote
    input   = "remote add origin rtomayko"
    command = "git remote add origin git://github.com/rtomayko/hub.git"
    assert_command input, command
  end

247 248 249 250 251 252
  def test_named_public_remote_f
    input   = "remote add -f origin rtomayko"
    command = "git remote add -f origin git://github.com/rtomayko/hub.git"
    assert_command input, command
  end

C
Chris Wanstrath 已提交
253
  def test_private_remote_with_repo
254 255
    input   = "remote add -p jashkenas/coffee-script"
    command = "git remote add jashkenas git@github.com:jashkenas/coffee-script.git"
C
Chris Wanstrath 已提交
256 257 258 259
    assert_command input, command
  end

  def test_public_remote_with_repo
260 261
    input   = "remote add jashkenas/coffee-script"
    command = "git remote add jashkenas git://github.com/jashkenas/coffee-script.git"
262 263 264 265
    assert_command input, command
  end

  def test_public_remote_f_with_repo
266 267
    input   = "remote add -f jashkenas/coffee-script"
    command = "git remote add -f jashkenas git://github.com/jashkenas/coffee-script.git"
C
Chris Wanstrath 已提交
268 269 270
    assert_command input, command
  end

271
  def test_named_private_remote_with_repo
272 273
    input   = "remote add -p origin jashkenas/coffee-script"
    command = "git remote add origin git@github.com:jashkenas/coffee-script.git"
274 275 276
    assert_command input, command
  end

277
  def test_fetch_existing_remote
278
    assert_forwarded "fetch mislav"
279 280 281 282 283 284
  end

  def test_fetch_new_remote
    stub_remotes_group('xoebus', nil)
    stub_existing_fork('xoebus')

285 286 287
    assert_commands "git remote add xoebus git://github.com/xoebus/hub.git",
                    "git fetch xoebus",
                    "fetch xoebus"
288 289 290 291 292 293
  end

  def test_fetch_new_remote_with_options
    stub_remotes_group('xoebus', nil)
    stub_existing_fork('xoebus')

294 295 296
    assert_commands "git remote add xoebus git://github.com/xoebus/hub.git",
                    "git fetch --depth=1 --prune xoebus",
                    "fetch --depth=1 --prune xoebus"
297 298 299 300 301 302 303 304
  end

  def test_fetch_multiple_new_remotes
    stub_remotes_group('xoebus', nil)
    stub_remotes_group('rtomayko', nil)
    stub_existing_fork('xoebus')
    stub_existing_fork('rtomayko')

305 306 307 308
    assert_commands "git remote add xoebus git://github.com/xoebus/hub.git",
                    "git remote add rtomayko git://github.com/rtomayko/hub.git",
                    "git fetch --multiple xoebus rtomayko",
                    "fetch --multiple xoebus rtomayko"
309 310 311 312 313 314 315 316
  end

  def test_fetch_multiple_comma_separated_remotes
    stub_remotes_group('xoebus', nil)
    stub_remotes_group('rtomayko', nil)
    stub_existing_fork('xoebus')
    stub_existing_fork('rtomayko')

317 318 319 320
    assert_commands "git remote add xoebus git://github.com/xoebus/hub.git",
                    "git remote add rtomayko git://github.com/rtomayko/hub.git",
                    "git fetch --multiple xoebus rtomayko",
                    "fetch xoebus,rtomayko"
321 322 323 324 325 326 327 328 329 330 331 332 333 334
  end

  def test_fetch_multiple_new_remotes_with_filtering
    stub_remotes_group('xoebus', nil)
    stub_remotes_group('mygrp', 'one two')
    stub_remotes_group('typo', nil)
    stub_existing_fork('xoebus')
    stub_nonexisting_fork('typo')

    # mislav: existing remote; skipped
    # xoebus: new remote, fork exists; added
    # mygrp:  a remotes group; skipped
    # URL:    can't be a username; skipped
    # typo:   fork doesn't exist; skipped
335 336 337
    assert_commands "git remote add xoebus git://github.com/xoebus/hub.git",
                    "git fetch --multiple mislav xoebus mygrp git://example.com typo",
                    "fetch --multiple mislav xoebus mygrp git://example.com typo"
338 339
  end

340
  def test_cherry_pick
341
    assert_forwarded "cherry-pick a319d88"
342 343 344
  end

  def test_cherry_pick_url
345
    url = 'http://github.com/mislav/hub/commit/a319d88'
346
    assert_commands "git fetch mislav", "git cherry-pick a319d88", "cherry-pick #{url}"
347 348
  end

349 350
  def test_cherry_pick_url_with_fragment
    url = 'http://github.com/mislav/hub/commit/abcdef0123456789#comments'
351
    assert_commands "git fetch mislav", "git cherry-pick abcdef0123456789", "cherry-pick #{url}"
352 353
  end

354 355
  def test_cherry_pick_url_with_remote_add
    url = 'https://github.com/xoebus/hub/commit/a319d88'
356
    assert_commands "git remote add -f xoebus git://github.com/xoebus/hub.git",
357 358
                    "git cherry-pick a319d88",
                    "cherry-pick #{url}"
359 360 361 362
  end

  def test_cherry_pick_origin_url
    url = 'https://github.com/defunkt/hub/commit/a319d88'
363
    assert_commands "git fetch origin", "git cherry-pick a319d88", "cherry-pick #{url}"
364 365 366
  end

  def test_cherry_pick_github_user_notation
367
    assert_commands "git fetch mislav", "git cherry-pick 368af20", "cherry-pick mislav@368af20"
368 369 370 371
  end

  def test_cherry_pick_github_user_repo_notation
    # not supported
372
    assert_forwarded "cherry-pick mislav/hubbub@a319d88"
373 374 375
  end

  def test_cherry_pick_github_notation_too_short
376
    assert_forwarded "cherry-pick mislav@a319"
377 378 379
  end

  def test_cherry_pick_github_notation_with_remote_add
380 381 382
    assert_commands "git remote add -f xoebus git://github.com/xoebus/hub.git",
                    "git cherry-pick a319d88",
                    "cherry-pick xoebus@a319d88"
383 384
  end

385 386 387 388 389 390 391 392 393
  def test_am_untouched
    assert_forwarded "am some.patch"
  end

  def test_am_pull_request
    with_tmpdir('/tmp/') do
      assert_commands "curl -#LA 'hub #{Hub::Version}' https://github.com/defunkt/hub/pull/55.patch -o /tmp/55.patch",
                      "git am --signoff /tmp/55.patch -p2",
                      "am --signoff https://github.com/defunkt/hub/pull/55 -p2"
394 395 396

      cmd = Hub("am https://github.com/defunkt/hub/pull/55/files").command
      assert_includes '/pull/55.patch', cmd
397 398 399 400 401 402 403 404 405 406 407 408 409
    end
  end

  def test_am_commit_url
    with_tmpdir('/tmp/') do
      url = 'https://github.com/davidbalbert/hub/commit/fdb9921'

      assert_commands "curl -#LA 'hub #{Hub::Version}' #{url}.patch -o /tmp/fdb9921.patch",
                      "git am --signoff /tmp/fdb9921.patch -p2",
                      "am --signoff #{url} -p2"
    end
  end

410 411 412 413 414 415 416 417 418 419
  def test_am_gist
    with_tmpdir('/tmp/') do
      url = 'https://gist.github.com/8da7fb575debd88c54cf'

      assert_commands "curl -#LA 'hub #{Hub::Version}' #{url}.txt -o /tmp/gist-8da7fb575debd88c54cf.txt",
                      "git am --signoff /tmp/gist-8da7fb575debd88c54cf.txt -p2",
                      "am --signoff #{url} -p2"
    end
  end

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
  def test_apply_untouched
    assert_forwarded "apply some.patch"
  end

  def test_apply_pull_request
    with_tmpdir('/tmp/') do
      assert_commands "curl -#LA 'hub #{Hub::Version}' https://github.com/defunkt/hub/pull/55.patch -o /tmp/55.patch",
                      "git apply /tmp/55.patch -p2",
                      "apply https://github.com/defunkt/hub/pull/55 -p2"

      cmd = Hub("apply https://github.com/defunkt/hub/pull/55/files").command
      assert_includes '/pull/55.patch', cmd
    end
  end

  def test_apply_commit_url
    with_tmpdir('/tmp/') do
      url = 'https://github.com/davidbalbert/hub/commit/fdb9921'

      assert_commands "curl -#LA 'hub #{Hub::Version}' #{url}.patch -o /tmp/fdb9921.patch",
                      "git apply /tmp/fdb9921.patch -p2",
                      "apply #{url} -p2"
    end
  end

  def test_apply_gist
    with_tmpdir('/tmp/') do
      url = 'https://gist.github.com/8da7fb575debd88c54cf'

      assert_commands "curl -#LA 'hub #{Hub::Version}' #{url}.txt -o /tmp/gist-8da7fb575debd88c54cf.txt",
                      "git apply /tmp/gist-8da7fb575debd88c54cf.txt -p2",
                      "apply #{url} -p2"
    end
  end

C
Chris Wanstrath 已提交
455
  def test_init
456
    assert_commands "git init", "git remote add origin git@github.com:tpw/hub.git", "init -g"
457 458 459 460
  end

  def test_init_no_login
    out = hub("init -g") do
461
      stub_github_user(nil)
462 463
    end

464
    assert_equal "** No GitHub user set. See http://help.github.com/git-email-settings/\n", out
C
Chris Wanstrath 已提交
465 466
  end

467 468 469 470
  def test_push_untouched
    assert_forwarded "push"
  end

471
  def test_push_two
472 473
    assert_commands "git push origin cool-feature", "git push staging cool-feature",
                    "push origin,staging cool-feature"
474 475
  end

476 477 478 479 480 481
  def test_push_current_branch
    stub_branch('refs/heads/cool-feature')
    assert_commands "git push origin cool-feature", "git push staging cool-feature",
                    "push origin,staging"
  end

C
Chris Wanstrath 已提交
482
  def test_push_more
483 484 485 486
    assert_commands "git push origin cool-feature",
                    "git push staging cool-feature",
                    "git push qa cool-feature",
                    "push origin,staging,qa cool-feature"
C
Chris Wanstrath 已提交
487
  end
C
Chris Wanstrath 已提交
488

489
  def test_create
490
    stub_no_remotes
491
    stub_nonexisting_fork('tpw')
492 493
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/repos/create").
      with(:body => { 'name' => 'hub' })
494

495
    expected = "remote add -f origin git@github.com:tpw/hub.git\n"
496
    expected << "created repository: tpw/hub\n"
497 498 499
    assert_equal expected, hub("create") { ENV['GIT'] = 'echo' }
  end

500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
  def test_create_custom_name
    stub_no_remotes
    stub_nonexisting_fork('tpw', 'hubbub')
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/repos/create").
      with(:body => { 'name' => 'hubbub' })

    expected = "remote add -f origin git@github.com:tpw/hubbub.git\n"
    expected << "created repository: tpw/hubbub\n"
    assert_equal expected, hub("create hubbub") { ENV['GIT'] = 'echo' }
  end

  def test_create_in_organization
    stub_no_remotes
    stub_nonexisting_fork('acme', 'hubbub')
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/repos/create").
      with(:body => { 'name' => 'acme/hubbub' })

    expected = "remote add -f origin git@github.com:acme/hubbub.git\n"
    expected << "created repository: acme/hubbub\n"
    assert_equal expected, hub("create acme/hubbub") { ENV['GIT'] = 'echo' }
  end

522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
  def test_create_no_openssl
    stub_no_remotes
    stub_nonexisting_fork('tpw')
    stub_request(:post, "http://#{auth}github.com/api/v2/yaml/repos/create").
      with(:body => { 'name' => 'hub' })

    expected = "remote add -f origin git@github.com:tpw/hub.git\n"
    expected << "created repository: tpw/hub\n"

    assert_equal expected, hub("create") {
      ENV['GIT'] = 'echo'
      require 'net/https'
      Object.send :remove_const, :OpenSSL
    }
  end

538 539 540
  def test_create_failed
    stub_no_remotes
    stub_nonexisting_fork('tpw')
541
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/repos/create").
542 543
      to_return(:status => [401, "Your token is fail"])

544 545
    expected = "Error creating repository: Your token is fail (HTTP 401)\n"
    expected << "Check your token configuration (`git config github.token`)\n"
546 547 548
    assert_equal expected, hub("create") { ENV['GIT'] = 'echo' }
  end

549 550 551 552 553 554 555 556 557
  def test_create_with_env_authentication
    stub_no_remotes
    stub_nonexisting_fork('mojombo')

    old_user  = ENV['GITHUB_USER']
    old_token = ENV['GITHUB_TOKEN']
    ENV['GITHUB_USER']  = 'mojombo'
    ENV['GITHUB_TOKEN'] = '123abc'

558 559
    stub_request(:post, "https://#{auth('mojombo', '123abc')}github.com/api/v2/yaml/repos/create").
      with(:body => { 'name' => 'hub' })
560 561 562 563 564 565 566 567 568 569

    expected = "remote add -f origin git@github.com:mojombo/hub.git\n"
    expected << "created repository: mojombo/hub\n"
    assert_equal expected, hub("create") { ENV['GIT'] = 'echo' }

  ensure
    ENV['GITHUB_USER']  = old_user
    ENV['GITHUB_TOKEN'] = old_token
  end

570
  def test_create_private_repository
571
    stub_no_remotes
572
    stub_nonexisting_fork('tpw')
573 574
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/repos/create").
      with(:body => { 'name' => 'hub', 'public' => '0' })
575

576
    expected = "remote add -f origin git@github.com:tpw/hub.git\n"
577
    expected << "created repository: tpw/hub\n"
578 579 580
    assert_equal expected, hub("create -p") { ENV['GIT'] = 'echo' }
  end

581
  def test_create_with_description_and_homepage
582
    stub_no_remotes
583
    stub_nonexisting_fork('tpw')
584 585
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/repos/create").with(:body => {
      'name' => 'hub', 'description' => 'toyproject', 'homepage' => 'http://example.com'
586 587
    })

588
    expected = "remote add -f origin git@github.com:tpw/hub.git\n"
589
    expected << "created repository: tpw/hub\n"
590
    assert_equal expected, hub("create -d toyproject -h http://example.com") { ENV['GIT'] = 'echo' }
591 592
  end

593 594 595 596 597
  def test_create_with_invalid_arguments
    assert_equal "invalid argument: -a\n",   hub("create -a blah")   { ENV['GIT'] = 'echo' }
    assert_equal "invalid argument: bleh\n", hub("create blah bleh") { ENV['GIT'] = 'echo' }
  end

598
  def test_create_with_existing_repository
599
    stub_no_remotes
600 601 602 603
    stub_existing_fork('tpw')

    expected = "tpw/hub already exists on GitHub\n"
    expected << "remote add -f origin git@github.com:tpw/hub.git\n"
604
    expected << "set remote origin: tpw/hub\n"
605 606 607
    assert_equal expected, hub("create") { ENV['GIT'] = 'echo' }
  end

608 609 610 611 612 613 614 615 616 617 618
  def test_create_https_protocol
    stub_no_remotes
    stub_existing_fork('tpw')
    stub_https_is_preferred

    expected = "tpw/hub already exists on GitHub\n"
    expected << "remote add -f origin https://github.com/tpw/hub.git\n"
    expected << "set remote origin: tpw/hub\n"
    assert_equal expected, hub("create") { ENV['GIT'] = 'echo' }
  end

619
  def test_create_no_user
620
    stub_no_remotes
621 622 623
    out = hub("create") do
      stub_github_token(nil)
    end
624
    assert_equal "** No GitHub token set. See http://help.github.com/git-email-settings/\n", out
625 626
  end

627
  def test_create_outside_git_repo
628
    stub_no_git_repo
629 630
    assert_equal "'create' must be run from inside a git repository\n", hub("create")
  end
631 632

  def test_create_origin_already_exists
C
Chris Wanstrath 已提交
633
    stub_nonexisting_fork('tpw')
634 635
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/repos/create").
      with(:body => { 'name' => 'hub' })
C
Chris Wanstrath 已提交
636 637

    expected = "remote -v\ncreated repository: tpw/hub\n"
638 639
    assert_equal expected, hub("create") { ENV['GIT'] = 'echo' }
  end
C
Chris Wanstrath 已提交
640

641
  def test_fork
C
Chris Wanstrath 已提交
642
    stub_nonexisting_fork('tpw')
643
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/repos/fork/defunkt/hub")
C
Chris Wanstrath 已提交
644

C
Chris Wanstrath 已提交
645
    expected = "remote add -f tpw git@github.com:tpw/hub.git\n"
646 647 648 649
    expected << "new remote: tpw\n"
    assert_equal expected, hub("fork") { ENV['GIT'] = 'echo' }
  end

650 651
  def test_fork_failed
    stub_nonexisting_fork('tpw')
652
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/repos/fork/defunkt/hub").
653 654
      to_return(:status => [500, "Your fork is fail"])

655
    expected = "Error creating fork: Your fork is fail (HTTP 500)\n"
656 657 658
    assert_equal expected, hub("fork") { ENV['GIT'] = 'echo' }
  end

659
  def test_fork_no_remote
660
    stub_nonexisting_fork('tpw')
661
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/repos/fork/defunkt/hub")
C
Chris Wanstrath 已提交
662

663 664 665 666
    assert_equal "", hub("fork --no-remote") { ENV['GIT'] = 'echo' }
  end

  def test_fork_already_exists
667
    stub_existing_fork('tpw')
C
Chris Wanstrath 已提交
668

669
    expected = "tpw/hub already exists on GitHub\n"
C
Chris Wanstrath 已提交
670
    expected << "remote add -f tpw git@github.com:tpw/hub.git\n"
671 672 673
    expected << "new remote: tpw\n"
    assert_equal expected, hub("fork") { ENV['GIT'] = 'echo' }
  end
C
Chris Wanstrath 已提交
674

675 676 677 678 679 680 681 682 683 684
  def test_fork_https_protocol
    stub_existing_fork('tpw')
    stub_https_is_preferred

    expected = "tpw/hub already exists on GitHub\n"
    expected << "remote add -f tpw https://github.com/tpw/hub.git\n"
    expected << "new remote: tpw\n"
    assert_equal expected, hub("fork") { ENV['GIT'] = 'echo' }
  end

685 686 687 688 689 690
  def test_pullrequest
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/pulls/defunkt/hub").
      with(:body => { 'pull' => {'base' => "master", 'head' => "tpw:master", 'title' => "hereyougo"} }).
      to_return(:body => mock_pullreq_response(1))

    expected = "https://github.com/defunkt/hub/pull/1\n"
691 692 693 694
    assert_output expected, "pull-request hereyougo -f"
  end

  def test_pullrequest_with_checks
695
    stub_command_output "rev-list --cherry origin/master...", "+abcd1234\n+bcde2345"
696 697 698

    expected = "Aborted: 2 commits are not yet pushed to origin/master\n" <<
      "(use `-f` to force submit a pull request anyway)\n"
699 700 701 702 703 704 705 706 707 708 709
    assert_output expected, "pull-request hereyougo"
  end

  def test_pullrequest_from_branch
    stub_branch('refs/heads/feature')
    stub_tracking_nothing('feature')
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/pulls/defunkt/hub").
      with(:body => { 'pull' => {'base' => "master", 'head' => "tpw:feature", 'title' => "hereyougo"} }).
      to_return(:body => mock_pullreq_response(1))

    expected = "https://github.com/defunkt/hub/pull/1\n"
710
    assert_output expected, "pull-request hereyougo -f"
711 712 713 714 715
  end

  def test_pullrequest_from_tracking_branch
    stub_branch('refs/heads/feature')
    stub_tracking('feature', 'tpw', 'yay-feature')
716 717 718
    stub_command_output 'remote', "origin\ntpw"
    stub_config_value 'remote.tpw.url', 'git://github.com/tpw/hub.git', '--get-all'

719 720 721 722 723
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/pulls/defunkt/hub").
      with(:body => { 'pull' => {'base' => "master", 'head' => "tpw:yay-feature", 'title' => "hereyougo"} }).
      to_return(:body => mock_pullreq_response(1))

    expected = "https://github.com/defunkt/hub/pull/1\n"
724
    assert_output expected, "pull-request hereyougo -f"
725 726 727 728 729 730 731 732
  end

  def test_pullrequest_explicit_head
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/pulls/defunkt/hub").
      with(:body => { 'pull' => {'base' => "master", 'head' => "tpw:yay-feature", 'title' => "hereyougo"} }).
      to_return(:body => mock_pullreq_response(1))

    expected = "https://github.com/defunkt/hub/pull/1\n"
733
    assert_output expected, "pull-request hereyougo -h yay-feature -f"
734 735 736 737 738 739 740 741
  end

  def test_pullrequest_explicit_head_with_owner
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/pulls/defunkt/hub").
      with(:body => { 'pull' => {'base' => "master", 'head' => "mojombo:feature", 'title' => "hereyougo"} }).
      to_return(:body => mock_pullreq_response(1))

    expected = "https://github.com/defunkt/hub/pull/1\n"
742
    assert_output expected, "pull-request hereyougo -h mojombo:feature -f"
743 744 745 746 747 748 749 750
  end

  def test_pullrequest_explicit_base
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/pulls/defunkt/hub").
      with(:body => { 'pull' => {'base' => "feature", 'head' => "tpw:master", 'title' => "hereyougo"} }).
      to_return(:body => mock_pullreq_response(1))

    expected = "https://github.com/defunkt/hub/pull/1\n"
751
    assert_output expected, "pull-request hereyougo -b feature -f"
752 753 754 755 756 757 758 759
  end

  def test_pullrequest_explicit_base_with_owner
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/pulls/mojombo/hub").
      with(:body => { 'pull' => {'base' => "feature", 'head' => "tpw:master", 'title' => "hereyougo"} }).
      to_return(:body => mock_pullreq_response(1))

    expected = "https://github.com/defunkt/hub/pull/1\n"
760
    assert_output expected, "pull-request hereyougo -b mojombo:feature -f"
761 762
  end

763 764 765 766 767 768
  def test_pullrequest_explicit_base_with_repo
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/pulls/mojombo/hubbub").
      with(:body => { 'pull' => {'base' => "feature", 'head' => "tpw:master", 'title' => "hereyougo"} }).
      to_return(:body => mock_pullreq_response(1))

    expected = "https://github.com/defunkt/hub/pull/1\n"
769
    assert_output expected, "pull-request hereyougo -b mojombo/hubbub:feature -f"
770 771 772
  end

  def test_pullrequest_existing_issue
773 774 775 776 777
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/pulls/defunkt/hub").
      with(:body => { 'pull' => {'base' => "master", 'head' => "tpw:master", 'issue' => '92'} }).
      to_return(:body => mock_pullreq_response(92))

    expected = "https://github.com/defunkt/hub/pull/92\n"
778
    assert_output expected, "pull-request #92 -f"
779 780
  end

781 782 783 784 785 786
  def test_pullrequest_existing_issue_url
    stub_request(:post, "https://#{auth}github.com/api/v2/yaml/pulls/mojombo/hub").
      with(:body => { 'pull' => {'base' => "master", 'head' => "tpw:master", 'issue' => '92'} }).
      to_return(:body => mock_pullreq_response(92, 'mojombo/hub'))

    expected = "https://github.com/mojombo/hub/pull/92\n"
787
    assert_output expected, "pull-request https://github.com/mojombo/hub/issues/92#comment_4 -f"
788 789
  end

C
Chris Wanstrath 已提交
790
  def test_version
791
    out = hub('--version')
792
    assert_includes "git version 1.7.0.4", out
C
Chris Wanstrath 已提交
793
    assert_includes "hub version #{Hub::Version}", out
C
Chris Wanstrath 已提交
794
  end
C
Chris Wanstrath 已提交
795

796 797 798 799 800 801 802
  def test_exec_path
    out = hub('--exec-path')
    assert_equal "/usr/lib/git-core\n", out
  end

  def test_exec_path_arg
    out = hub('--exec-path=/home/wombat/share/my-l33t-git-core')
803
    assert_equal Hub::Commands.improved_help_text, out
804 805 806 807 808 809 810
  end

  def test_html_path
    out = hub('--html-path')
    assert_equal "/usr/share/doc/git-doc\n", out
  end

C
Chris Wanstrath 已提交
811 812 813 814 815 816 817
  def test_help
    assert_equal Hub::Commands.improved_help_text, hub("help")
  end

  def test_help_by_default
    assert_equal Hub::Commands.improved_help_text, hub("")
  end
C
Chris Wanstrath 已提交
818

S
Stephen Celis 已提交
819 820 821 822
  def test_help_with_pager
    assert_equal Hub::Commands.improved_help_text, hub("-p")
  end

C
Chris Wanstrath 已提交
823 824 825 826
  def test_help_hub
    help_manpage = hub("help hub")
    assert_includes "git + hub = github", help_manpage
    assert_includes <<-config, help_manpage
C
Chris Wanstrath 已提交
827
Use git-config(1) to display the currently configured GitHub username:
C
Chris Wanstrath 已提交
828 829 830 831
config
  end

  def test_help_hub_no_groff
832 833
    stub_available_commands()
    assert_equal "** Can't find groff(1)\n", hub("help hub")
C
Chris Wanstrath 已提交
834
  end
835

836 837 838 839 840
  def test_hub_standalone
    help_standalone = hub("hub standalone")
    assert_equal Hub::Standalone.build, help_standalone
  end

C
Chris Wanstrath 已提交
841 842
  def test_hub_compare
    assert_command "compare refactor",
843
      "open https://github.com/defunkt/hub/compare/refactor"
844
  end
C
Chris Wanstrath 已提交
845

846 847 848 849
  def test_hub_compare_nothing
    expected = "Usage: hub compare [USER] [<START>...]<END>\n"
    assert_equal expected, hub("compare")
  end
C
Chris Wanstrath 已提交
850

851 852 853 854 855 856 857 858
  def test_hub_compare_tracking_nothing
    stub_tracking_nothing
    expected = "Usage: hub compare [USER] [<START>...]<END>\n"
    assert_equal expected, hub("compare")
  end

  def test_hub_compare_tracking_branch
    stub_branch('refs/heads/feature')
859
    stub_tracking('feature', 'mislav', 'experimental')
860 861

    assert_command "compare",
862
      "open https://github.com/mislav/hub/compare/experimental"
863 864
  end

865
  def test_hub_compare_range
C
Chris Wanstrath 已提交
866
    assert_command "compare 1.0...fix",
867
      "open https://github.com/defunkt/hub/compare/1.0...fix"
868
  end
C
Chris Wanstrath 已提交
869

870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
  def test_hub_compare_range_fixes_two_dots_for_tags
    assert_command "compare 1.0..fix",
      "open https://github.com/defunkt/hub/compare/1.0...fix"
  end

  def test_hub_compare_range_fixes_two_dots_for_shas
    assert_command "compare 1234abc..3456cde",
      "open https://github.com/defunkt/hub/compare/1234abc...3456cde"
  end

  def test_hub_compare_range_ignores_two_dots_for_complex_ranges
    assert_command "compare @{a..b}..@{c..d}",
      "open https://github.com/defunkt/hub/compare/@{a..b}..@{c..d}"
  end

885 886 887 888 889 890
  def test_hub_compare_on_wiki
    stub_repo_url 'git://github.com/defunkt/hub.wiki.git'
    assert_command "compare 1.0...fix",
      "open https://github.com/defunkt/hub/wiki/_compare/1.0...fix"
  end

891
  def test_hub_compare_fork
C
Chris Wanstrath 已提交
892
    assert_command "compare myfork feature",
893
      "open https://github.com/myfork/hub/compare/feature"
894 895 896 897
  end

  def test_hub_compare_url
    assert_command "compare -u 1.0...1.1",
898
      "echo https://github.com/defunkt/hub/compare/1.0...1.1"
899 900 901
  end

  def test_hub_browse
902
    assert_command "browse mojombo/bert", "open https://github.com/mojombo/bert"
903 904
  end

905 906 907 908
  def test_hub_browse_commit
    assert_command "browse mojombo/bert commit/5d5582", "open https://github.com/mojombo/bert/commit/5d5582"
  end

909 910
  def test_hub_browse_tracking_nothing
    stub_tracking_nothing
911
    assert_command "browse mojombo/bert", "open https://github.com/mojombo/bert"
912 913
  end

914
  def test_hub_browse_url
915
    assert_command "browse -u mojombo/bert", "echo https://github.com/mojombo/bert"
916 917
  end

918
  def test_hub_browse_self
919
    assert_command "browse resque", "open https://github.com/tpw/resque"
920 921
  end

922 923
  def test_hub_browse_subpage
    assert_command "browse resque commits",
924
      "open https://github.com/tpw/resque/commits/master"
925
    assert_command "browse resque issues",
926
      "open https://github.com/tpw/resque/issues"
927
    assert_command "browse resque wiki",
928
      "open https://github.com/tpw/resque/wiki"
929 930 931 932
  end

  def test_hub_browse_on_branch
    stub_branch('refs/heads/feature')
933
    stub_tracking('feature', 'mislav', 'experimental')
934

935
    assert_command "browse resque", "open https://github.com/tpw/resque"
936
    assert_command "browse resque commits",
937
      "open https://github.com/tpw/resque/commits/master"
938 939

    assert_command "browse",
940
      "open https://github.com/mislav/hub/tree/experimental"
941
    assert_command "browse -- tree",
942
      "open https://github.com/mislav/hub/tree/experimental"
943
    assert_command "browse -- commits",
944
      "open https://github.com/mislav/hub/commits/experimental"
945
  end
946

947
  def test_hub_browse_current
948 949
    assert_command "browse", "open https://github.com/defunkt/hub"
    assert_command "browse --", "open https://github.com/defunkt/hub"
950 951
  end

952 953 954 955
  def test_hub_browse_commit_from_current
    assert_command "browse -- commit/6616e4", "open https://github.com/defunkt/hub/commit/6616e4"
  end

956 957 958 959 960 961 962
  def test_hub_browse_no_tracking
    stub_tracking_nothing
    assert_command "browse", "open https://github.com/defunkt/hub"
  end

  def test_hub_browse_no_tracking_on_branch
    stub_branch('refs/heads/feature')
963
    stub_tracking_nothing('feature')
964 965 966
    assert_command "browse", "open https://github.com/defunkt/hub"
  end

967 968 969 970 971 972 973 974 975
  def test_hub_browse_current_wiki
    stub_repo_url 'git://github.com/defunkt/hub.wiki.git'

    assert_command "browse", "open https://github.com/defunkt/hub/wiki"
    assert_command "browse -- wiki", "open https://github.com/defunkt/hub/wiki"
    assert_command "browse -- commits", "open https://github.com/defunkt/hub/wiki/_history"
    assert_command "browse -- pages", "open https://github.com/defunkt/hub/wiki/_pages"
  end

976 977
  def test_hub_browse_current_subpage
    assert_command "browse -- network",
978
      "open https://github.com/defunkt/hub/network"
979
    assert_command "browse -- anything/everything",
980
      "open https://github.com/defunkt/hub/anything/everything"
981 982
  end

983 984 985 986
  def test_hub_browse_deprecated_private
    with_browser_env('echo') do
      assert_includes "Warning: the `-p` flag has no effect anymore\n", hub("browse -p defunkt/hub")
    end
987 988
  end

989 990 991
  def test_hub_browse_no_repo
    stub_repo_url(nil)
    assert_equal "Usage: hub browse [<USER>/]<REPOSITORY>\n", hub("browse")
992
  end
993 994 995 996 997 998 999 1000 1001 1002

  def test_custom_browser
    with_browser_env("custom") do
      assert_browser("custom")
    end
  end

  def test_linux_browser
    stub_available_commands "open", "xdg-open", "cygstart"
    with_browser_env(nil) do
1003
      with_host_os("i686-linux") do
1004 1005 1006 1007 1008 1009 1010 1011
        assert_browser("xdg-open")
      end
    end
  end

  def test_cygwin_browser
    stub_available_commands "open", "cygstart"
    with_browser_env(nil) do
1012
      with_host_os("i686-linux") do
1013 1014 1015 1016 1017 1018 1019 1020 1021
        assert_browser("cygstart")
      end
    end
  end

  def test_no_browser
    stub_available_commands()
    expected = "Please set $BROWSER to a web launcher to use this command.\n"
    with_browser_env(nil) do
1022
      with_host_os("i686-linux") do
1023 1024 1025 1026 1027
        assert_equal expected, hub("browse")
      end
    end
  end

1028
  def test_context_method_doesnt_hijack_git_command
1029
    assert_forwarded 'remotes'
1030 1031
  end

1032 1033 1034 1035 1036
  def test_not_choking_on_ruby_methods
    assert_forwarded 'id'
    assert_forwarded 'name'
  end

1037 1038
  def test_multiple_remote_urls
    stub_repo_url("git://example.com/other.git\ngit://github.com/my/repo.git")
1039
    assert_command "browse", "open https://github.com/my/repo"
1040 1041
  end

1042 1043 1044
  def test_global_flags_preserved
    cmd = '--no-pager --bare -c core.awesome=true -c name=value --git-dir=/srv/www perform'
    assert_command cmd, 'git --bare -c core.awesome=true -c name=value --git-dir=/srv/www --no-pager perform'
1045
    assert_equal %w[git --bare -c core.awesome=true -c name=value --git-dir=/srv/www], git_reader.executable
1046 1047
  end

1048 1049
  protected

1050
    def stub_github_user(name)
1051
      stub_config_value 'github.user', name
1052 1053
    end

1054
    def stub_github_token(token)
1055
      stub_config_value 'github.token', token
1056 1057
    end

1058
    def stub_repo_url(value)
1059
      stub_config_value 'remote.origin.url', value, '--get-all'
1060 1061 1062
    end

    def stub_branch(value)
1063
      stub_command_output 'symbolic-ref -q HEAD', value
1064 1065
    end

1066
    def stub_tracking(from, remote_name, remote_branch)
1067 1068
      stub_command_output "rev-parse --symbolic-full-name #{from}@{upstream}",
        remote_branch ? "refs/remotes/#{remote_name}/#{remote_branch}" : nil
1069 1070
    end

1071 1072
    def stub_tracking_nothing(from = 'master')
      stub_tracking(from, nil, nil)
1073 1074
    end

1075
    def stub_remotes_group(name, value)
1076
      stub_config_value "remotes.#{name}", value
1077 1078
    end

1079
    def stub_no_remotes
1080
      stub_command_output 'remote', nil
1081 1082 1083
    end

    def stub_no_git_repo
1084
      stub_command_output 'rev-parse -q --git-dir', nil
1085 1086
    end

M
Mislav Marohnić 已提交
1087
    def stub_alias(name, value)
1088
      stub_config_value "alias.#{name}", value
M
Mislav Marohnić 已提交
1089 1090
    end

1091 1092
    def stub_existing_fork(user, repo = 'hub')
      stub_fork(user, repo, 200)
1093 1094
    end

1095 1096
    def stub_nonexisting_fork(user, repo = 'hub')
      stub_fork(user, repo, 404)
1097 1098
    end

1099 1100
    def stub_fork(user, repo, status)
      stub_request(:get, "github.com/api/v2/yaml/repos/show/#{user}/#{repo}").
1101 1102 1103
        to_return(:status => status)
    end

1104 1105 1106 1107
    def stub_available_commands(*names)
      COMMANDS.replace names
    end

1108
    def stub_https_is_preferred
1109
      stub_config_value 'hub.protocol', 'https'
1110 1111
    end

1112 1113 1114 1115 1116 1117 1118
    def with_browser_env(value)
      browser, ENV['BROWSER'] = ENV['BROWSER'], value
      yield
    ensure
      ENV['BROWSER'] = browser
    end

1119 1120 1121 1122 1123 1124 1125
    def with_tmpdir(value)
      dir, ENV['TMPDIR'] = ENV['TMPDIR'], value
      yield
    ensure
      ENV['TMPDIR'] = dir
    end

1126
    def assert_browser(browser)
1127
      assert_command "browse", "#{browser} https://github.com/defunkt/hub"
1128 1129
    end

1130 1131 1132 1133 1134 1135 1136 1137
    def with_host_os(value)
      host_os = RbConfig::CONFIG['host_os']
      RbConfig::CONFIG['host_os'] = value
      begin
        yield
      ensure
        RbConfig::CONFIG['host_os'] = host_os
      end
1138 1139
    end

1140
    def auth(user = git_config('github.user'), password = git_config('github.token'))
1141 1142 1143
      "#{user}%2Ftoken:#{password}@"
    end

1144 1145 1146 1147 1148 1149
    def mock_pullreq_response(id, name_with_owner = 'defunkt/hub')
      YAML.dump('pull' => {
        'html_url' => "https://github.com/#{name_with_owner}/pull/#{id}"
      })
    end

C
Chris Wanstrath 已提交
1150
end