git_reflow 0.8.6 → 0.8.7
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.
- 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
|