git_reflow 0.8.6 → 0.8.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +348 -348
- data/Gemfile.lock +13 -15
- data/LICENSE +20 -20
- data/README.rdoc +461 -461
- data/Rakefile +8 -8
- data/bin/console +7 -7
- data/bin/setup +6 -6
- data/circle.yml +5 -5
- data/exe/git-reflow +36 -36
- data/git_reflow.gemspec +1 -1
- data/lib/git_reflow/commands/deliver.rb +10 -10
- data/lib/git_reflow/commands/refresh.rb +20 -20
- data/lib/git_reflow/commands/review.rb +13 -13
- data/lib/git_reflow/commands/setup.rb +11 -11
- data/lib/git_reflow/commands/stage.rb +9 -9
- data/lib/git_reflow/commands/start.rb +22 -22
- data/lib/git_reflow/commands/status.rb +7 -7
- data/lib/git_reflow/config.rb +9 -9
- data/lib/git_reflow/git_server/base.rb +68 -68
- data/lib/git_reflow/git_server/bit_bucket/pull_request.rb +84 -84
- data/lib/git_reflow/git_server/bit_bucket.rb +101 -101
- data/lib/git_reflow/git_server/git_hub/pull_request.rb +4 -1
- data/lib/git_reflow/git_server/pull_request.rb +11 -2
- data/lib/git_reflow/git_server.rb +63 -63
- data/lib/git_reflow/logger.rb +49 -0
- data/lib/git_reflow/merge_error.rb +9 -9
- data/lib/git_reflow/os_detector.rb +23 -23
- data/lib/git_reflow/rspec/command_line_helpers.rb +12 -8
- data/lib/git_reflow/rspec/stub_helpers.rb +13 -13
- data/lib/git_reflow/rspec.rb +2 -2
- data/lib/git_reflow/sandbox.rb +11 -6
- data/lib/git_reflow/version.rb +1 -1
- data/lib/git_reflow/workflow.rb +59 -59
- data/lib/git_reflow/workflows/core.rb +238 -238
- data/lib/git_reflow/workflows/flat_merge.rb +10 -10
- data/lib/git_reflow.rb +11 -0
- data/spec/fixtures/awesome_workflow.rb +7 -0
- data/spec/fixtures/git/git_config +7 -0
- data/spec/fixtures/issues/comment.json.erb +27 -0
- data/spec/fixtures/issues/comments.json +29 -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 +47 -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 +142 -0
- data/spec/fixtures/pull_requests/pull_request.json.erb +142 -0
- data/spec/fixtures/pull_requests/pull_request_exists_error.json +32 -0
- data/spec/fixtures/pull_requests/pull_requests.json +136 -0
- data/spec/fixtures/repositories/commit.json +53 -0
- data/spec/fixtures/repositories/commit.json.erb +53 -0
- data/spec/fixtures/repositories/commits.json.erb +13 -0
- data/spec/fixtures/repositories/statuses.json +31 -0
- data/spec/fixtures/workflow_with_super.rb +8 -0
- data/spec/lib/git_reflow/config_spec.rb +74 -0
- data/spec/lib/git_reflow/git_helpers_spec.rb +182 -0
- data/spec/lib/git_reflow/git_server/bit_bucket_spec.rb +81 -0
- data/spec/lib/git_reflow/git_server/git_hub/pull_request_spec.rb +587 -0
- data/spec/lib/git_reflow/git_server/git_hub_spec.rb +221 -0
- data/spec/lib/git_reflow/git_server/pull_request_spec.rb +524 -0
- data/spec/lib/git_reflow/git_server_spec.rb +101 -0
- data/spec/lib/git_reflow/logger_spec.rb +18 -0
- data/spec/lib/git_reflow/sandbox_spec.rb +15 -0
- data/spec/lib/git_reflow/workflow_spec.rb +59 -0
- data/spec/lib/git_reflow/workflows/core_spec.rb +665 -0
- data/spec/lib/git_reflow/workflows/flat_merge_spec.rb +59 -0
- data/spec/lib/git_reflow_spec.rb +75 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/fake_github.rb +128 -0
- data/spec/support/fixtures.rb +54 -0
- data/spec/support/github_helpers.rb +109 -0
- data/spec/support/mock_pull_request.rb +17 -0
- data/spec/support/web_mocks.rb +39 -0
- metadata +83 -6
@@ -0,0 +1,524 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GitReflow::GitServer::PullRequest do
|
4
|
+
let(:pr_response) { Fixture.new('pull_requests/external_pull_request.json').to_json_hashie }
|
5
|
+
let(:pr) { MockPullRequest.new(pr_response) }
|
6
|
+
let(:github) { stub_github_with({ user: 'reenhanced', repo: 'repo', pull: pr_response }) }
|
7
|
+
let!(:github_api) { github.connection }
|
8
|
+
let(:git_server) { GitReflow::GitServer::GitHub.new {} }
|
9
|
+
let(:user) { 'reenhanced' }
|
10
|
+
let(:password) { 'shazam' }
|
11
|
+
let(:enterprise_site) { 'https://github.reenhanced.com' }
|
12
|
+
let(:enterprise_api) { 'https://github.reenhanced.com' }
|
13
|
+
|
14
|
+
describe ".minimum_approvals" do
|
15
|
+
subject { MockPullRequest.minimum_approvals }
|
16
|
+
before { allow(GitReflow::Config).to receive(:get).with('constants.minimumApprovals').and_return('2') }
|
17
|
+
it { should eql '2' }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe ".approval_regex" do
|
21
|
+
subject { MockPullRequest.approval_regex }
|
22
|
+
it { should eql MockPullRequest::DEFAULT_APPROVAL_REGEX }
|
23
|
+
|
24
|
+
context "with custom approval regex set" do
|
25
|
+
before { allow(GitReflow::Config).to receive(:get).with('constants.approvalRegex').and_return('/dude/') }
|
26
|
+
it { should eql Regexp.new('/dude/') }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
%w{commit_author comments last_comment reviewers approvals}.each do |method_name|
|
31
|
+
describe "##{method_name}" do
|
32
|
+
specify { expect{ pr.send(method_name.to_sym) }.to raise_error("MockPullRequest##{method_name} method must be implemented") }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#good_to_merge?(options)" do
|
37
|
+
subject { pr.good_to_merge? }
|
38
|
+
|
39
|
+
context "with no status" do
|
40
|
+
context "and approved" do
|
41
|
+
before { allow(pr).to receive(:approved?).and_return(true) }
|
42
|
+
it { should be_truthy }
|
43
|
+
end
|
44
|
+
|
45
|
+
context "but not approved" do
|
46
|
+
before { allow(pr).to receive(:approved?).and_return(false) }
|
47
|
+
it { should be_falsy }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "with build status" do
|
52
|
+
context "of 'success'" do
|
53
|
+
before { allow(pr).to receive(:build_status).and_return('success') }
|
54
|
+
|
55
|
+
context "and approved" do
|
56
|
+
before { allow(pr).to receive(:approved?).and_return(true) }
|
57
|
+
it { should be_truthy }
|
58
|
+
end
|
59
|
+
|
60
|
+
context "but not approved" do
|
61
|
+
before { allow(pr).to receive(:approved?).and_return(false) }
|
62
|
+
it { should be_falsy }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "NOT of 'success'" do
|
67
|
+
before { allow(pr).to receive(:build_status).and_return('failure') }
|
68
|
+
|
69
|
+
context "and approved" do
|
70
|
+
before { allow(pr).to receive(:approved?).and_return(true) }
|
71
|
+
it { should be_falsy }
|
72
|
+
end
|
73
|
+
|
74
|
+
context "and not approved" do
|
75
|
+
before { allow(pr).to receive(:approved?).and_return(false) }
|
76
|
+
it { should be_falsy }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "force merge?" do
|
82
|
+
subject { pr.good_to_merge?(force: true) }
|
83
|
+
|
84
|
+
context "with no successful build" do
|
85
|
+
before { allow(pr).to receive(:build_status).and_return(nil) }
|
86
|
+
it { should be_truthy }
|
87
|
+
end
|
88
|
+
|
89
|
+
context "with no approval" do
|
90
|
+
before { allow(pr).to receive(:approved?).and_return(false) }
|
91
|
+
it { should be_truthy }
|
92
|
+
end
|
93
|
+
|
94
|
+
context "with neither build success or approval" do
|
95
|
+
before do
|
96
|
+
allow(pr).to receive(:build_status).and_return(nil)
|
97
|
+
allow(pr).to receive(:approved?).and_return(false)
|
98
|
+
end
|
99
|
+
it { should be_truthy }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "#approved?" do
|
105
|
+
subject { pr.approved? }
|
106
|
+
|
107
|
+
context "no approvals required" do
|
108
|
+
before do
|
109
|
+
allow(MockPullRequest).to receive(:minimum_approvals).and_return('0')
|
110
|
+
allow(pr).to receive(:has_comments?).and_return(false)
|
111
|
+
allow(pr).to receive(:approvals).and_return([])
|
112
|
+
end
|
113
|
+
it { should be_truthy }
|
114
|
+
end
|
115
|
+
|
116
|
+
context "all commenters must approve" do
|
117
|
+
before { allow(MockPullRequest).to receive(:minimum_approvals).and_return(nil) }
|
118
|
+
|
119
|
+
context "and there are comments" do
|
120
|
+
before { allow(pr).to receive(:comments).and_return(['Cool.', 'This is nice.']) }
|
121
|
+
context "and there are no reviewers pending a resposne" do
|
122
|
+
before { allow(pr).to receive(:reviewers_pending_response).and_return([]) }
|
123
|
+
it { should be_truthy }
|
124
|
+
end
|
125
|
+
context "but there are reviewers pending a resposne" do
|
126
|
+
before { allow(pr).to receive(:reviewers_pending_response).and_return(['octocat']) }
|
127
|
+
it { should be_falsy }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "and there are no comments" do
|
132
|
+
before { allow(pr).to receive(:comments).and_return([]) }
|
133
|
+
context "and there are approvals" do
|
134
|
+
before { allow(pr).to receive(:approvals).and_return(['Sally']) }
|
135
|
+
context "and there are no reviewers pending a resposne" do
|
136
|
+
before { allow(pr).to receive(:reviewers_pending_response).and_return([]) }
|
137
|
+
it { should be_truthy }
|
138
|
+
end
|
139
|
+
context "but there are reviewers pending a resposne" do
|
140
|
+
before { allow(pr).to receive(:reviewers_pending_response).and_return(['octocat']) }
|
141
|
+
it { should be_falsy }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
context "and there are no approvals" do
|
145
|
+
before { allow(pr).to receive(:approvals).and_return([]) }
|
146
|
+
it { should be_falsy }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context "custom minimum approval set" do
|
152
|
+
before { allow(MockPullRequest).to receive(:minimum_approvals).and_return('2') }
|
153
|
+
|
154
|
+
context "with comments" do
|
155
|
+
before { allow(pr).to receive(:comments).and_return(['Sally']) }
|
156
|
+
context "and approvals is greater than minimum" do
|
157
|
+
before { allow(pr).to receive(:approvals).and_return(['Sally', 'Joey', 'Randy']) }
|
158
|
+
it { should be_truthy }
|
159
|
+
end
|
160
|
+
|
161
|
+
context "and approvals is equal to minimum" do
|
162
|
+
before { allow(pr).to receive(:approvals).and_return(['Sally', 'Joey']) }
|
163
|
+
it { should be_truthy }
|
164
|
+
end
|
165
|
+
|
166
|
+
context "but approvals is less than minimum" do
|
167
|
+
before { allow(pr).to receive(:approvals).and_return(['Sally']) }
|
168
|
+
it { should be_falsy }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "without_comments" do
|
173
|
+
before { allow(pr).to receive(:comments).and_return([]) }
|
174
|
+
context "and approvals is greater than minimum" do
|
175
|
+
before { allow(pr).to receive(:approvals).and_return(['Sally', 'Joey', 'Randy']) }
|
176
|
+
it { should be_truthy }
|
177
|
+
end
|
178
|
+
|
179
|
+
context "and approvals is equal to minimum" do
|
180
|
+
before { allow(pr).to receive(:approvals).and_return(['Sally', 'Joey']) }
|
181
|
+
it { should be_truthy }
|
182
|
+
end
|
183
|
+
|
184
|
+
context "but approvals is less than minimum" do
|
185
|
+
before { allow(pr).to receive(:approvals).and_return(['Sally']) }
|
186
|
+
it { should be_falsy }
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
describe "#rejection_message" do
|
195
|
+
subject { pr.rejection_message }
|
196
|
+
|
197
|
+
context "and the build is not successful" do
|
198
|
+
before do
|
199
|
+
allow(pr).to receive(:build_status).and_return('failure')
|
200
|
+
allow(pr).to receive(:build).and_return(MockPullRequest::Build.new(state: 'failure', description: 'no dice', url: 'https://example.com'))
|
201
|
+
allow(pr).to receive(:reviewers).and_return([])
|
202
|
+
allow(pr).to receive(:approvals).and_return([])
|
203
|
+
end
|
204
|
+
|
205
|
+
it { is_expected.to eql("no dice: https://example.com") }
|
206
|
+
|
207
|
+
context "but it is blank" do
|
208
|
+
before { allow(pr).to receive(:build_status).and_return(nil) }
|
209
|
+
it { is_expected.to_not eql("no dice: https://example.com") }
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context "and the build is successful" do
|
214
|
+
context "but approval minimums haven't been reached" do
|
215
|
+
before do
|
216
|
+
allow(pr).to receive(:approval_minimums_reached?).and_return(false)
|
217
|
+
allow(MockPullRequest).to receive(:minimum_approvals).and_return('3')
|
218
|
+
end
|
219
|
+
it { is_expected.to eql("You need approval from at least 3 users!") }
|
220
|
+
end
|
221
|
+
|
222
|
+
context "and approval minimums have been reached" do
|
223
|
+
before { allow(pr).to receive(:approval_minimums_reached?).and_return(true) }
|
224
|
+
|
225
|
+
context "but all comments haven't been addressed" do
|
226
|
+
before do
|
227
|
+
allow(pr).to receive(:all_comments_addressed?).and_return(false)
|
228
|
+
allow(pr).to receive(:last_comment).and_return("nope")
|
229
|
+
end
|
230
|
+
it { is_expected.to eql("The last comment is holding up approval:\nnope") }
|
231
|
+
end
|
232
|
+
|
233
|
+
context "and all comments have been addressed" do
|
234
|
+
before { allow(pr).to receive(:all_comments_addressed?).and_return(true) }
|
235
|
+
|
236
|
+
context "but there are still pending reviews" do
|
237
|
+
before { allow(pr).to receive(:reviewers_pending_response).and_return(['sally', 'bog']) }
|
238
|
+
it { is_expected.to eql("You still need a LGTM from: sally, bog") }
|
239
|
+
end
|
240
|
+
|
241
|
+
context "and there are no pending reviews" do
|
242
|
+
before { allow(pr).to receive(:reviewers_pending_response).and_return([]) }
|
243
|
+
it { is_expected.to eql("Your code has not been reviewed yet.") }
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
describe "#approval_minimums_reached?" do
|
251
|
+
subject { pr.approval_minimums_reached? }
|
252
|
+
|
253
|
+
before { allow(pr).to receive(:approvals).and_return(['sally', 'bog']) }
|
254
|
+
|
255
|
+
context "minimum approvals not set" do
|
256
|
+
before { allow(MockPullRequest).to receive(:minimum_approvals).and_return('') }
|
257
|
+
it { is_expected.to eql true }
|
258
|
+
end
|
259
|
+
|
260
|
+
context "minimum approvals not met" do
|
261
|
+
before { allow(MockPullRequest).to receive(:minimum_approvals).and_return('3') }
|
262
|
+
it { is_expected.to eql false }
|
263
|
+
end
|
264
|
+
|
265
|
+
context "minimum approvals met" do
|
266
|
+
before { allow(MockPullRequest).to receive(:minimum_approvals).and_return('2') }
|
267
|
+
it { is_expected.to eql true }
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
describe "#all_comments_addressed?" do
|
272
|
+
subject { pr.all_comments_addressed? }
|
273
|
+
before { allow(pr).to receive(:approvals).and_return(['sally', 'bog']) }
|
274
|
+
|
275
|
+
context "minimum approvals not set" do
|
276
|
+
before do
|
277
|
+
allow(MockPullRequest).to receive(:minimum_approvals).and_return('')
|
278
|
+
allow(pr).to receive(:last_comment).and_return('nope')
|
279
|
+
end
|
280
|
+
it { is_expected.to eql true }
|
281
|
+
end
|
282
|
+
|
283
|
+
context "minimum approvals set" do
|
284
|
+
before { allow(MockPullRequest).to receive(:minimum_approvals).and_return('2') }
|
285
|
+
context "last comment approved" do
|
286
|
+
before { allow(pr).to receive(:last_comment).and_return('lgtm') }
|
287
|
+
it { is_expected.to eql true }
|
288
|
+
end
|
289
|
+
|
290
|
+
context "last comment not approved" do
|
291
|
+
before { allow(pr).to receive(:last_comment).and_return('nope') }
|
292
|
+
it { is_expected.to eql false }
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe "#display_pull_request_summary" do
|
298
|
+
subject { pr.display_pull_request_summary }
|
299
|
+
|
300
|
+
before do
|
301
|
+
allow(pr).to receive(:reviewers).and_return([])
|
302
|
+
allow(pr).to receive(:approvals).and_return([])
|
303
|
+
allow(pr).to receive(:reviewers_pending_response).and_return([])
|
304
|
+
end
|
305
|
+
|
306
|
+
it "displays relevant information about the pull request" do
|
307
|
+
expect{ subject }.to have_output("branches: #{pr.feature_branch_name} -> #{pr.base_branch_name}")
|
308
|
+
expect{ subject }.to have_output("number: #{pr.number}")
|
309
|
+
expect{ subject }.to have_output("url: #{pr.html_url}")
|
310
|
+
expect{ subject }.to have_said("No one has reviewed your pull request.\n", :notice)
|
311
|
+
end
|
312
|
+
|
313
|
+
context "with build status" do
|
314
|
+
let(:build) { MockPullRequest::Build.new(state: "failure", description: "no dice", url: "https://example.com") }
|
315
|
+
before { allow(pr).to receive(:build).and_return(build) }
|
316
|
+
specify { expect{ subject }.to have_said("Your build status is not successful: #{build.url}.\n", :notice) }
|
317
|
+
context "and build status is 'success'" do
|
318
|
+
before { allow(build).to receive(:state).and_return('success') }
|
319
|
+
specify { expect{ subject }.to_not have_output("Your build status is not successful") }
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
context "with reviewers" do
|
324
|
+
before do
|
325
|
+
allow(pr).to receive(:reviewers).and_return(['tito', 'ringo'])
|
326
|
+
allow(pr).to receive(:last_comment).and_return('nope')
|
327
|
+
end
|
328
|
+
|
329
|
+
specify { expect{ subject }.to have_output("reviewed by: #{"tito".colorize(:red)}, #{"ringo".colorize(:red)}") }
|
330
|
+
specify { expect{ subject }.to have_output("Last comment: nope") }
|
331
|
+
|
332
|
+
context "and approvals" do
|
333
|
+
before { allow(pr).to receive(:approvals).and_return(['tito']) }
|
334
|
+
specify { expect{ subject }.to have_output 'tito'.colorize(:green) }
|
335
|
+
specify { expect{ subject }.to have_output 'ringo'.colorize(:red) }
|
336
|
+
end
|
337
|
+
|
338
|
+
context "and pending approvals" do
|
339
|
+
before { allow(pr).to receive(:reviewers_pending_response).and_return(['tito', 'ringo']) }
|
340
|
+
specify { expect{ subject }.to_not have_output "You still need a LGTM from: tito, ringo" }
|
341
|
+
end
|
342
|
+
|
343
|
+
context "and no pending approvals" do
|
344
|
+
before { allow(pr).to receive(:reviewers_pending_response).and_return([]) }
|
345
|
+
specify { expect{ subject }.to_not have_output "You still need a LGTM from" }
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
context "#merge!" do
|
351
|
+
let(:commit_message_for_merge) { "This changes everything!" }
|
352
|
+
|
353
|
+
let(:inputs) do
|
354
|
+
{
|
355
|
+
base: "base_branch",
|
356
|
+
title: "title",
|
357
|
+
message: "message"
|
358
|
+
}
|
359
|
+
end
|
360
|
+
|
361
|
+
let(:lgtm_comment_authors) { ["simonzhu24", "reenhanced"] }
|
362
|
+
let(:merge_response) { { message: "Failure_Message" } }
|
363
|
+
|
364
|
+
subject { pr.merge! inputs }
|
365
|
+
|
366
|
+
before do
|
367
|
+
allow(GitReflow).to receive(:append_to_squashed_commit_message)
|
368
|
+
allow(pr).to receive(:commit_message_for_merge).and_return(commit_message_for_merge)
|
369
|
+
end
|
370
|
+
|
371
|
+
context "and can deliver" do
|
372
|
+
before { allow(pr).to receive(:deliver?).and_return(true) }
|
373
|
+
|
374
|
+
specify { expect{ subject }.to have_said "Merging pull request ##{pr.number}: '#{pr.title}', from '#{pr.feature_branch_name}' into '#{pr.base_branch_name}'", :notice }
|
375
|
+
|
376
|
+
it "updates both feature and destination branch and squash-merges feature into base branch" do
|
377
|
+
expect(GitReflow).to receive(:update_current_branch)
|
378
|
+
expect(GitReflow).to receive(:fetch_destination).with(pr.base_branch_name)
|
379
|
+
expect(GitReflow).to receive(:append_to_squashed_commit_message).with(pr.commit_message_for_merge)
|
380
|
+
expect { subject }.to have_run_commands_in_order [
|
381
|
+
"git checkout #{pr.base_branch_name}",
|
382
|
+
"git pull origin #{pr.base_branch_name}",
|
383
|
+
"git merge --squash #{pr.feature_branch_name}"
|
384
|
+
]
|
385
|
+
end
|
386
|
+
|
387
|
+
context "and successfully commits merge" do
|
388
|
+
specify { expect{ subject }.to have_said "Pull request ##{pr.number} successfully merged.", :success }
|
389
|
+
|
390
|
+
context "and cleaning up feature branch" do
|
391
|
+
before { allow(pr).to receive(:cleanup_feature_branch?).and_return(true) }
|
392
|
+
specify { expect{ subject }.to have_said "Nice job buddy." }
|
393
|
+
it "pushes the base branch and removes the feature branch locally and remotely" do
|
394
|
+
expect { subject }.to have_run_commands_in_order [
|
395
|
+
"git push origin #{pr.base_branch_name}",
|
396
|
+
"git push origin :#{pr.feature_branch_name}",
|
397
|
+
"git branch -D #{pr.feature_branch_name}"
|
398
|
+
]
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
context "but not cleaning up feature branch" do
|
403
|
+
before { allow(pr).to receive(:cleanup_feature_branch?).and_return(false) }
|
404
|
+
specify { expect{ subject }.to_not have_run_command "git push origin #{pr.base_branch_name}" }
|
405
|
+
specify { expect{ subject }.to_not have_run_command "git push origin :#{pr.feature_branch_name}" }
|
406
|
+
specify { expect{ subject }.to_not have_run_command "git branch -D #{pr.feature_branch_name}" }
|
407
|
+
specify { expect{ subject }.to have_said "Cleanup halted. Local changes were not pushed to remote repo.", :deliver_halted }
|
408
|
+
specify { expect{ subject }.to have_said "To reset and go back to your branch run \`git reset --hard origin/#{pr.base_branch_name} && git checkout #{pr.feature_branch_name}\`" }
|
409
|
+
end
|
410
|
+
|
411
|
+
context "and NOT squash merging" do
|
412
|
+
let(:inputs) do
|
413
|
+
{
|
414
|
+
base: "base_branch",
|
415
|
+
title: "title",
|
416
|
+
message: "message",
|
417
|
+
merge_method: "merge"
|
418
|
+
}
|
419
|
+
end
|
420
|
+
|
421
|
+
specify { expect{ subject }.to have_run_command "git merge #{pr.feature_branch_name}" }
|
422
|
+
specify { expect{ subject }.to_not have_run_command "git merge --squash #{pr.feature_branch_name}" }
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
context "but has an issue commiting the merge" do
|
427
|
+
before do
|
428
|
+
allow(GitReflow).to receive(:run_command_with_label)
|
429
|
+
allow(GitReflow).to receive(:run_command_with_label).with('git commit', with_system: true).and_return(false)
|
430
|
+
end
|
431
|
+
specify { expect{ subject }.to have_said "There were problems commiting your feature... please check the errors above and try again.", :error }
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
context "but cannot deliver" do
|
436
|
+
before { allow(pr).to receive(:deliver?).and_return(false) }
|
437
|
+
specify { expect{ subject }.to have_said "Merge aborted", :deliver_halted }
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
context "#commit_message_for_merge" do
|
442
|
+
subject { pr.commit_message_for_merge }
|
443
|
+
|
444
|
+
let(:lgtm_comment_authors) { ["simonzhu24", "reenhanced"] }
|
445
|
+
let(:output) { lgtm_comment_authors.join(', @') }
|
446
|
+
let(:first_commit_message) { "Awesome commit here." }
|
447
|
+
|
448
|
+
before do
|
449
|
+
allow(GitReflow).to receive(:get_first_commit_message).and_return(first_commit_message)
|
450
|
+
allow(pr).to receive(:approvals).and_return []
|
451
|
+
end
|
452
|
+
|
453
|
+
specify { expect(subject).to include "\nMerges ##{pr.number}\n" }
|
454
|
+
specify { expect(subject).to_not include "\nLGTM given by: " }
|
455
|
+
|
456
|
+
context "with description" do
|
457
|
+
before { allow(pr).to receive(:description).and_return("Bingo.") }
|
458
|
+
specify { expect(subject).to include "Bingo." }
|
459
|
+
specify { expect(subject).to_not include first_commit_message }
|
460
|
+
end
|
461
|
+
|
462
|
+
context "with no description" do
|
463
|
+
before do
|
464
|
+
allow(pr).to receive(:description).and_return('')
|
465
|
+
end
|
466
|
+
specify { expect(subject).to include first_commit_message }
|
467
|
+
end
|
468
|
+
|
469
|
+
context "with approvals" do
|
470
|
+
before { allow(pr).to receive(:approvals).and_return(['sally', 'joey']) }
|
471
|
+
specify { expect(subject).to include "\nLGTM given by: @sally, @joey\n" }
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
context "#cleanup_feature_branch?" do
|
476
|
+
subject { pr.cleanup_feature_branch? }
|
477
|
+
|
478
|
+
context "and always cleanup config is set to 'true'" do
|
479
|
+
before { allow(GitReflow::Config).to receive(:get).with('reflow.always-cleanup').and_return('true') }
|
480
|
+
it { should be_truthy }
|
481
|
+
end
|
482
|
+
|
483
|
+
context "and always cleanup config is not set" do
|
484
|
+
before do
|
485
|
+
allow(GitReflow::Config).to receive(:get).with('reflow.always-cleanup').and_return('false')
|
486
|
+
end
|
487
|
+
|
488
|
+
context "and user chooses to cleanup" do
|
489
|
+
before { expect(pr).to receive(:ask).with('Would you like to push this branch to your remote repo and cleanup your feature branch? ').and_return('yes') }
|
490
|
+
it { should be_truthy }
|
491
|
+
end
|
492
|
+
|
493
|
+
context "and user choose not to cleanup" do
|
494
|
+
before { expect(pr).to receive(:ask).with('Would you like to push this branch to your remote repo and cleanup your feature branch? ').and_return('no') }
|
495
|
+
it { should be_falsy }
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
context "#deliver?" do
|
501
|
+
subject { pr.deliver? }
|
502
|
+
|
503
|
+
context "and always deliver config is set to 'true'" do
|
504
|
+
before { allow(GitReflow::Config).to receive(:get).with('reflow.always-deliver').and_return('true') }
|
505
|
+
it { should be_truthy }
|
506
|
+
end
|
507
|
+
|
508
|
+
context "and always deliver config is not set" do
|
509
|
+
before do
|
510
|
+
allow(GitReflow::Config).to receive(:get).with('reflow.always-deliver').and_return('false')
|
511
|
+
end
|
512
|
+
|
513
|
+
context "and user chooses to deliver" do
|
514
|
+
before { expect(pr).to receive(:ask).with('This is the current status of your Pull Request. Are you sure you want to deliver? ').and_return('yes') }
|
515
|
+
it { should be_truthy }
|
516
|
+
end
|
517
|
+
|
518
|
+
context "and user choose not to cleanup" do
|
519
|
+
before { expect(pr).to receive(:ask).with('This is the current status of your Pull Request. Are you sure you want to deliver? ').and_return('no') }
|
520
|
+
it { should be_falsy }
|
521
|
+
end
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GitReflow::GitServer do
|
4
|
+
let(:connection_options) { nil }
|
5
|
+
|
6
|
+
subject { GitReflow::GitServer.connect connection_options }
|
7
|
+
|
8
|
+
before do
|
9
|
+
allow(GitReflow::GitServer::GitHub).to receive(:new)
|
10
|
+
|
11
|
+
module GitReflow::GitServer
|
12
|
+
class DummyHub < Base
|
13
|
+
def initialize(options)
|
14
|
+
"Initialized with #{options}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def authenticate(options={})
|
18
|
+
end
|
19
|
+
|
20
|
+
def connection
|
21
|
+
'Connected!'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '.connect(options)' do
|
28
|
+
it 'initializes a new GitHub server provider by default' do
|
29
|
+
stubbed_github = Class.new
|
30
|
+
allow(stubbed_github).to receive(:authenticate)
|
31
|
+
expect(GitReflow::GitServer::GitHub).to receive(:new).and_return(stubbed_github)
|
32
|
+
subject
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'provider is specified' do
|
36
|
+
let(:connection_options) { {provider: 'DummyHub'}.merge(expected_server_options) }
|
37
|
+
let(:expected_server_options) {{ basic_auth: 'user:pass', end_point: 'https://api.example.com' }}
|
38
|
+
|
39
|
+
it 'initializes any server provider that has been implemented' do
|
40
|
+
dummy_hub = GitReflow::GitServer::DummyHub.new({})
|
41
|
+
expect(GitReflow::GitServer::DummyHub).to receive(:new).with(expected_server_options).and_return(dummy_hub)
|
42
|
+
expect(subject).to eq(dummy_hub)
|
43
|
+
expect($says).not_to include 'GitServer not setup for: DummyHub'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'provider not yet implemented' do
|
48
|
+
let(:connection_options) {{ provider: 'GitLab' }}
|
49
|
+
it { expect{ subject }.to have_said "Error connecting to GitLab: GitServer not setup for \"GitLab\"", :error }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '.current_provider' do
|
54
|
+
subject { GitReflow::GitServer.current_provider }
|
55
|
+
|
56
|
+
before { allow(GitReflow::Config).to receive(:get).with('reflow.git-server', local: true).and_return(nil) }
|
57
|
+
|
58
|
+
context 'Reflow setup to use GitHub' do
|
59
|
+
before { allow(GitReflow::Config).to receive(:get).with('reflow.git-server').and_return('GitHub') }
|
60
|
+
it { is_expected.to eq(GitReflow::GitServer::GitHub) }
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'Reflow has not yet been setup' do
|
64
|
+
before { allow(GitReflow::Config).to receive(:get).with('reflow.git-server').and_return('') }
|
65
|
+
it { is_expected.to be_nil }
|
66
|
+
it { expect{ subject }.to have_said "Reflow hasn't been setup yet. Run 'git reflow setup' to continue", :notice }
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'an unknown server provider is stored in the git config' do
|
70
|
+
before { allow(GitReflow::Config).to receive(:get).with('reflow.git-server').and_return('GittyUp') }
|
71
|
+
|
72
|
+
it { is_expected.to be_nil }
|
73
|
+
it { expect{ subject }.to have_said "GitServer not setup for \"GittyUp\"", :error }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '.connection' do
|
78
|
+
subject { GitReflow::GitServer.connection }
|
79
|
+
|
80
|
+
before do
|
81
|
+
allow(GitReflow::Config).to receive(:get).with('reflow.git-server', local: true).and_return(nil)
|
82
|
+
allow(GitReflow::Config).to receive(:get).with('reflow.git-server').and_return(nil)
|
83
|
+
end
|
84
|
+
|
85
|
+
it { is_expected.to be_nil }
|
86
|
+
|
87
|
+
context "with a valid provider" do
|
88
|
+
before { allow(GitReflow::Config).to receive(:get).with('reflow.git-server').and_return('GitHub') }
|
89
|
+
it 'calls connection on the provider' do
|
90
|
+
expect(GitReflow::GitServer::GitHub).to receive(:connection)
|
91
|
+
subject
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "with an invalid provider" do
|
96
|
+
before { allow(GitReflow::Config).to receive(:get).with('reflow.git-server').and_return('GittyUp') }
|
97
|
+
it { is_expected.to be_nil }
|
98
|
+
it { expect{ subject }.to have_said "GitServer not setup for \"GittyUp\"", :error }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GitReflow::Logger do
|
4
|
+
context "defaults" do
|
5
|
+
it "logs to '/tmp/git-reflow.log' by default" do
|
6
|
+
logger = described_class.new
|
7
|
+
expect(logger.instance_variable_get("@logdev").dev.path).to eq GitReflow::Logger::DEFAULT_LOG_FILE
|
8
|
+
end
|
9
|
+
|
10
|
+
context "when a log path is configured " do
|
11
|
+
it "initializes a new logger with the given path" do
|
12
|
+
allow(GitReflow::Config).to receive(:get).with("reflow.log_file_path").and_return("kenny-loggins.log")
|
13
|
+
logger = described_class.new
|
14
|
+
expect(logger.instance_variable_get("@logdev").dev.path).to eq "kenny-loggins.log"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe GitReflow::Sandbox do
|
4
|
+
describe ".run" do
|
5
|
+
it "is blocking by default when the command exits with a failure" do
|
6
|
+
allow(GitReflow::Sandbox).to receive(:run).and_call_original
|
7
|
+
expect { GitReflow::Sandbox.run("ls wtf") }.to raise_error SystemExit, "\`ls wtf\` failed to run."
|
8
|
+
end
|
9
|
+
|
10
|
+
it "when blocking is flagged off, the command exits silently" do
|
11
|
+
allow(GitReflow::Sandbox).to receive(:run).and_call_original
|
12
|
+
expect { GitReflow::Sandbox.run("ls wtf", blocking: false) }.to_not raise_error SystemExit
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|