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.
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