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.
- checksums.yaml +4 -4
- data/Gemfile.lock +11 -9
- data/README.rdoc +3 -1
- data/bin/git-reflow +0 -11
- data/bin/gitreflow-common +1 -1
- data/git_reflow.gemspec +3 -2
- data/lib/git_reflow.rb +13 -60
- data/lib/git_reflow/commands/deliver.rb +1 -2
- data/lib/git_reflow/commands/start.rb +0 -6
- data/lib/git_reflow/config.rb +15 -14
- data/lib/git_reflow/git_server.rb +14 -4
- data/lib/git_reflow/git_server/base.rb +0 -39
- data/lib/git_reflow/git_server/bit_bucket.rb +15 -80
- data/lib/git_reflow/git_server/bit_bucket/pull_request.rb +84 -0
- data/lib/git_reflow/git_server/git_hub.rb +18 -75
- data/lib/git_reflow/git_server/git_hub/pull_request.rb +108 -0
- data/lib/git_reflow/git_server/pull_request.rb +97 -0
- data/lib/git_reflow/version.rb +1 -1
- data/spec/fixtures/issues/comment.json.erb +27 -0
- data/spec/fixtures/issues/comments.json.erb +15 -0
- data/spec/fixtures/pull_requests/comment.json.erb +45 -0
- data/spec/fixtures/pull_requests/comments.json.erb +15 -0
- data/spec/fixtures/pull_requests/commits.json +29 -0
- data/spec/fixtures/pull_requests/external_pull_request.json +145 -0
- data/spec/fixtures/pull_requests/pull_request.json +19 -0
- data/spec/fixtures/pull_requests/pull_request.json.erb +142 -0
- data/spec/fixtures/pull_requests/pull_requests.json +19 -0
- data/spec/fixtures/repositories/commit.json.erb +53 -0
- data/spec/fixtures/repositories/commits.json.erb +13 -0
- data/spec/git_reflow_spec.rb +32 -25
- data/spec/lib/git_reflow/config_spec.rb +22 -6
- data/spec/lib/git_server/bit_bucket_spec.rb +5 -34
- data/spec/lib/git_server/git_hub/pull_request_spec.rb +319 -0
- data/spec/lib/git_server/git_hub_spec.rb +17 -25
- data/spec/lib/git_server/pull_request_spec.rb +93 -0
- data/spec/support/command_line_helpers.rb +16 -1
- data/spec/support/fake_github.rb +128 -0
- data/spec/support/fixtures.rb +52 -6
- data/spec/support/github_helpers.rb +22 -12
- 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
|
+
]
|
data/spec/git_reflow_spec.rb
CHANGED
@@ -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) {
|
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.
|
41
|
-
GitReflow.
|
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.
|
44
|
-
GitReflow.
|
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
|
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
|
-
|
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(
|
119
|
-
github.
|
120
|
-
|
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
|
-
|
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.
|
177
|
-
|
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
|
-
|
185
|
-
|
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
|
-
|
200
|
-
|
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
|
-
|
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
|
-
|
223
|
-
|
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
|
-
|
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 {
|
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
|
-
|
379
|
-
|
380
|
-
|
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.
|
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
|