git_reflow 0.6.7 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +11 -9
  3. data/README.rdoc +3 -1
  4. data/bin/git-reflow +0 -11
  5. data/bin/gitreflow-common +1 -1
  6. data/git_reflow.gemspec +3 -2
  7. data/lib/git_reflow.rb +13 -60
  8. data/lib/git_reflow/commands/deliver.rb +1 -2
  9. data/lib/git_reflow/commands/start.rb +0 -6
  10. data/lib/git_reflow/config.rb +15 -14
  11. data/lib/git_reflow/git_server.rb +14 -4
  12. data/lib/git_reflow/git_server/base.rb +0 -39
  13. data/lib/git_reflow/git_server/bit_bucket.rb +15 -80
  14. data/lib/git_reflow/git_server/bit_bucket/pull_request.rb +84 -0
  15. data/lib/git_reflow/git_server/git_hub.rb +18 -75
  16. data/lib/git_reflow/git_server/git_hub/pull_request.rb +108 -0
  17. data/lib/git_reflow/git_server/pull_request.rb +97 -0
  18. data/lib/git_reflow/version.rb +1 -1
  19. data/spec/fixtures/issues/comment.json.erb +27 -0
  20. data/spec/fixtures/issues/comments.json.erb +15 -0
  21. data/spec/fixtures/pull_requests/comment.json.erb +45 -0
  22. data/spec/fixtures/pull_requests/comments.json.erb +15 -0
  23. data/spec/fixtures/pull_requests/commits.json +29 -0
  24. data/spec/fixtures/pull_requests/external_pull_request.json +145 -0
  25. data/spec/fixtures/pull_requests/pull_request.json +19 -0
  26. data/spec/fixtures/pull_requests/pull_request.json.erb +142 -0
  27. data/spec/fixtures/pull_requests/pull_requests.json +19 -0
  28. data/spec/fixtures/repositories/commit.json.erb +53 -0
  29. data/spec/fixtures/repositories/commits.json.erb +13 -0
  30. data/spec/git_reflow_spec.rb +32 -25
  31. data/spec/lib/git_reflow/config_spec.rb +22 -6
  32. data/spec/lib/git_server/bit_bucket_spec.rb +5 -34
  33. data/spec/lib/git_server/git_hub/pull_request_spec.rb +319 -0
  34. data/spec/lib/git_server/git_hub_spec.rb +17 -25
  35. data/spec/lib/git_server/pull_request_spec.rb +93 -0
  36. data/spec/support/command_line_helpers.rb +16 -1
  37. data/spec/support/fake_github.rb +128 -0
  38. data/spec/support/fixtures.rb +52 -6
  39. data/spec/support/github_helpers.rb +22 -12
  40. metadata +47 -6
@@ -0,0 +1,53 @@
1
+ {
2
+ "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
3
+ "commit": {
4
+ "url": "https://api.github.com/repos/<%= repo_owner %>/<%= repo_name %>/git/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e",
5
+ "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
6
+ "author": {
7
+ "name": "Monalisa Octocat",
8
+ "email": "support@github.com",
9
+ "date": "<%= created_at || "2011-04-14T16:00:49Z" %>"
10
+ },
11
+ "committer": {
12
+ "name": "Monalisa Octocat",
13
+ "email": "support@github.com",
14
+ "date": "<%= created_at || "2011-04-14T16:00:49Z" %>"
15
+ },
16
+ "message": "Fix all the bugs",
17
+ "tree": {
18
+ "url": "https://api.github.com/repos/<%= repo_owner %>/<%= repo_name %>/tree/6dcb09b5b57875f334f61aebed695e2e4193db5e",
19
+ "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e"
20
+ }
21
+ },
22
+ "author": {
23
+ "login": "<%= author %>",
24
+ "id": 1,
25
+ "avatar_url": "https://github.com/images/error/octocat_happy.gif",
26
+ "url": "https://api.github.com/users/<%= author %>"
27
+ },
28
+ "committer": {
29
+ "login": "<%= author %>",
30
+ "id": 1,
31
+ "avatar_url": "https://github.com/images/error/octocat_happy.gif",
32
+ "url": "https://api.github.com/users/<%= author %>"
33
+ },
34
+ "parents": [
35
+ {
36
+ "url": "https://api.github.com/repos/<%= repo_owner %>/<%= repo_name %>/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e",
37
+ "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e"
38
+ }
39
+ ],
40
+ "stats": {
41
+ "additions": 104,
42
+ "deletions": 4,
43
+ "total": 108
44
+ },
45
+ "files": [
46
+ {
47
+ "filename": "file1.txt",
48
+ "additions": 10,
49
+ "deletions": 2,
50
+ "total": 12
51
+ }
52
+ ]
53
+ }
@@ -0,0 +1,13 @@
1
+ [
2
+ <% commits_json = [] %>
3
+ <% commits.each_with_index do |commit, index| %>
4
+ <% commits_json << Fixture.new('repositories/commit.json.erb',
5
+ id: index,
6
+ author: commit[:author],
7
+ repo_owner: repo_owner,
8
+ repo_name: repo_name,
9
+ created_at: commit[:created_at] || Chronic.parse("1.minute.ago")
10
+ ).to_s %>
11
+ <% end %>
12
+ <%= commits_json.join(", ") %>
13
+ ]
@@ -14,7 +14,7 @@ describe GitReflow do
14
14
  let(:hostname) { 'hostname.local' }
15
15
 
16
16
  let(:github_authorizations) { Github::Client::Authorizations.new }
17
- let(:existing_pull_requests) { JSON.parse(fixture('pull_requests/pull_requests.json').read).collect { |pull| Hashie::Mash.new(pull)} }
17
+ let(:existing_pull_requests) { Fixture.new('pull_requests/pull_requests.json').to_json_hashie }
18
18
  let(:existing_pull_request) { GitReflow::GitServer::GitHub::PullRequest.new existing_pull_requests.first }
19
19
 
20
20
  before do
@@ -37,11 +37,13 @@ describe GitReflow do
37
37
  subject { GitReflow.status(base_branch) }
38
38
 
39
39
  before do
40
- GitReflow.stub(:current_branch).and_return(feature_branch)
41
- GitReflow.stub(:destination_branch).and_return(base_branch)
40
+ allow(GitReflow).to receive(:current_branch).and_return(feature_branch)
41
+ allow(GitReflow).to receive(:destination_branch).and_return(base_branch)
42
42
 
43
- Github.stub(:new).and_return(github)
44
- GitReflow.stub(:git_server).and_return(git_server)
43
+ allow(Github).to receive(:new).and_return(github)
44
+ allow(GitReflow).to receive(:git_server).and_return(git_server)
45
+ allow(git_server).to receive(:connection).and_return(github)
46
+ allow(git_server).to receive(:get_build_status).and_return(Struct.new(:state, :description, :target_url).new())
45
47
  end
46
48
 
47
49
  context 'with no existing pull request' do
@@ -51,10 +53,12 @@ describe GitReflow do
51
53
  end
52
54
 
53
55
  context 'with an existing pull request' do
54
- before { git_server.stub(:find_open_pull_request).with({from: feature_branch, to: base_branch}).and_return(existing_pull_request) }
56
+ before do
57
+ git_server.stub(:find_open_pull_request).with({from: feature_branch, to: base_branch}).and_return(existing_pull_request)
58
+ end
55
59
 
56
60
  it 'displays a summary of the pull request and asks to open it in the browser' do
57
- GitReflow.should_receive(:display_pull_request_summary).with(existing_pull_request)
61
+ existing_pull_request.should_receive(:display_pull_request_summary)
58
62
  GitReflow.should_receive(:ask_to_open_in_browser).with(existing_pull_request.html_url)
59
63
  subject
60
64
  $output.should include "Here's the status of your review:"
@@ -77,6 +81,7 @@ describe GitReflow do
77
81
  }
78
82
 
79
83
  let(:github) do
84
+ allow_any_instance_of(GitReflow::GitServer::GitHub::PullRequest).to receive(:build).and_return(Struct.new(:state, :description, :url).new)
80
85
  stub_github_with({
81
86
  :user => user,
82
87
  :password => password,
@@ -115,15 +120,15 @@ describe GitReflow do
115
120
  context "pull request exists" do
116
121
  before do
117
122
  GitReflow.stub(:push_current_branch)
118
- github_error = Github::Error::UnprocessableEntity.new( eval(fixture('pull_requests/pull_request_exists_error.json').read) )
119
- github.stub(:create_pull_request).with(inputs.except('state')).and_raise(github_error)
120
- GitReflow.stub(:display_pull_request_summary)
123
+ github_error = Github::Error::UnprocessableEntity.new( eval(Fixture.new('pull_requests/pull_request_exists_error.json').to_s) )
124
+ github.should_receive(:find_open_pull_request).and_return(existing_pull_request)
125
+ existing_pull_request.stub(:display_pull_request_summary)
121
126
  end
122
127
 
123
128
  subject { GitReflow.review inputs }
124
129
 
125
130
  it "displays a pull request summary for the existing pull request" do
126
- GitReflow.should_receive(:display_pull_request_summary)
131
+ existing_pull_request.should_receive(:display_pull_request_summary)
127
132
  subject
128
133
  end
129
134
 
@@ -138,6 +143,7 @@ describe GitReflow do
138
143
  let(:branch) { 'new-feature' }
139
144
  let(:inputs) { {} }
140
145
  let!(:github) do
146
+ allow_any_instance_of(GitReflow::GitServer::GitHub::PullRequest).to receive(:build).and_return(Struct.new(:state, :description, :url).new)
141
147
  stub_github_with({
142
148
  :user => user,
143
149
  :password => password,
@@ -173,16 +179,17 @@ describe GitReflow do
173
179
  context "and pull request exists for the feature branch to the destination branch" do
174
180
  before do
175
181
  github.stub(:get_build_status).and_return(build_status)
176
- github.stub(:has_pull_request_comments?).and_return(true)
177
- github.stub(:comment_authors_for_pull_request).and_return(['codenamev'])
182
+ github.should_receive(:find_open_pull_request).and_return(existing_pull_request)
183
+ existing_pull_request.stub(:has_comments?).and_return(true)
184
+ github.stub(:reviewers).and_return(['codenamev'])
178
185
  end
179
186
 
180
187
  context 'and build status is not "success"' do
181
188
  let(:build_status) { Hashie::Mash.new({ state: 'failure', description: 'Build resulted in failed test(s)' }) }
182
189
 
183
190
  before do
184
- # just stubbing these in a locked state as the test is specific to this scenario
185
- GitReflow.stub(:has_pull_request_comments?).and_return(true)
191
+ existing_pull_request.stub(:build).and_return(build_status)
192
+ existing_pull_request.stub(:has_comments?).and_return(true)
186
193
  end
187
194
 
188
195
  it "halts delivery and notifies user of a failed build" do
@@ -196,8 +203,8 @@ describe GitReflow do
196
203
 
197
204
  before do
198
205
  # stubbing unrelated results so we can just test that it made it insdide the conditional block
199
- GitReflow.stub(:has_pull_request_comments?).and_return(true)
200
- GitReflow.stub(:comment_authors_for_pull_request).and_return([])
206
+ existing_pull_request.stub(:has_comments?).and_return(true)
207
+ existing_pull_request.stub(:reviewers).and_return([])
201
208
  GitReflow.stub(:update_destination).and_return(true)
202
209
  GitReflow.stub(:merge_feature_branch).and_return(true)
203
210
  GitReflow.stub(:append_to_squashed_commit_message).and_return(true)
@@ -213,14 +220,14 @@ describe GitReflow do
213
220
 
214
221
  context 'and has comments' do
215
222
  before do
216
- GitReflow.stub(:has_pull_request_comments?).and_return(true)
223
+ existing_pull_request.stub(:has_comments?).and_return(true)
217
224
  end
218
225
 
219
226
  context 'but there is a LGTM' do
220
227
  let(:lgtm_comment_authors) { ['nhance'] }
221
228
  before do
222
- github.stub(:approvals).and_return(lgtm_comment_authors)
223
- github.stub(:reviewers_pending_response).and_return([])
229
+ existing_pull_request.stub(:approvals).and_return(lgtm_comment_authors)
230
+ existing_pull_request.stub(:reviewers_pending_response).and_return([])
224
231
  end
225
232
 
226
233
  it "includes the pull request body in the commit message" do
@@ -236,7 +243,7 @@ describe GitReflow do
236
243
  existing_pull_request.description = ''
237
244
  github.stub(:find_open_pull_request).and_return(existing_pull_request)
238
245
  GitReflow.stub(:get_first_commit_message).and_return(first_commit_message)
239
- github.stub(:comment_authors_for_pull_request).and_return(lgtm_comment_authors)
246
+ existing_pull_request.stub(:reviewers).and_return(lgtm_comment_authors)
240
247
  end
241
248
 
242
249
  it "includes the first commit message for the new branch in the commit message of the merge" do
@@ -366,7 +373,7 @@ describe GitReflow do
366
373
 
367
374
  context 'but there are still unaddressed comments' do
368
375
  let(:open_comment_authors) { ['nhance', 'codenamev'] }
369
- before { github.stub(:reviewers_pending_response).and_return(open_comment_authors) }
376
+ before { existing_pull_request.stub(:reviewers_pending_response).and_return(open_comment_authors) }
370
377
  it "notifies the user to get their code reviewed" do
371
378
  expect { subject }.to have_said "You still need a LGTM from: #{open_comment_authors.join(', ')}", :deliver_halted
372
379
  end
@@ -375,9 +382,9 @@ describe GitReflow do
375
382
 
376
383
  context 'but has no comments' do
377
384
  before do
378
- github.stub(:has_pull_request_comments?).and_return(false)
379
- github.stub(:approvals).and_return([])
380
- github.stub(:reviewers_pending_response).and_return([])
385
+ existing_pull_request.stub(:has_comments?).and_return(false)
386
+ existing_pull_request.stub(:approvals).and_return([])
387
+ existing_pull_request.stub(:reviewers_pending_response).and_return([])
381
388
  end
382
389
 
383
390
  it "notifies the user to get their code reviewed" do
@@ -6,8 +6,24 @@ describe GitReflow::Config do
6
6
  it { expect{ subject }.to have_run_command_silently 'git config --get chucknorris.roundhouse' }
7
7
 
8
8
  context "and getting all values" do
9
- subject { GitReflow::Config.get('chucknorris.roundhouse', all: true) }
10
- it { expect{ subject }.to have_run_command_silently 'git config --get-all chucknorris.roundhouse' }
9
+ subject { GitReflow::Config.get('chucknorris.roundhouse-kick', all: true) }
10
+ it { expect{ subject }.to have_run_command_silently 'git config --get-all chucknorris.roundhouse-kick' }
11
+
12
+ context "and checking locally only" do
13
+ subject { GitReflow::Config.get('chucknorris.jump', local: true) }
14
+ it { expect{ subject }.to have_run_command_silently 'git config --local --get chucknorris.jump' }
15
+ end
16
+ end
17
+
18
+ context "and checking for updates" do
19
+ before { GitReflow::Config.get('checknorris.roundhouse') }
20
+ subject { GitReflow::Config.get('chucknorris.roundhouse') }
21
+ it { expect{ subject }.to_not have_run_command_silently 'git config --get chucknorris.roundhouse-kick' }
22
+ end
23
+
24
+ context "and checking locally only" do
25
+ subject { GitReflow::Config.get('chucknorris.smash', local: true) }
26
+ it { expect{ subject }.to have_run_command_silently 'git config --local --get chucknorris.smash' }
11
27
  end
12
28
  end
13
29
 
@@ -23,20 +39,20 @@ describe GitReflow::Config do
23
39
 
24
40
  describe ".unset(key)" do
25
41
  subject { GitReflow::Config.unset('chucknorris.roundhouse') }
26
- it { expect{ subject }.to have_run_command_silently 'git config --global --unset chucknorris.roundhouse ' }
42
+ it { expect{ subject }.to have_run_command_silently 'git config --global --unset-all chucknorris.roundhouse ' }
27
43
 
28
44
  context "for multi-value keys" do
29
45
  subject { GitReflow::Config.unset('chucknorris.roundhouse', value: 'to the face') }
30
- it { expect{ subject }.to have_run_command_silently 'git config --global --unset chucknorris.roundhouse "to the face"' }
46
+ it { expect{ subject }.to have_run_command_silently 'git config --global --unset-all chucknorris.roundhouse "to the face"' }
31
47
  end
32
48
 
33
49
  context "for current project only" do
34
50
  subject { GitReflow::Config.unset('chucknorris.roundhouse', local: true) }
35
- it { expect{ subject }.to have_run_command_silently 'git config --unset chucknorris.roundhouse ' }
51
+ it { expect{ subject }.to have_run_command_silently 'git config --unset-all chucknorris.roundhouse ' }
36
52
 
37
53
  context "for multi-value keys" do
38
54
  subject { GitReflow::Config.unset('chucknorris.roundhouse', value: 'to the face', local: true) }
39
- it { expect{ subject }.to have_run_command_silently 'git config --unset chucknorris.roundhouse "to the face"' }
55
+ it { expect{ subject }.to have_run_command_silently 'git config --unset-all chucknorris.roundhouse "to the face"' }
40
56
  end
41
57
  end
42
58
  end
@@ -25,7 +25,7 @@ describe GitReflow::GitServer::BitBucket do
25
25
  subject { GitReflow::GitServer::BitBucket.new({}) }
26
26
 
27
27
  it 'sets the reflow git server provider to BitBucket in the git config' do
28
- GitReflow::Config.should_receive(:set).once.with('reflow.git-server', 'BitBucket')
28
+ GitReflow::Config.should_receive(:set).once.with('reflow.git-server', 'BitBucket', local: false)
29
29
  subject
30
30
  end
31
31
 
@@ -47,10 +47,11 @@ describe GitReflow::GitServer::BitBucket do
47
47
 
48
48
  context 'already authenticated' do
49
49
  it "notifies the user of successful setup" do
50
- GitReflow::Config.should_receive(:set).with('reflow.git-server', 'BitBucket')
50
+ allow(GitReflow::Config).to receive(:set).with('reflow.git-server', 'BitBucket', local: false)
51
51
  allow(GitReflow::Config).to receive(:get).with('remote.origin.url').and_return(remote_url)
52
- allow(GitReflow::Config).to receive(:get).with('bitbucket.user').and_return(user)
53
- allow(GitReflow::Config).to receive(:get).with('bitbucket.api-key', reload: true).and_return(api_key)
52
+ allow(GitReflow::Config).to receive(:get).with('bitbucket.user', local: false).and_return(user)
53
+ allow(GitReflow::Config).to receive(:get).with('bitbucket.api-key', reload: true, local: false).and_return(api_key)
54
+ allow(GitReflow::Config).to receive(:get).with('reflow.local-projects', all: true).and_return('')
54
55
  expect { subject }.to have_output "\nYour BitBucket account was already setup with:"
55
56
  expect { subject }.to have_output "\tUser Name: #{user}"
56
57
  end
@@ -77,34 +78,4 @@ describe GitReflow::GitServer::BitBucket do
77
78
  end
78
79
  end
79
80
 
80
- xdescribe '#create_pull_request(options)' do
81
- let(:title) { 'Fresh title' }
82
- let(:body) { 'Funky body' }
83
- let(:current_branch) { 'new-feature' }
84
-
85
- it 'creates a pull request using the remote user and repo' do
86
- end
87
- end
88
-
89
- xdescribe '#find_pull_request(from, to)' do
90
- end
91
-
92
- xdescribe '#pull_request_comments(pull_request)' do
93
- end
94
-
95
- xdescribe '#has_pull_request_comments?(pull_request)' do
96
- end
97
-
98
- xdescribe '#get_build_status(sha)' do
99
- end
100
-
101
- xdescribe '#find_authors_of_open_pull_request_comments(pull_request)' do
102
- end
103
-
104
- xdescribe '#comment_authors_for_pull_request(pull_request, options = {})' do
105
- end
106
-
107
- xdescribe '#get_commited_time(commit_sha)' do
108
- end
109
-
110
81
  end
@@ -0,0 +1,319 @@
1
+ require 'spec_helper'
2
+
3
+ describe GitReflow::GitServer::GitHub::PullRequest do
4
+ let(:user) { 'reenhanced' }
5
+ let(:password) { 'shazam' }
6
+ let(:repo) { 'repo' }
7
+ let(:oauth_token_hash) { Hashie::Mash.new({ token: 'a1b2c3d4e5f6g7h8i9j0'}) }
8
+ let(:hostname) { 'hostname.local' }
9
+ let(:github_site) { 'https://github.com' }
10
+ let(:github_api_endpoint) { 'https://api.github.com' }
11
+ let(:enterprise_site) { 'https://github.gittyup.com' }
12
+ let(:enterprise_api) { 'https://github.gittyup.com/api/v3' }
13
+ let(:github) { stub_github_with }
14
+ let!(:github_api) { github.connection }
15
+ let(:existing_pull_request) { Fixture.new('pull_requests/external_pull_request.json').to_json_hashie }
16
+ let(:existing_pull_requests) { Fixture.new('pull_requests/pull_requests.json').to_json_hashie }
17
+ let(:existing_pull_commits) { Fixture.new('pull_requests/commits.json').to_json_hashie }
18
+ let(:comment_author) { 'octocat' }
19
+ let(:existing_pull_comments) {
20
+ Fixture.new('pull_requests/comments.json.erb',
21
+ repo_owner: user,
22
+ repo_name: repo,
23
+ comments: [{author: comment_author}],
24
+ pull_request_number: existing_pull_request.number).to_json_hashie }
25
+ let(:existing_issue_comments) {
26
+ Fixture.new('issues/comments.json.erb',
27
+ repo_owner: user,
28
+ repo_name: repo,
29
+ comments: [{author: comment_author}],
30
+ pull_request_number: existing_pull_request.number).to_json_hashie }
31
+
32
+ subject { GitReflow::GitServer::GitHub::PullRequest.new(existing_pull_request) }
33
+
34
+
35
+ before do
36
+ stub_command_line_inputs({
37
+ "Please enter your GitHub username: " => user,
38
+ "Please enter your GitHub password (we do NOT store this): " => password,
39
+ "Please enter your Enterprise site URL (e.g. https://github.company.com):" => enterprise_site,
40
+ "Please enter your Enterprise API endpoint (e.g. https://github.company.com/api/v3):" => enterprise_api
41
+ })
42
+
43
+ github.class.stub(:remote_user).and_return(user)
44
+ github.class.stub(:remote_repo_name).and_return(repo)
45
+ end
46
+
47
+ describe '#initialize(options)' do
48
+ specify { expect(subject.number).to eql(existing_pull_request.number) }
49
+ specify { expect(subject.description).to eql(existing_pull_request.body) }
50
+ specify { expect(subject.html_url).to eql(existing_pull_request.html_url) }
51
+ specify { expect(subject.feature_branch_name).to eql(existing_pull_request.head.label) }
52
+ specify { expect(subject.base_branch_name).to eql(existing_pull_request.base.label) }
53
+ specify { expect(subject.build_status).to eql('success') }
54
+ specify { expect(subject.source_object).to eql(existing_pull_request) }
55
+ end
56
+
57
+ describe '#commit_author' do
58
+ before do
59
+ stub_request(:get, %r{#{GitReflow.git_server.class.api_endpoint}/repos/#{user}/#{repo}/pulls/#{existing_pull_request.number}/commits}).
60
+ with(query: {"access_token" => "a1b2c3d4e5f6g7h8i9j0"}).
61
+ to_return(:body => Fixture.new("pull_requests/commits.json").to_s, status: 201, headers: {content_type: "application/json; charset=utf-8"})
62
+ end
63
+ specify { expect(subject.commit_author).to eql("#{existing_pull_commits.first.commit.author.name} <#{existing_pull_commits.first.commit.author.email}>") }
64
+ end
65
+
66
+ describe '#comments' do
67
+ before do
68
+ FakeGitHub.new(
69
+ repo_owner: user,
70
+ repo_name: repo,
71
+ pull_request: {
72
+ number: existing_pull_request.number,
73
+ comments: [{author: comment_author}]
74
+ },
75
+ issue: {
76
+ number: existing_pull_request.number,
77
+ comments: [{author: comment_author}]
78
+ })
79
+ end
80
+
81
+ specify { expect(subject.comments).to eql(existing_pull_comments.to_a + existing_issue_comments.to_a) }
82
+ end
83
+
84
+ describe '#reviewers' do
85
+ before do
86
+ allow(existing_pull_request.user).to receive(:login).and_return('ringo')
87
+
88
+ FakeGitHub.new(
89
+ repo_owner: user,
90
+ repo_name: repo,
91
+ pull_request: {
92
+ number: existing_pull_request.number,
93
+ owner: existing_pull_request.user.login,
94
+ comments: [{author: 'tito'}, {author: 'bobby'}, {author: 'ringo'}]
95
+ },
96
+ issue: {
97
+ number: existing_pull_request.number,
98
+ comments: [{author: 'ringo'}, {author: 'randy'}]
99
+ })
100
+ end
101
+
102
+ specify { expect(subject.reviewers).to eql(['tito', 'bobby', 'randy']) }
103
+ end
104
+
105
+ describe '#approvals' do
106
+ context "no comments" do
107
+ before do
108
+ FakeGitHub.new(
109
+ repo_owner: user,
110
+ repo_name: repo,
111
+ pull_request: {
112
+ number: existing_pull_request.number,
113
+ owner: existing_pull_request.head.user.login,
114
+ comments: []
115
+ })
116
+ end
117
+
118
+ specify { expect(subject.approvals).to eq([]) }
119
+ end
120
+
121
+ context "single reviewer without approval" do
122
+ before do
123
+ FakeGitHub.new(
124
+ repo_owner: user,
125
+ repo_name: repo,
126
+ pull_request: {
127
+ number: existing_pull_request.number,
128
+ owner: existing_pull_request.head.user.login,
129
+ comments: [{author: 'tito', body: 'This is some funky stuff'}]
130
+ })
131
+ end
132
+
133
+ specify { expect(subject.approvals).to eq([]) }
134
+ end
135
+
136
+ context "single reviewer with approval" do
137
+ before do
138
+ FakeGitHub.new(
139
+ repo_owner: user,
140
+ repo_name: repo,
141
+ pull_request: {
142
+ number: existing_pull_request.number,
143
+ owner: existing_pull_request.head.user.login,
144
+ comments: [{author: 'tito', body: 'LGTM'}]
145
+ })
146
+ end
147
+
148
+ specify { expect(subject.approvals).to eq(['tito']) }
149
+
150
+ context "but a new commit has been introduced" do
151
+ before do
152
+ FakeGitHub.new(
153
+ repo_owner: user,
154
+ repo_name: repo,
155
+ commits: [
156
+ {
157
+ author: user,
158
+ pull_request_number: existing_pull_request.number,
159
+ created_at: Chronic.parse("1 second ago")
160
+ }
161
+ ],
162
+ pull_request: {
163
+ number: existing_pull_request.number,
164
+ owner: existing_pull_request.head.user.login,
165
+ comments: [{author: 'tito', body: 'LGTM', created_at: Chronic.parse('1 minute ago')}]
166
+ })
167
+ end
168
+
169
+ specify { expect(subject.approvals).to eq([]) }
170
+ end
171
+ end
172
+
173
+ context "multiple reviewers with only one approval" do
174
+ before do
175
+ FakeGitHub.new(
176
+ repo_owner: user,
177
+ repo_name: repo,
178
+ pull_request: {
179
+ number: existing_pull_request.number,
180
+ owner: existing_pull_request.head.user.login,
181
+ comments: [{author: 'tito', body: 'LGTM'}, {author: 'ringo', body: 'Needs more cowbell.'}]
182
+ })
183
+ end
184
+
185
+ specify { expect(subject.approvals).to eq(['tito']) }
186
+ end
187
+
188
+ context "multiple reviewers with all approvals" do
189
+ before do
190
+ FakeGitHub.new(
191
+ repo_owner: user,
192
+ repo_name: repo,
193
+ pull_request: {
194
+ number: existing_pull_request.number,
195
+ owner: existing_pull_request.head.user.login,
196
+ comments: [{author: 'tito', body: 'lgtm'}, {author: 'ringo', body: ':+1:'}]
197
+ })
198
+ end
199
+
200
+ specify { expect(subject.approvals).to eq(['tito', 'ringo']) }
201
+
202
+ context "but a new commit has been introduced" do
203
+ before do
204
+ FakeGitHub.new(
205
+ repo_owner: user,
206
+ repo_name: repo,
207
+ commits: [
208
+ {
209
+ author: user,
210
+ pull_request_number: existing_pull_request.number,
211
+ created_at: Chronic.parse("1 second ago")
212
+ }
213
+ ],
214
+ pull_request: {
215
+ number: existing_pull_request.number,
216
+ owner: existing_pull_request.head.user.login,
217
+ comments: [{author: 'tito', body: 'lgtm', created_at: Chronic.parse('1 minute ago')}, {author: 'ringo', body: ':+1:', created_at: Chronic.parse('1 minute ago')}]
218
+ })
219
+ end
220
+
221
+ specify { expect(subject.approvals).to eq([]) }
222
+ end
223
+ end
224
+
225
+ end
226
+
227
+ describe '#last_comment' do
228
+ before do
229
+ FakeGitHub.new(
230
+ repo_owner: user,
231
+ repo_name: repo,
232
+ pull_request: {
233
+ number: existing_pull_request.number,
234
+ owner: existing_pull_request.head.user.login,
235
+ comments: [{author: 'tito', body: 'lgtm'}, {author: 'ringo', body: 'Cha cha cha'}]
236
+ })
237
+ end
238
+
239
+ specify { expect(subject.last_comment).to eq('"Cha cha cha"') }
240
+ end
241
+
242
+ describe '#build' do
243
+ let(:build) { Fixture.new('repositories/statuses.json').to_json_hashie.first }
244
+
245
+ context "with an existing build" do
246
+ specify { expect(subject.build.state).to eq(build.state) }
247
+ specify { expect(subject.build.description).to eq(build.description) }
248
+ specify { expect(subject.build.url).to eq(build.target_url) }
249
+ end
250
+
251
+ context "no build found" do
252
+ before { allow(GitReflow.git_server).to receive(:get_build_status).and_return(nil) }
253
+ specify { expect(subject.build.state).to eq(nil) }
254
+ specify { expect(subject.build.description).to eq(nil) }
255
+ specify { expect(subject.build.url).to eq(nil) }
256
+ end
257
+ end
258
+
259
+
260
+ describe '.create(options)' do
261
+ let(:title) { 'Bone Saw is ready!' }
262
+ let(:body) { 'Snap into a Slim Jim!' }
263
+ let(:base_branch_name) { 'base-branch' }
264
+ let(:pull_request_response) do
265
+ Fixture.new('pull_requests/pull_request.json.erb',
266
+ number: 2,
267
+ title: title,
268
+ body: body,
269
+ base_branch: base_branch_name,
270
+ repo_owner: user,
271
+ repo_name: repo)
272
+ end
273
+
274
+ subject { GitReflow::GitServer::GitHub::PullRequest.create(title: title, body: body, base: base_branch_name) }
275
+
276
+ before do
277
+ stub_request(:post, %r{/repos/#{user}/#{repo}/pulls}).
278
+ to_return(body: pull_request_response.to_s, status: 201, headers: {content_type: "application/json; charset=utf-8"})
279
+ end
280
+
281
+ specify { expect(subject.class.to_s).to eql('GitReflow::GitServer::GitHub::PullRequest') }
282
+ specify { expect(subject.title).to eql(title) }
283
+ specify { expect(subject.description).to eql(body) }
284
+ specify { expect(subject.base_branch_name).to eql(base_branch_name) }
285
+ end
286
+
287
+ describe '.find_open(options)' do
288
+ let(:feature_branch) { 'new-feature' }
289
+ let(:base_branch) { 'base-branch' }
290
+
291
+ subject { GitReflow::GitServer::GitHub::PullRequest.find_open(from: feature_branch, to: base_branch) }
292
+
293
+ before do
294
+ allow(GitReflow.git_server.class).to receive(:current_branch).and_return(feature_branch)
295
+ FakeGitHub.new(
296
+ repo_owner: user,
297
+ repo_name: repo,
298
+ pull_request: {
299
+ number: existing_pull_request.number,
300
+ owner: existing_pull_request.head.user.login,
301
+ base_branch: base_branch,
302
+ feature_branch: feature_branch
303
+ })
304
+ end
305
+
306
+ specify { expect(subject.class.to_s).to eql('GitReflow::GitServer::GitHub::PullRequest') }
307
+ specify { expect(subject.number).to eql(existing_pull_request.number) }
308
+
309
+ context "without any options" do
310
+ let(:base_branch) { 'master' }
311
+ subject { GitReflow::GitServer::GitHub::PullRequest.find_open() }
312
+ it "defaults to the current branch as the feature branch and 'master' as the base branch" do
313
+ expect(subject.class.to_s).to eql('GitReflow::GitServer::GitHub::PullRequest')
314
+ expect(subject.number).to eql(existing_pull_request.number)
315
+ end
316
+ end
317
+ end
318
+
319
+ end