git_reflow 0.3.5 → 0.4.0
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 +7 -0
- data/.gitignore +2 -0
- data/Gemfile.lock +67 -35
- data/Rakefile +8 -0
- data/git_reflow.gemspec +5 -4
- data/lib/git_reflow/commands/setup.rb +8 -1
- data/lib/git_reflow/commands/start.rb +2 -2
- data/lib/git_reflow/config.rb +23 -0
- data/lib/git_reflow/git_helpers.rb +65 -0
- data/lib/git_reflow/git_server/base.rb +88 -0
- data/lib/git_reflow/git_server/git_hub.rb +184 -0
- data/lib/git_reflow/git_server.rb +52 -0
- data/lib/git_reflow/sandbox.rb +39 -0
- data/lib/git_reflow/version.rb +1 -1
- data/lib/git_reflow.rb +48 -273
- data/spec/fixtures/git/git_config +7 -0
- data/spec/fixtures/issues/comments.json +29 -0
- data/spec/fixtures/pull_requests/comments.json +47 -0
- data/spec/fixtures/pull_requests/pull_request.json +123 -0
- data/spec/fixtures/pull_requests/pull_request_exists_error.json +32 -0
- data/spec/fixtures/pull_requests/pull_requests.json +117 -0
- data/spec/fixtures/repositories/statuses.json +31 -0
- data/spec/git_reflow_spec.rb +386 -0
- data/spec/lib/git_reflow/config_spec.rb +18 -0
- data/spec/lib/git_reflow/git_helpers_spec.rb +146 -0
- data/spec/lib/git_reflow/git_server_spec.rb +94 -0
- data/spec/lib/git_server/git_hub_spec.rb +236 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/support/command_line_helpers.rb +99 -0
- data/spec/support/fixtures.rb +8 -0
- data/spec/support/github_helpers.rb +98 -0
- data/spec/support/rspec_stub_helpers.rb +7 -0
- data/spec/support/web_mocks.rb +39 -0
- metadata +104 -62
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GitReflow::GitHelpers do
|
4
|
+
let(:origin_url) { 'git@github.com:reenhanced.spectacular/this-is-the.shit.git' }
|
5
|
+
|
6
|
+
before do
|
7
|
+
stub_with_fallback(GitReflow::Config, :get).with('remote.origin.url').and_return(origin_url)
|
8
|
+
|
9
|
+
module Gitacular
|
10
|
+
include GitReflow::GitHelpers
|
11
|
+
extend self
|
12
|
+
end
|
13
|
+
|
14
|
+
stub_run_for Gitacular
|
15
|
+
end
|
16
|
+
|
17
|
+
describe ".remote_user" do
|
18
|
+
subject { Gitacular.remote_user }
|
19
|
+
|
20
|
+
it { should == 'reenhanced.spectacular' }
|
21
|
+
|
22
|
+
context "remote origin url isn't set" do
|
23
|
+
let(:origin_url) { nil }
|
24
|
+
it { should == '' }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe ".remote_repo_name" do
|
29
|
+
subject { Gitacular.remote_repo_name }
|
30
|
+
|
31
|
+
it { should == 'this-is-the.shit' }
|
32
|
+
|
33
|
+
context "remote origin url isn't set" do
|
34
|
+
let(:origin_url) { nil }
|
35
|
+
it { should == '' }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe ".current_branch" do
|
40
|
+
subject { Gitacular.current_branch }
|
41
|
+
it { expect{ subject }.to have_run_command_silently "git branch --no-color | grep '^\* ' | grep -v 'no branch' | sed 's/^* //g'" }
|
42
|
+
end
|
43
|
+
|
44
|
+
describe ".get_first_commit_message" do
|
45
|
+
subject { Gitacular.get_first_commit_message }
|
46
|
+
it { expect{ subject }.to have_run_command_silently 'git log --pretty=format:"%s" --no-merges -n 1' }
|
47
|
+
end
|
48
|
+
|
49
|
+
describe ".push_current_branch" do
|
50
|
+
subject { Gitacular.push_current_branch }
|
51
|
+
before { Gitacular.stub(:current_branch).and_return('bingo') }
|
52
|
+
it { expect{ subject }.to have_run_command "git push origin bingo" }
|
53
|
+
end
|
54
|
+
|
55
|
+
describe ".fetch_destination(destination_branch)" do
|
56
|
+
subject { Gitacular.fetch_destination('new-feature') }
|
57
|
+
it { expect{ subject }.to have_run_command "git fetch origin new-feature" }
|
58
|
+
end
|
59
|
+
|
60
|
+
describe ".update_destination(destination_branch)" do
|
61
|
+
let(:current_branch) { 'bananas' }
|
62
|
+
let(:destination_branch) { 'monkey-business' }
|
63
|
+
|
64
|
+
before { Gitacular.stub(:current_branch).and_return(current_branch) }
|
65
|
+
subject { Gitacular.update_destination(destination_branch) }
|
66
|
+
|
67
|
+
it "updates the destination branch with the latest code from the remote repo" do
|
68
|
+
expect { subject }.to have_run_commands_in_order [
|
69
|
+
"git checkout #{destination_branch}",
|
70
|
+
"git pull origin #{destination_branch}",
|
71
|
+
"git checkout #{current_branch}"
|
72
|
+
]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe ".merge_feature_branch(options)" do
|
77
|
+
let(:destination_branch) { 'monkey-business' }
|
78
|
+
let(:feature_branch) { 'bananas' }
|
79
|
+
let(:merge_options) { {} }
|
80
|
+
|
81
|
+
subject { Gitacular.merge_feature_branch(feature_branch, merge_options) }
|
82
|
+
|
83
|
+
it 'checks out master as the default destination branch and squash merges the feature branch' do
|
84
|
+
expect { subject }.to have_run_commands_in_order [
|
85
|
+
'git checkout master',
|
86
|
+
"git merge --squash #{feature_branch}"
|
87
|
+
]
|
88
|
+
end
|
89
|
+
|
90
|
+
context "providing a destination branch" do
|
91
|
+
let(:merge_options) {{ destination_branch: destination_branch }}
|
92
|
+
it { expect{ subject }.to have_run_command "git checkout #{destination_branch}" }
|
93
|
+
end
|
94
|
+
|
95
|
+
context "with a message" do
|
96
|
+
let(:merge_options) {{ message: "don't throw doo doo" }}
|
97
|
+
it "appends the message to the squashed commit message" do
|
98
|
+
Gitacular.should_receive(:append_to_squashed_commit_message).with("don't throw doo doo")
|
99
|
+
subject
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'and a pull reuqest number' do
|
103
|
+
before { merge_options.merge!(pull_request_number: 3) }
|
104
|
+
it "appends the message to the squashed commit message" do
|
105
|
+
Gitacular.should_receive(:append_to_squashed_commit_message).with("don't throw doo doo\nCloses #3\n")
|
106
|
+
subject
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "with a pull request number" do
|
112
|
+
let(:merge_options) {{ pull_request_number: 3 }}
|
113
|
+
it "appends the message to the squashed commit message" do
|
114
|
+
Gitacular.should_receive(:append_to_squashed_commit_message).with("\nCloses #3\n")
|
115
|
+
subject
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context "with one LGTM author" do
|
120
|
+
let(:merge_options) {{ lgtm_authors: 'codenamev' }}
|
121
|
+
it "appends the message to the squashed commit message" do
|
122
|
+
Gitacular.should_receive(:append_to_squashed_commit_message).with("\nLGTM given by: @#{merge_options[:lgtm_authors]}\n")
|
123
|
+
subject
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "with LGTM authors" do
|
128
|
+
let(:merge_options) {{ lgtm_authors: ['codenamev', 'nhance'] }}
|
129
|
+
it "appends the message to the squashed commit message" do
|
130
|
+
Gitacular.should_receive(:append_to_squashed_commit_message).with("\nLGTM given by: @#{merge_options[:lgtm_authors].join(', @')}\n")
|
131
|
+
subject
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe ".append_to_squashed_commit_message(message)" do
|
137
|
+
let(:message) { "do do the voodoo that you do" }
|
138
|
+
subject { Gitacular.append_to_squashed_commit_message(message) }
|
139
|
+
it "appends the message to git's SQUASH_MSG temp file" do
|
140
|
+
expect{ subject }.to have_run_commands_in_order [
|
141
|
+
"echo \"#{message}\" | cat - .git/SQUASH_MSG > ./tmp_squash_msg",
|
142
|
+
'mv ./tmp_squash_msg .git/SQUASH_MSG'
|
143
|
+
]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,94 @@
|
|
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
|
+
GitReflow::GitServer::GitHub.stub(: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
|
+
stubbed_github.stub(:authenticate)
|
31
|
+
GitReflow::GitServer::GitHub.should_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
|
+
GitReflow::GitServer::DummyHub.should_receive(:new).with(expected_server_options).and_return(dummy_hub)
|
42
|
+
subject.should == dummy_hub
|
43
|
+
$output.should_not 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_output "Error connecting to GitLab: GitServer not setup for \"GitLab\"" }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '.current_provider' do
|
54
|
+
subject { GitReflow::GitServer.current_provider }
|
55
|
+
|
56
|
+
context 'Reflow setup to use GitHub' do
|
57
|
+
before { GitReflow::Config.stub(:get).with('reflow.git-server').and_return('GitHub') }
|
58
|
+
it { should == GitReflow::GitServer::GitHub }
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'Reflow has not yet been setup' do
|
62
|
+
before { GitReflow::Config.stub(:get).with('reflow.git-server').and_return('') }
|
63
|
+
it { should be_nil }
|
64
|
+
it { expect{ subject }.to have_output "[notice] Reflow hasn't been setup yet. Run 'git reflow setup' to continue" }
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'an unknown server provider is stored in the git config' do
|
68
|
+
before { GitReflow::Config.stub(:get).with('reflow.git-server').and_return('GittyUp') }
|
69
|
+
|
70
|
+
it { should be_nil }
|
71
|
+
it { expect{ subject }.to have_output "GitServer not setup for \"GittyUp\"" }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '.connection' do
|
76
|
+
subject { GitReflow::GitServer.connection }
|
77
|
+
|
78
|
+
it { should be_nil }
|
79
|
+
|
80
|
+
context "with a valid provider" do
|
81
|
+
before { GitReflow::Config.stub(:get).with('reflow.git-server').and_return('GitHub') }
|
82
|
+
it 'calls connection on the provider' do
|
83
|
+
GitReflow::GitServer::GitHub.should_receive(:connection)
|
84
|
+
subject
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "with an invalid provider" do
|
89
|
+
before { GitReflow::Config.stub(:get).with('reflow.git-server').and_return('GittyUp') }
|
90
|
+
it { should be_nil }
|
91
|
+
it { expect{ subject }.to have_output "GitServer not setup for \"GittyUp\"" }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GitReflow::GitServer::GitHub do
|
4
|
+
let(:user) { 'reenhanced' }
|
5
|
+
let(:password) { 'shazam' }
|
6
|
+
let(:repo) { 'repo' }
|
7
|
+
let(:oauth_token_hash) { Hashie::Mash.new({ token: 'a1b2c3d4e5f6g7h8i9j0'}) }
|
8
|
+
let(:hostname) { 'hostname.local' }
|
9
|
+
let(:github_site) { 'https://github.com' }
|
10
|
+
let(:github_api_endpoint) { 'https://api.github.com' }
|
11
|
+
let(:enterprise_site) { 'https://github.gittyup.com' }
|
12
|
+
let(:enterprise_api) { 'https://github.gittyup.com/api/v3' }
|
13
|
+
let(:github) { stub_github_with(pull: existing_pull_request) }
|
14
|
+
let!(:github_api) { github.connection }
|
15
|
+
let(:existing_pull_request) { Hashie::Mash.new JSON.parse(fixture('pull_requests/pull_request.json').read) }
|
16
|
+
let(:existing_pull_requests) { JSON.parse(fixture('pull_requests/pull_requests.json').read).collect {|pull| Hashie::Mash.new pull } }
|
17
|
+
|
18
|
+
before do
|
19
|
+
HighLine.any_instance.stub(:ask) do |terminal, question|
|
20
|
+
values = {
|
21
|
+
"Please enter your GitHub username: " => user,
|
22
|
+
"Please enter your GitHub password (we do NOT store this): " => password,
|
23
|
+
"Please enter your Enterprise site URL (e.g. https://github.company.com):" => enterprise_site,
|
24
|
+
"Please enter your Enterprise API endpoint (e.g. https://github.company.com/api/v3):" => enterprise_api
|
25
|
+
}
|
26
|
+
return_value = values[question]
|
27
|
+
question = ""
|
28
|
+
return_value
|
29
|
+
end
|
30
|
+
|
31
|
+
github.stub(:remote_user).and_return(user)
|
32
|
+
github.stub(:remote_repo_name).and_return(repo)
|
33
|
+
github.stub(:run).with('hostname', loud: false).and_return(hostname)
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#initialize(options)' do
|
37
|
+
subject { GitReflow::GitServer::GitHub.new({}) }
|
38
|
+
|
39
|
+
it 'sets the reflow git server provider to GitHub in the git config' do
|
40
|
+
GitReflow::Config.should_receive(:set).once.with('github.site', github_site, local: false)
|
41
|
+
GitReflow::Config.should_receive(:set).once.with('github.endpoint', github_api_endpoint, local: false)
|
42
|
+
GitReflow::Config.should_receive(:set).once.with('reflow.git-server', 'GitHub')
|
43
|
+
subject
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'using enterprise' do
|
47
|
+
subject { GitReflow::GitServer::GitHub.new(enterprise: true) }
|
48
|
+
|
49
|
+
it 'sets the enterprise site and api as the site and api endpoints for the GitHub provider in the git config' do
|
50
|
+
GitReflow::Config.should_receive(:set).once.with('github.site', enterprise_site, local: false)
|
51
|
+
GitReflow::Config.should_receive(:set).once.with('github.endpoint', enterprise_api, local: false)
|
52
|
+
GitReflow::Config.should_receive(:set).once.with('reflow.git-server', 'GitHub')
|
53
|
+
subject
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'storing git config settings only for this project' do
|
59
|
+
subject { GitReflow::GitServer::GitHub.new(project_only: true) }
|
60
|
+
|
61
|
+
it 'sets the enterprise site and api as the site and api endpoints for the GitHub provider in the git config' do
|
62
|
+
GitReflow::Config.should_receive(:set).once.with('github.site', github_site, local: true).and_call_original
|
63
|
+
GitReflow::Config.should_receive(:set).once.with('github.endpoint', github_api_endpoint, local: true)
|
64
|
+
GitReflow::Config.should_receive(:set).once.with('reflow.git-server', 'GitHub', local: true)
|
65
|
+
subject
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#authenticate' do
|
72
|
+
let(:github) { GitReflow::GitServer::GitHub.new({}) }
|
73
|
+
let!(:github_api) { Github.new }
|
74
|
+
let(:github_authorizations) { Github::Client::Authorizations.new }
|
75
|
+
subject { github.authenticate }
|
76
|
+
|
77
|
+
before do
|
78
|
+
GitReflow::GitServer::GitHub.stub(:user).and_return('reenhanced')
|
79
|
+
github_api.stub(:oauth).and_return(github_authorizations)
|
80
|
+
github_api.stub_chain(:oauth, :all).and_return([])
|
81
|
+
github.stub(:run).with('hostname', loud: false).and_return(hostname)
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'not yet authenticated' do
|
85
|
+
context 'with valid GitHub credentials' do
|
86
|
+
|
87
|
+
before do
|
88
|
+
Github.stub(:new).and_return(github_api)
|
89
|
+
github_authorizations.stub(:authenticated?).and_return(true)
|
90
|
+
github_api.oauth.stub(:create).with({ scopes: ['repo'], note: "git-reflow (#{hostname})" }).and_return(oauth_token_hash)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "notifies the user of successful setup" do
|
94
|
+
expect { subject }.to have_output "\nYour GitHub account was successfully setup!"
|
95
|
+
end
|
96
|
+
|
97
|
+
it "creates a new GitHub oauth token" do
|
98
|
+
github_api.oauth.should_receive(:create).and_return(oauth_token_hash)
|
99
|
+
subject
|
100
|
+
end
|
101
|
+
|
102
|
+
it "creates git config keys for github connections" do
|
103
|
+
expect{ subject }.to have_run_command_silently "git config --global --replace-all github.site \"#{GitReflow::GitServer::GitHub.site_url}\""
|
104
|
+
expect{ subject }.to have_run_command_silently "git config --global --replace-all github.endpoint \"#{GitReflow::GitServer::GitHub.api_endpoint}\""
|
105
|
+
expect{ subject }.to have_run_command_silently "git config --global --replace-all github.oauth-token \"#{oauth_token_hash[:token]}\""
|
106
|
+
expect{ subject }.to have_run_command_silently "git config --global --replace-all reflow.git-server \"GitHub\""
|
107
|
+
end
|
108
|
+
|
109
|
+
context "exclusive to project" do
|
110
|
+
let(:github) { GitReflow::GitServer::GitHub.new(project_only: true) }
|
111
|
+
before { GitReflow::GitServer::GitHub.stub(:@project_only).and_return(true) }
|
112
|
+
|
113
|
+
it "creates _local_ git config keys for github connections" do
|
114
|
+
expect{ subject }.to_not have_run_command_silently "git config --global --replace-all github.site \"#{GitReflow::GitServer::GitHub.site_url}\""
|
115
|
+
expect{ subject }.to_not have_run_command_silently "git config --global --replace-all github.endpoint \"#{GitReflow::GitServer::GitHub.api_endpoint}\""
|
116
|
+
expect{ subject }.to_not have_run_command_silently "git config --global --replace-all github.oauth-token \"#{oauth_token_hash[:token]}\""
|
117
|
+
expect{ subject }.to_not have_run_command_silently "git config --global --replace-all reflow.git-server \"GitHub\""
|
118
|
+
|
119
|
+
expect{ subject }.to have_run_command_silently "git config --replace-all github.site \"#{GitReflow::GitServer::GitHub.site_url}\""
|
120
|
+
expect{ subject }.to have_run_command_silently "git config --replace-all github.endpoint \"#{GitReflow::GitServer::GitHub.api_endpoint}\""
|
121
|
+
expect{ subject }.to have_run_command_silently "git config --replace-all github.oauth-token \"#{oauth_token_hash[:token]}\""
|
122
|
+
expect{ subject }.to have_run_command_silently "git config --replace-all reflow.git-server \"GitHub\""
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "use GitHub enterprise account" do
|
127
|
+
let(:github) { GitReflow::GitServer::GitHub.new(enterprise: true) }
|
128
|
+
before { GitReflow::GitServer::GitHub.stub(:@using_enterprise).and_return(true) }
|
129
|
+
it "creates git config keys for github connections" do
|
130
|
+
expect{ subject }.to have_run_command_silently "git config --global --replace-all github.site \"#{enterprise_site}\""
|
131
|
+
expect{ subject }.to have_run_command_silently "git config --global --replace-all github.endpoint \"#{enterprise_api}\""
|
132
|
+
expect{ subject }.to have_run_command_silently "git config --global --replace-all github.oauth-token \"#{oauth_token_hash[:token]}\""
|
133
|
+
expect{ subject }.to have_run_command_silently "git config --global --replace-all reflow.git-server \"GitHub\""
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "with invalid GitHub credentials" do
|
139
|
+
let(:unauthorized_error_response) {{
|
140
|
+
response_headers: {'content-type' => 'application/json; charset=utf-8', status: 'Unauthorized'},
|
141
|
+
method: 'GET',
|
142
|
+
status: '401',
|
143
|
+
body: { error: "GET https://api.github.com/authorizations: 401 Bad credentials" }
|
144
|
+
}}
|
145
|
+
|
146
|
+
before do
|
147
|
+
Github.should_receive(:new).and_raise Github::Error::Unauthorized.new(unauthorized_error_response)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "notifies user of invalid login details" do
|
151
|
+
expect { subject }.to have_output "\nInvalid username or password: #{Github::Error::Unauthorized.new(unauthorized_error_response).inspect}"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe '#create_pull_request(options)' do
|
158
|
+
let(:title) { 'Fresh title' }
|
159
|
+
let(:body) { 'Funky body' }
|
160
|
+
let(:current_branch) { 'new-feature' }
|
161
|
+
|
162
|
+
before { github.stub(:current_branch).and_return(current_branch) }
|
163
|
+
|
164
|
+
it 'creates a pull request using the remote user and repo' do
|
165
|
+
github_api.stub(:pull_requests)
|
166
|
+
github_api.pull_requests.should_receive(:create).with(user, repo, title: title, body: body, head: "#{user}:#{current_branch}", base: 'master')
|
167
|
+
github.create_pull_request({ title: title, body: body, base: 'master' })
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe '#find_pull_request(from, to)' do
|
172
|
+
subject { github.find_pull_request({ from: 'new-feature', to: 'master'}) }
|
173
|
+
|
174
|
+
it 'looks for an open pull request matching the remote user/repo' do
|
175
|
+
subject.should == existing_pull_requests.first
|
176
|
+
end
|
177
|
+
|
178
|
+
context 'no pull request exists' do
|
179
|
+
before { github.stub(:find_pull_request).and_return([]) }
|
180
|
+
it { should == [] }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe '#pull_request_comments(pull_request)' do
|
185
|
+
let(:pull_request_comments) { JSON.parse(fixture('pull_requests/comments.json').read).collect {|c| Hashie::Mash.new(c) } }
|
186
|
+
|
187
|
+
subject { github.pull_request_comments(existing_pull_request) }
|
188
|
+
|
189
|
+
before do
|
190
|
+
github_api.stub_chain(:issues, :comments)
|
191
|
+
github_api.stub_chain(:pull_requests, :comments)
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'includes both issue comments and pull request comments' do
|
195
|
+
github_api.issues.comments.should_receive(:all).with(user, repo, number: existing_pull_request.number).and_return([pull_request_comments.first])
|
196
|
+
github_api.pull_requests.comments.should_receive(:all).with(user, repo, number: existing_pull_request.number).and_return([pull_request_comments.first])
|
197
|
+
subject.count.should == 2
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe '#has_pull_request_comments?(pull_request)' do
|
202
|
+
let(:existing_pull_request) { Hashie::Mash.new JSON.parse(fixture('pull_requests/pull_request.json').read) }
|
203
|
+
let(:pull_request_comments) { JSON.parse(fixture('pull_requests/comments.json').read).collect {|c| Hashie::Mash.new(c) } }
|
204
|
+
|
205
|
+
before { github.stub(:pull_request_comments).and_return([pull_request_comments]) }
|
206
|
+
subject { github.has_pull_request_comments?(existing_pull_request) }
|
207
|
+
|
208
|
+
it { should == true }
|
209
|
+
|
210
|
+
context 'no comments exist for the given pull request' do
|
211
|
+
before { github.stub(:pull_request_comments).and_return([]) }
|
212
|
+
it { should == false }
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe '#get_build_status(sha)' do
|
217
|
+
let(:sha) { '6dcb09b5b57875f334f61aebed695e2e4193db5e' }
|
218
|
+
subject { github.get_build_status(sha) }
|
219
|
+
before { github_api.stub_chain(:repos, :statuses) }
|
220
|
+
|
221
|
+
it 'gets the latest build status for the given commit hash' do
|
222
|
+
github_api.repos.statuses.should_receive(:all).with(user, repo, sha).and_return([{ state: 'success'}])
|
223
|
+
subject
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe '#find_authors_of_open_pull_request_comments(pull_request)' do
|
228
|
+
end
|
229
|
+
|
230
|
+
describe '#comment_authors_for_pull_request(pull_request, options = {})' do
|
231
|
+
end
|
232
|
+
|
233
|
+
describe '#get_commited_time(commit_sha)' do
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rspec'
|
3
|
+
require 'multi_json'
|
4
|
+
require 'webmock/rspec'
|
5
|
+
require 'pry'
|
6
|
+
|
7
|
+
$LOAD_PATH << 'lib'
|
8
|
+
require 'git_reflow'
|
9
|
+
|
10
|
+
Dir[File.expand_path('../support/**/*.rb', __FILE__)].each {|f| require f}
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.include GithubHelpers
|
14
|
+
config.include WebMock::API
|
15
|
+
config.include CommandLineHelpers
|
16
|
+
config.include GithubHelpers
|
17
|
+
config.include RspecStubHelpers
|
18
|
+
|
19
|
+
config.expect_with :rspec do |c|
|
20
|
+
c.syntax = [:should, :expect]
|
21
|
+
end
|
22
|
+
|
23
|
+
config.mock_with :rspec do |c|
|
24
|
+
c.syntax = [:should, :expect]
|
25
|
+
end
|
26
|
+
|
27
|
+
config.before(:each) do
|
28
|
+
WebMock.reset!
|
29
|
+
stub_command_line
|
30
|
+
allow_message_expectations_on_nil
|
31
|
+
end
|
32
|
+
|
33
|
+
config.after(:each) do
|
34
|
+
WebMock.reset!
|
35
|
+
reset_stubbed_command_line
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module CommandLineHelpers
|
2
|
+
def stub_command_line
|
3
|
+
$commands_ran = []
|
4
|
+
$output = []
|
5
|
+
|
6
|
+
stub_run_for GitReflow
|
7
|
+
stub_run_for GitReflow::Sandbox
|
8
|
+
|
9
|
+
STDOUT.stub(:puts) do |output|
|
10
|
+
$output << output
|
11
|
+
output = ''
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def stub_run_for(module_to_stub)
|
16
|
+
module_to_stub.stub(:run) do |command, options|
|
17
|
+
options ||= {}
|
18
|
+
$commands_ran << Hashie::Mash.new(command: command, options: options)
|
19
|
+
command = "" # we need this due to a bug in rspec that will keep this assignment on subsequent runs of the stub
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def reset_stubbed_command_line
|
24
|
+
$commands_ran = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def stub_command(command, return_value)
|
28
|
+
GitReflow::Sandbox.stub(:run).with(command).and_return(return_value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
RSpec::Matchers.define :have_run_command do |command|
|
33
|
+
match do |block|
|
34
|
+
block.call
|
35
|
+
(
|
36
|
+
$commands_ran.include? Hashie::Mash.new(command: command, options: {}) or
|
37
|
+
$commands_ran.include? Hashie::Mash.new(command: command, options: {with_system: true})
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
supports_block_expectations
|
42
|
+
|
43
|
+
failure_message do |block|
|
44
|
+
"expected to have run the command \`#{command}\` but instead ran:\n\t#{$commands_ran.inspect}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
RSpec::Matchers.define :have_run_command_silently do |command|
|
49
|
+
match do |block|
|
50
|
+
block.call
|
51
|
+
$commands_ran.include? Hashie::Mash.new(command: command, options: { loud: false })
|
52
|
+
end
|
53
|
+
|
54
|
+
supports_block_expectations
|
55
|
+
|
56
|
+
failure_message do |block|
|
57
|
+
"expected to have run the command \`#{command}\` silently but instead ran:\n\t#{$commands_ran.inspect}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
RSpec::Matchers.define :have_run_commands_in_order do |commands|
|
62
|
+
match do |block|
|
63
|
+
block.call
|
64
|
+
command_count = commands.count
|
65
|
+
command_start_index = $commands_ran.reverse.find_index {|c| c.command == commands.first }
|
66
|
+
return false unless command_start_index
|
67
|
+
|
68
|
+
$commands_ran.reverse.each_with_index do |command_ran, index|
|
69
|
+
next unless command_start_index
|
70
|
+
if command_count >= 1
|
71
|
+
current_command = commands[command_count - 1]
|
72
|
+
current_command.should == command_ran.command
|
73
|
+
command_count -= 1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
return command_count == 0
|
78
|
+
end
|
79
|
+
|
80
|
+
supports_block_expectations
|
81
|
+
|
82
|
+
failure_message do |block|
|
83
|
+
"expected to have run these commands in order:\n\t\t#{commands.inspect}\n\tgot:\n\t\t#{$commands_ran.inspect}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
RSpec::Matchers.define :have_output do |expected_output|
|
88
|
+
match do |block|
|
89
|
+
block.call
|
90
|
+
$output.include? expected_output
|
91
|
+
end
|
92
|
+
|
93
|
+
supports_block_expectations
|
94
|
+
|
95
|
+
failure_message do |block|
|
96
|
+
"expected STDOUT to include #{expected_output} but didn't: \n\t#{$output.inspect}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|