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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +348 -348
  4. data/Gemfile.lock +13 -15
  5. data/LICENSE +20 -20
  6. data/README.rdoc +461 -461
  7. data/Rakefile +8 -8
  8. data/bin/console +7 -7
  9. data/bin/setup +6 -6
  10. data/circle.yml +5 -5
  11. data/exe/git-reflow +36 -36
  12. data/git_reflow.gemspec +1 -1
  13. data/lib/git_reflow/commands/deliver.rb +10 -10
  14. data/lib/git_reflow/commands/refresh.rb +20 -20
  15. data/lib/git_reflow/commands/review.rb +13 -13
  16. data/lib/git_reflow/commands/setup.rb +11 -11
  17. data/lib/git_reflow/commands/stage.rb +9 -9
  18. data/lib/git_reflow/commands/start.rb +22 -22
  19. data/lib/git_reflow/commands/status.rb +7 -7
  20. data/lib/git_reflow/config.rb +9 -9
  21. data/lib/git_reflow/git_server/base.rb +68 -68
  22. data/lib/git_reflow/git_server/bit_bucket/pull_request.rb +84 -84
  23. data/lib/git_reflow/git_server/bit_bucket.rb +101 -101
  24. data/lib/git_reflow/git_server/git_hub/pull_request.rb +4 -1
  25. data/lib/git_reflow/git_server/pull_request.rb +11 -2
  26. data/lib/git_reflow/git_server.rb +63 -63
  27. data/lib/git_reflow/logger.rb +49 -0
  28. data/lib/git_reflow/merge_error.rb +9 -9
  29. data/lib/git_reflow/os_detector.rb +23 -23
  30. data/lib/git_reflow/rspec/command_line_helpers.rb +12 -8
  31. data/lib/git_reflow/rspec/stub_helpers.rb +13 -13
  32. data/lib/git_reflow/rspec.rb +2 -2
  33. data/lib/git_reflow/sandbox.rb +11 -6
  34. data/lib/git_reflow/version.rb +1 -1
  35. data/lib/git_reflow/workflow.rb +59 -59
  36. data/lib/git_reflow/workflows/core.rb +238 -238
  37. data/lib/git_reflow/workflows/flat_merge.rb +10 -10
  38. data/lib/git_reflow.rb +11 -0
  39. data/spec/fixtures/awesome_workflow.rb +7 -0
  40. data/spec/fixtures/git/git_config +7 -0
  41. data/spec/fixtures/issues/comment.json.erb +27 -0
  42. data/spec/fixtures/issues/comments.json +29 -0
  43. data/spec/fixtures/issues/comments.json.erb +15 -0
  44. data/spec/fixtures/pull_requests/comment.json.erb +45 -0
  45. data/spec/fixtures/pull_requests/comments.json +47 -0
  46. data/spec/fixtures/pull_requests/comments.json.erb +15 -0
  47. data/spec/fixtures/pull_requests/commits.json +29 -0
  48. data/spec/fixtures/pull_requests/external_pull_request.json +145 -0
  49. data/spec/fixtures/pull_requests/pull_request.json +142 -0
  50. data/spec/fixtures/pull_requests/pull_request.json.erb +142 -0
  51. data/spec/fixtures/pull_requests/pull_request_exists_error.json +32 -0
  52. data/spec/fixtures/pull_requests/pull_requests.json +136 -0
  53. data/spec/fixtures/repositories/commit.json +53 -0
  54. data/spec/fixtures/repositories/commit.json.erb +53 -0
  55. data/spec/fixtures/repositories/commits.json.erb +13 -0
  56. data/spec/fixtures/repositories/statuses.json +31 -0
  57. data/spec/fixtures/workflow_with_super.rb +8 -0
  58. data/spec/lib/git_reflow/config_spec.rb +74 -0
  59. data/spec/lib/git_reflow/git_helpers_spec.rb +182 -0
  60. data/spec/lib/git_reflow/git_server/bit_bucket_spec.rb +81 -0
  61. data/spec/lib/git_reflow/git_server/git_hub/pull_request_spec.rb +587 -0
  62. data/spec/lib/git_reflow/git_server/git_hub_spec.rb +221 -0
  63. data/spec/lib/git_reflow/git_server/pull_request_spec.rb +524 -0
  64. data/spec/lib/git_reflow/git_server_spec.rb +101 -0
  65. data/spec/lib/git_reflow/logger_spec.rb +18 -0
  66. data/spec/lib/git_reflow/sandbox_spec.rb +15 -0
  67. data/spec/lib/git_reflow/workflow_spec.rb +59 -0
  68. data/spec/lib/git_reflow/workflows/core_spec.rb +665 -0
  69. data/spec/lib/git_reflow/workflows/flat_merge_spec.rb +59 -0
  70. data/spec/lib/git_reflow_spec.rb +75 -0
  71. data/spec/spec_helper.rb +38 -0
  72. data/spec/support/fake_github.rb +128 -0
  73. data/spec/support/fixtures.rb +54 -0
  74. data/spec/support/github_helpers.rb +109 -0
  75. data/spec/support/mock_pull_request.rb +17 -0
  76. data/spec/support/web_mocks.rb +39 -0
  77. 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