git_reflow 0.6.7 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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