gemnasium 3.2.1

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.
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe Gemnasium::Connection do
4
+ before do
5
+ stub_config
6
+ stub_requests
7
+ end
8
+ let(:connection) { Gemnasium::Connection.new }
9
+
10
+ describe 'initialize' do
11
+ it 'initializes a Net::HTTP object' do
12
+ expect(connection.instance_variable_get('@connection')).to be_kind_of(Net::HTTP)
13
+ end
14
+ end
15
+
16
+ describe 'get' do
17
+ before { connection.get('/test_path') }
18
+
19
+ it 'issues a GET request' do
20
+ expect(WebMock).to have_requested(:get, api_url('/test_path'))
21
+ .with(:headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json'})
22
+ end
23
+ end
24
+
25
+ describe 'post' do
26
+ before { connection.post('/test_path', { foo: 'bar' }.to_json) }
27
+
28
+ it 'issues a POST request' do
29
+ expect(WebMock).to have_requested(:post, api_url('/test_path'))
30
+ .with(:body => {"foo"=>"bar"}, :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json'})
31
+ end
32
+ end
33
+
34
+ describe 'api_path_for' do
35
+ context 'base API path' do
36
+ it{ expect(connection.api_path_for('base')).to eql "/api/#{Gemnasium.config.api_version}" }
37
+ end
38
+
39
+ context 'projects API path' do
40
+ it{ expect(connection.api_path_for('projects')).to eql "/api/#{Gemnasium.config.api_version}/projects" }
41
+ end
42
+
43
+ context 'dependency files API path' do
44
+ it{ expect(connection.api_path_for('dependency_files')).to eql "/api/#{Gemnasium.config.api_version}/projects/#{Gemnasium.config.project_slug}/dependency_files" }
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe Gemnasium::DependencyFiles do
4
+ let(:project_path) { File.expand_path("#{File.dirname(__FILE__)}../../../")}
5
+
6
+ describe 'get_sha1s_hash' do
7
+ context 'with a non matching regexp' do
8
+ it 'returns an empty Hash' do
9
+ sha1s_hash = Gemnasium::DependencyFiles.get_sha1s_hash("#{project_path}/tmp")
10
+
11
+ expect(sha1s_hash).to be_kind_of Hash
12
+ expect(sha1s_hash).to be_empty
13
+ end
14
+ end
15
+
16
+ context 'with a mathing regexp' do
17
+ before { allow(Gemnasium).to receive_message_chain(:config, :ignored_paths).and_return([]) }
18
+ it 'returns a Hash of matching files and their git calculated hash' do
19
+ sha1s_hash = Gemnasium::DependencyFiles.get_sha1s_hash(project_path)
20
+
21
+ expect(sha1s_hash).to include({ 'gemnasium.gemspec' => git_hash('gemnasium.gemspec') })
22
+ end
23
+ end
24
+
25
+ context 'with a dependency file in a subdirectory' do
26
+ let(:subdir_file_path) { 'tmp/ignored.gemspec' }
27
+
28
+ before do
29
+ FileUtils.touch(subdir_file_path)
30
+ allow(Gemnasium).to receive_message_chain(:config, :ignored_paths).and_return([])
31
+ end
32
+ after { File.delete(subdir_file_path) }
33
+
34
+ it 'returns a Hash of the subdirectory dependency file' do
35
+ sha1s_hash = Gemnasium::DependencyFiles.get_sha1s_hash(project_path)
36
+
37
+ expect(sha1s_hash).to have_key(subdir_file_path)
38
+ if File.exists?('Gemfile.lock')
39
+ expect(sha1s_hash).to have_key('Gemfile.lock')
40
+ else
41
+ pending "Warning: 'Gemfile.lock' not found, skipping test."
42
+ end
43
+ end
44
+
45
+ context 'which is ignored' do
46
+ before { allow(Gemnasium).to receive_message_chain(:config, :ignored_paths).and_return([/^tmp/, /[^\/]+\.lock/]) }
47
+
48
+ it 'returns a Hash of matching files without ignored ones' do
49
+ sha1s_hash = Gemnasium::DependencyFiles.get_sha1s_hash(project_path)
50
+
51
+ expect(sha1s_hash).to_not have_key(subdir_file_path)
52
+ expect(sha1s_hash).to_not have_key('Gemfile.lock')
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ describe 'get_content_to_upload' do
59
+ context 'with no files' do
60
+ it 'returns an empty Hash' do
61
+ content_to_upload = Gemnasium::DependencyFiles.get_content_to_upload(project_path, [])
62
+
63
+ expect(content_to_upload).to be_kind_of Array
64
+ expect(content_to_upload).to be_empty
65
+ end
66
+ end
67
+
68
+ context 'with a mathing regexp' do
69
+ it 'returns a Hash of matching files and their git calculated hash' do
70
+ content_to_upload = Gemnasium::DependencyFiles.get_content_to_upload(project_path, ['gemnasium.gemspec'])
71
+
72
+ expect(content_to_upload).to eql([{ filename: 'gemnasium.gemspec', sha: git_hash('gemnasium.gemspec'), content: File.open('gemnasium.gemspec') {|io| io.read} }])
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ def git_hash(path)
79
+ %x( git hash-object #{path} ).strip
80
+ end
@@ -0,0 +1,135 @@
1
+ require 'spec_helper'
2
+
3
+ describe Gemnasium::Options do
4
+ describe 'parse' do
5
+ context 'without options' do
6
+ it 'does not parse any option' do
7
+ options, parser = Gemnasium::Options.parse []
8
+ expect(options).to be_empty
9
+ expect(parser).to be_kind_of OptionParser
10
+ end
11
+ end
12
+
13
+ context 'with version options' do
14
+ it 'understands short version' do
15
+ options, parser = Gemnasium::Options.parse ['-v']
16
+ expect(options).to eql({ show_version: true })
17
+ end
18
+
19
+ it 'understands long version' do
20
+ options, parser = Gemnasium::Options.parse ['--version']
21
+ expect(options).to eql({ show_version: true })
22
+ end
23
+ end
24
+
25
+ context 'with help options' do
26
+ it 'understands short version' do
27
+ options, parser = Gemnasium::Options.parse ['-h']
28
+ expect(options).to eql({ show_help: true })
29
+ end
30
+
31
+ it 'understands long version' do
32
+ options, parser = Gemnasium::Options.parse ['--help']
33
+ expect(options).to eql({ show_help: true })
34
+ end
35
+ end
36
+
37
+ context 'with multiple options' do
38
+ it 'understands concatenated options' do
39
+ options, parser = Gemnasium::Options.parse ['-hv']
40
+ expect(options).to eql({ show_help: true, show_version: true })
41
+ end
42
+
43
+ it 'understands separated options' do
44
+ options, parser = Gemnasium::Options.parse ['-v', '--help']
45
+ expect(options).to eql({ show_help: true, show_version: true })
46
+ end
47
+ end
48
+
49
+ context 'with unsupported option' do
50
+ it 'raises an error' do
51
+ expect { Gemnasium::Options.parse ['--foo'] }.to raise_error OptionParser::ParseError
52
+ end
53
+ end
54
+
55
+ context 'with unsupported subcommand' do
56
+ it 'raises an error' do
57
+ expect { Gemnasium::Options.parse ['hack'] }.to raise_error OptionParser::ParseError
58
+ end
59
+ end
60
+
61
+ context 'with unsupported option for a valid subcommand' do
62
+ it 'raises an error' do
63
+ expect { Gemnasium::Options.parse ['install', '--foo'] }.to raise_error OptionParser::ParseError
64
+ end
65
+ end
66
+
67
+ context 'with valid subcommand' do
68
+ context '`create`' do
69
+ context 'with no options' do
70
+ it 'correctly set the options' do
71
+ options, parser = Gemnasium::Options.parse ['create']
72
+ expect(options).to eql({ command: 'create' })
73
+ end
74
+ end
75
+ end
76
+
77
+ context '`install`' do
78
+ context 'with no options' do
79
+ it 'correctly set the options' do
80
+ options, parser = Gemnasium::Options.parse ['install']
81
+ expect(options).to eql({ command: 'install' })
82
+ end
83
+ end
84
+
85
+ context 'with rake option' do
86
+ it 'correctly set the options' do
87
+ options, parser = Gemnasium::Options.parse ['install', '--rake']
88
+ expect(options).to eql({ command: 'install', install_rake_task: true })
89
+ end
90
+ end
91
+ end
92
+
93
+ context '`push`' do
94
+ context 'with no options' do
95
+ it 'correctly set the options' do
96
+ options, parser = Gemnasium::Options.parse ['push']
97
+ expect(options).to eql({ command: 'push' })
98
+ end
99
+ end
100
+
101
+ context 'with ignore branch options' do
102
+ it 'correctly set the options' do
103
+ options, parser = Gemnasium::Options.parse ['push', '--ignore-branch']
104
+ expect(options).to eql({ command: 'push', ignore_branch: true })
105
+ end
106
+ end
107
+
108
+ context 'with silence branch failure options' do
109
+ it 'correctly set the options' do
110
+ options, parser = Gemnasium::Options.parse ['push', '--silence-branch']
111
+ expect(options).to eql({ command: 'push', silence_branch: true })
112
+ end
113
+ end
114
+ end
115
+
116
+ context '`migrate`' do
117
+ context 'with no options' do
118
+ it 'correctly set the options' do
119
+ options, parser = Gemnasium::Options.parse ['migrate']
120
+ expect(options).to eql({ command: 'migrate' })
121
+ end
122
+ end
123
+ end
124
+
125
+ context '`resolve`' do
126
+ context 'with no options' do
127
+ it 'correctly set the options' do
128
+ options, parser = Gemnasium::Options.parse ['resolve']
129
+ expect(options).to eql({ command: 'resolve' })
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,520 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'an installed file' do
4
+ it 'creates the configuration file' do
5
+ expect(File.exists? target_path).to be_truthy
6
+ # Test that the files are identical comparing their MD5 hashes
7
+ template_file_md5 = Digest::MD5.hexdigest(File.read(template_path))
8
+ new_file_md5 = Digest::MD5.hexdigest(File.read(target_path))
9
+ expect(new_file_md5).to eql template_file_md5
10
+ end
11
+
12
+ it 'informs the user that the file has been created' do
13
+ expect(output).to include "File created in #{target_path}"
14
+ end
15
+ end
16
+
17
+ shared_examples_for 'a command that requires a compatible config file' do
18
+ context 'with an old configuration file' do
19
+ before { stub_config(needs_to_migrate?: true) }
20
+
21
+ it 'quits the program' do
22
+ expect{ Gemnasium.push({ project_path: project_path }) }.to raise_error { |e|
23
+ expect(e).to be_kind_of SystemExit
24
+ expect(error_output).to include 'Your configuration file is not compatible with this version. Please run the `migrate` command first.'
25
+ }
26
+ end
27
+ end
28
+ end
29
+
30
+ describe Gemnasium do
31
+ let(:output) { [] }
32
+ let(:error_output) { [] }
33
+ let(:project_path) { File.expand_path("#{File.dirname(__FILE__)}../../tmp") }
34
+ before do
35
+ allow(Gemnasium).to receive(:notify) { |arg| output << arg }
36
+ allow(Gemnasium).to receive(:quit_because_of) { |arg| error_output << arg && abort }
37
+ stub_config
38
+ stub_requests
39
+ end
40
+
41
+ describe 'push' do
42
+
43
+ it_should_behave_like 'a command that requires a compatible config file'
44
+
45
+ context 'on a non tracked branch' do
46
+ before { allow(Gemnasium).to receive(:current_branch).and_return('non_project_branch') }
47
+
48
+ context 'with supported dependency files for gemnasium project not up-to-date' do
49
+ let(:sha1_hash) {{ 'new_gemspec.gemspec' => 'gemspec_sha1_hash', 'modified_lockfile.lock' => 'lockfile_sha1_hash', 'Gemfile_unchanged.lock' => 'gemfile_sha1_hash' }}
50
+ let(:hash_to_upload) {[{ filename: 'new_gemspec.gemspec', sha: 'gemspec_sha1_hash', content: 'stubbed gemspec content' },
51
+ { filename: 'modified_lockfile.lock', sha: 'lockfile_sha1_hash', content: 'stubbed lockfile content' }]}
52
+
53
+ before do
54
+ allow(Gemnasium::DependencyFiles).to receive(:get_sha1s_hash).and_return(sha1_hash)
55
+ allow(Gemnasium::DependencyFiles).to receive(:get_content_to_upload).and_return(hash_to_upload)
56
+ end
57
+
58
+ it 'quit the program with an error' do
59
+ expect{ Gemnasium.push({ project_path: project_path }) }.to raise_error { |e|
60
+ expect(e).to be_kind_of SystemExit
61
+ expect(error_output).to include 'Not on tracked branch (master)'
62
+ }
63
+ end
64
+
65
+ context "when :silence_branch is true" do
66
+ it "should not raise an error" do
67
+ expect{ Gemnasium.push({ project_path: project_path, silence_branch: true }) }.to_not raise_error
68
+ end
69
+
70
+ it 'should not contact Gemnasium' do
71
+ Gemnasium.push({ project_path: project_path, silence_branch: true })
72
+ expect(WebMock).to_not have_requested(:post, api_url('/api/v3/projects/existing-slug/dependency_files/compare'))
73
+ end
74
+ end
75
+
76
+ context "when :ignore_branch is true" do
77
+ it "should not raise an error" do
78
+ expect{ Gemnasium.push({ project_path: project_path, ignore_branch: true }) }.to_not raise_error
79
+ end
80
+
81
+ it 'should still contact Gemnasium' do
82
+ Gemnasium.push({ project_path: project_path, ignore_branch: true })
83
+ expect(WebMock).to have_requested(:post, api_url('/api/v3/projects/existing-slug/dependency_files/compare'))
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ context 'on the tracked branch' do
90
+ before { allow(Gemnasium).to receive(:current_branch).and_return('master') }
91
+
92
+ context 'with no project slug' do
93
+ before do
94
+ stub_config({ project_slug: nil })
95
+ end
96
+
97
+ it 'quit the program with an error' do
98
+ expect{ Gemnasium.push({ project_path: project_path }) }.to raise_error { |e|
99
+ expect(e).to be_kind_of SystemExit
100
+ expect(error_output).to include 'Project slug not defined. Please create a new project or "resolve" the name of an existing project.'
101
+ }
102
+ end
103
+ end
104
+
105
+ context 'with no supported dependency files found' do
106
+ before { allow(Gemnasium::DependencyFiles).to receive(:get_sha1s_hash).and_return([]) }
107
+
108
+ it 'quit the program with an error' do
109
+ expect{ Gemnasium.push({ project_path: project_path }) }.to raise_error { |e|
110
+ expect(e).to be_kind_of SystemExit
111
+ expect(error_output).to include "No supported dependency files detected."
112
+ }
113
+ end
114
+ end
115
+
116
+ context 'with supported dependency files found' do
117
+ let(:sha1_hash) {{ 'new_gemspec.gemspec' => 'gemspec_sha1_hash', 'modified_lockfile.lock' => 'lockfile_sha1_hash', 'Gemfile_unchanged.lock' => 'gemfile_sha1_hash' }}
118
+ before { allow(Gemnasium::DependencyFiles).to receive(:get_sha1s_hash).and_return(sha1_hash) }
119
+
120
+ context 'for a gemnasium project already up-to-date' do
121
+ before do
122
+ stub_config({ project_slug: 'up_to_date_project' })
123
+ Gemnasium.push({ project_path: project_path })
124
+ end
125
+
126
+ it 'quit the program with an error' do
127
+ expect(output).to include "The project's dependency files are up-to-date."
128
+ end
129
+ end
130
+
131
+ context 'for gemnasium project not up-to-date' do
132
+ let(:hash_to_upload) {[{ filename: 'new_gemspec.gemspec', sha: 'gemspec_sha1_hash', content: 'stubbed gemspec content' },
133
+ { filename: 'modified_lockfile.lock', sha: 'lockfile_sha1_hash', content: 'stubbed lockfile content' }]}
134
+ before do
135
+ allow(Gemnasium::DependencyFiles).to receive(:get_content_to_upload).and_return(hash_to_upload)
136
+ Gemnasium.push({ project_path: project_path })
137
+ end
138
+
139
+ it 'informs the user that it found supported dependency files' do
140
+ expect(output).to include "#{sha1_hash.keys.count} supported dependency file(s) found: #{sha1_hash.keys.join(', ')}"
141
+ end
142
+
143
+ it 'makes a request to Gemnasium to get updated files to upload' do
144
+ expect(WebMock).to have_requested(:post, api_url('/api/v3/projects/existing-slug/dependency_files/compare'))
145
+ .with(:body => sha1_hash.to_json,
146
+ :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json'})
147
+ end
148
+
149
+ it 'informs the user that Gemnasium has deleted an old dependency file' do
150
+ expect(output).to include "1 deleted file(s): old_dependency_file"
151
+ end
152
+
153
+ it 'displays the list of files that are being uploaded' do
154
+ expect(output).to include "2 file(s) to upload: new_gemspec.gemspec, modified_lockfile.lock"
155
+ end
156
+
157
+ it 'makes a request to Gemnasium to upload needed files' do
158
+ expect(WebMock).to have_requested(:post, api_url('/api/v3/projects/existing-slug/dependency_files/upload'))
159
+ .with(:body => hash_to_upload.to_json,
160
+ :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json'})
161
+ end
162
+
163
+ it 'informs the user of added file' do
164
+ expect(output).to include 'Added dependency files: ["new_gemspec.gemspec"]'
165
+ end
166
+
167
+ it 'informs the user of updated file' do
168
+ expect(output).to include 'Updated dependency files: ["modified_lockfile.lockfile"]'
169
+ end
170
+
171
+ it 'informs the user of unchanged file' do
172
+ expect(output).to_not include 'Unchanged dependency files: []'
173
+ end
174
+
175
+ it 'informs the user of unsupported file' do
176
+ expect(output).to_not include 'Unsupported dependency files: []'
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ describe 'create_project' do
184
+
185
+ it_should_behave_like 'a command that requires a compatible config file'
186
+
187
+ context 'with a project slug' do
188
+ before { stub_config({ project_slug: 'existing-slug' }) }
189
+
190
+ it 'quit the program with an error' do
191
+ expect{ Gemnasium.create_project({ project_path: project_path }) }.to raise_error { |e|
192
+ expect(e).to be_kind_of SystemExit
193
+ expect(error_output).to include "You already have a project slug refering to an existing project. Please remove this project slug from your configuration file to create a new project."
194
+ }
195
+ end
196
+ end
197
+
198
+ context 'with no project slug' do
199
+ before { stub_config({ project_slug: nil }) }
200
+
201
+ it 'issues the correct request' do
202
+ Gemnasium.create_project({ project_path: project_path })
203
+
204
+ expect(WebMock).to have_requested(:post, api_url("/api/v3/projects"))
205
+ .with(:body => {name: "gemnasium-gem", branch: "master"},
206
+ :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json'})
207
+ end
208
+
209
+ it 'displays a confirmation message' do
210
+ Gemnasium.create_project({ project_path: project_path })
211
+
212
+ expect(output).to include 'Project `gemnasium-gem` successfully created.'
213
+ expect(output).to include 'Project slug is `new-slug`.'
214
+ expect(output).to include 'Remaining private slots: 9001'
215
+ expect(output).to include 'Your configuration file has been updated.'
216
+ end
217
+
218
+ it 'updates the configuration file' do
219
+ expect(Gemnasium.config).to receive(:store_value!)
220
+ .with(:project_slug, 'new-slug', "This unique project slug has been set by `gemnasium create`.")
221
+
222
+ Gemnasium.create_project({ project_path: project_path })
223
+ end
224
+ end
225
+
226
+ context 'with a read-only config file' do
227
+ before { stub_config({ project_slug: nil, writable?: false }) }
228
+ before { Gemnasium.create_project({ project_path: project_path }) }
229
+
230
+ it 'displays a confirmation message' do
231
+ expect(output).to include 'Project `gemnasium-gem` successfully created.'
232
+ expect(output).to include 'Project slug is `new-slug`.'
233
+ expect(output).to include 'Remaining private slots: 9001'
234
+ expect(output).to include 'Configuration file cannot be updated. Please edit the file and update the project slug manually.'
235
+ end
236
+ end
237
+ end
238
+
239
+ describe 'migrate' do
240
+
241
+ context 'when the config file needs a migration' do
242
+ before { stub_config({ needs_to_migrate?: true }) }
243
+
244
+ it 'notifies the user' do
245
+ Gemnasium.migrate({ project_path: project_path })
246
+ expect(output).to include "Your configuration has been updated."
247
+ expect(output).to include "Run `gemnasium resolve` if your config is related to an existing project."
248
+ expect(output).to include "Run `gemnasium create` if you want to create a new project on Gemnasium."
249
+ end
250
+
251
+ it 'updates the config file' do
252
+ expect(Gemnasium.config).to receive(:migrate!)
253
+ Gemnasium.migrate({ project_path: project_path })
254
+ end
255
+ end
256
+
257
+ context 'when the config file is already up-to-date' do
258
+ before do
259
+ stub_config({ needs_to_migrate?: false })
260
+ Gemnasium.migrate({ project_path: project_path })
261
+ end
262
+
263
+ it 'notifies the user' do
264
+ expect(output).to include "Your configuration file is already up-to-date."
265
+ end
266
+ end
267
+ end
268
+
269
+ describe 'resolve_project' do
270
+
271
+ it_should_behave_like 'a command that requires a compatible config file'
272
+
273
+ context 'with a project slug' do
274
+ before { stub_config({ project_slug: 'existing-slug' }) }
275
+
276
+ it 'quit the program with an error' do
277
+ expect{ Gemnasium.resolve_project({ project_path: project_path }) }.to raise_error { |e|
278
+ expect(e).to be_kind_of SystemExit
279
+ expect(error_output).to include "You already have a project slug refering to an existing project. Please remove this project slug from your configuration file."
280
+ }
281
+ end
282
+ end
283
+
284
+ context 'with no project slug' do
285
+ context 'with no candidate on the server' do
286
+ before { stub_config({ project_slug: nil, project_name: 'no-candidate' }) }
287
+
288
+ it 'quit the program with an error' do
289
+ expect { Gemnasium.resolve_project({ project_path: project_path }) }.to raise_error { |e|
290
+ expect(e).to be_kind_of SystemExit
291
+ expect(error_output).to include "You have no off-line project matching name `no-candidate` and branch `master`."
292
+ }
293
+ end
294
+ end
295
+
296
+ context 'with one candidate on the server' do
297
+ before { stub_config({ project_slug: nil, project_name: 'one-candidate' }) }
298
+
299
+ it 'displays a confirmation message' do
300
+ Gemnasium.resolve_project({ project_path: project_path })
301
+
302
+ expect(output).to include 'Project slug is `one-candidate-slug`.'
303
+ expect(output).to include 'Your configuration file has been updated.'
304
+ end
305
+
306
+ it 'updates the configuration file' do
307
+ expect(Gemnasium.config).to receive(:store_value!)
308
+ .with(:project_slug, 'one-candidate-slug',
309
+ "This unique project slug has been set by `gemnasium resolve`.")
310
+
311
+ Gemnasium.resolve_project({ project_path: project_path })
312
+ end
313
+
314
+ context 'with a read-only config file' do
315
+ before { stub_config({ project_slug: nil, project_name: 'one-candidate', writable?: false }) }
316
+ before { Gemnasium.resolve_project({ project_path: project_path }) }
317
+
318
+ it 'displays a confirmation message' do
319
+ expect(output).to include 'Project slug is `one-candidate-slug`.'
320
+ expect(output).to include 'Configuration file cannot be updated. Please edit the file and update the project slug manually.'
321
+ end
322
+ end
323
+ end
324
+
325
+ context 'with many candidates on the server' do
326
+ before { stub_config({ project_slug: nil, project_name: 'many-candidates' }) }
327
+
328
+ it 'quit the program with an error' do
329
+ expect{ Gemnasium.resolve_project({ project_path: project_path }) }.to raise_error { |e|
330
+ expect(e).to be_kind_of SystemExit
331
+ expect(error_output).to include "You have more than one off-line project matching name `many-candidates` and branch `master`."
332
+ }
333
+ end
334
+ end
335
+ end
336
+ end
337
+
338
+ describe 'install' do
339
+ after { FileUtils.rm_r "#{project_path}/config" }
340
+
341
+ context 'if config file already exists' do
342
+ before do
343
+ FileUtils.mkdir_p "#{project_path}/config"
344
+ FileUtils.touch("#{project_path}/config/gemnasium.yml")
345
+
346
+ Gemnasium.install({ project_path: project_path })
347
+ end
348
+
349
+ it 'informs the user that the file already exists' do
350
+ expect(output).to include "The file #{project_path}/config/gemnasium.yml already exists"
351
+ end
352
+ end
353
+
354
+ context 'if config file does not exist' do
355
+ context 'neither do the config folder' do
356
+ before do
357
+ FileUtils.touch "#{project_path}/.gitignore"
358
+ Gemnasium.install({ project_path: project_path })
359
+ end
360
+ after { FileUtils.rm "#{project_path}/.gitignore" }
361
+
362
+ it 'creates the config folder' do
363
+ expect(File.exists? "#{project_path}/config").to be_truthy
364
+ end
365
+
366
+ it 'informs the user that the folder has been created' do
367
+ expect(output).to include "Creating config directory"
368
+ end
369
+
370
+ it_should_behave_like 'an installed file' do
371
+ let(:template_path) { File.expand_path("#{File.dirname(__FILE__)}../../lib/templates/gemnasium.yml") }
372
+ let(:target_path) { "#{project_path}/config/gemnasium.yml" }
373
+ end
374
+
375
+ it 'adds the config file to the .gitignore' do
376
+ expect(output).to include "Configuration file added to your project's .gitignore."
377
+ expect(File.open("#{project_path}/.gitignore").read()).to include "# Gemnasium gem configuration file\nconfig/gemnasium.yml"
378
+ end
379
+
380
+ it 'asks the user to fill the config file' do
381
+ expect(output).to include 'Please fill configuration file with accurate values.'
382
+ end
383
+ end
384
+
385
+ context 'with an already existing config folder' do
386
+ before do
387
+ FileUtils.mkdir_p "#{project_path}/config"
388
+ Gemnasium.install({ project_path: project_path })
389
+ end
390
+
391
+ it 'does not inform the user that the config folder has been created' do
392
+ expect(output).to_not include "Creating config directory"
393
+ end
394
+
395
+ it_should_behave_like 'an installed file' do
396
+ let(:template_path) { File.expand_path("#{File.dirname(__FILE__)}../../lib/templates/gemnasium.yml") }
397
+ let(:target_path) { "#{project_path}/config/gemnasium.yml" }
398
+ end
399
+
400
+ it 'asks the user to fill the config file' do
401
+ expect(output).to include 'Please fill configuration file with accurate values.'
402
+ end
403
+ end
404
+ end
405
+
406
+ context 'with git option' do
407
+ context 'for a non git repo' do
408
+ before { Gemnasium.install({ project_path: project_path, install_git_hook: true }) }
409
+
410
+ it 'informs the user that the target project is not a git repository' do
411
+ expect(output).to include "#{project_path} is not a git repository. Try to run `git init`."
412
+ end
413
+
414
+ it 'does not install the hook' do
415
+ expect(File.exists? "#{project_path}/.git/hooks/post-commit").to eql false
416
+ end
417
+ end
418
+
419
+ context 'for a git repo' do
420
+ before { FileUtils.mkdir_p "#{project_path}/.git/hooks" }
421
+ after { FileUtils.rm_r "#{project_path}/.git" }
422
+
423
+ context 'if the hook already exists' do
424
+ before do
425
+ FileUtils.touch("#{project_path}/.git/hooks/post-commit")
426
+ Gemnasium.install({ project_path: project_path, install_git_hook: true })
427
+ end
428
+
429
+ it 'informs the user that a post-commit hook already exists.' do
430
+ expect(output).to include "The file #{project_path}/.git/hooks/post-commit already exists"
431
+ end
432
+ end
433
+
434
+ context 'if the hook does not exist' do
435
+ before { Gemnasium.install({ project_path: project_path, install_git_hook: true }) }
436
+
437
+ it_should_behave_like 'an installed file' do
438
+ let(:template_path) { File.expand_path("#{File.dirname(__FILE__)}../../lib/templates/post-commit") }
439
+ let(:target_path) { "#{project_path}/.git/hooks/post-commit" }
440
+ end
441
+ end
442
+ end
443
+ end
444
+
445
+ context 'with rake option' do
446
+ context 'for a repo without Rakefile' do
447
+ before { Gemnasium.install({ project_path: project_path, install_rake_task: true }) }
448
+
449
+ it 'informs the user that the target repo does not contain Rakefile' do
450
+ expect(output).to include "Rakefile not found."
451
+ end
452
+
453
+ it 'does not install the task' do
454
+ expect(File.exists? "#{project_path}/lib/tasks/gemnasium.rake").to eql false
455
+ end
456
+ end
457
+
458
+ context 'for a project with Rakefile' do
459
+ before { FileUtils.touch("#{project_path}/Rakefile") }
460
+ after do
461
+ FileUtils.rm "#{project_path}/Rakefile"
462
+ FileUtils.rm_r "#{project_path}/lib"
463
+ end
464
+
465
+ context 'without /lib/tasks foler' do
466
+ before { Gemnasium.install({ project_path: project_path, install_rake_task: true }) }
467
+
468
+ it 'creates the /lib/tasks folder' do
469
+ expect(File.exists? "#{project_path}/lib/tasks").to be_truthy
470
+ end
471
+
472
+ it 'informs the user that the folder has been created' do
473
+ expect(output).to include "Creating lib/tasks directory"
474
+ end
475
+
476
+ it_should_behave_like 'an installed file' do
477
+ let(:template_path) { File.expand_path("#{File.dirname(__FILE__)}../../lib/templates/gemnasium.rake") }
478
+ let(:target_path) { "#{project_path}/lib/tasks/gemnasium.rake" }
479
+ end
480
+
481
+ it 'informs the user on how to use the rake tasks' do
482
+ expect(output).to include 'Usage:'
483
+ expect(output).to include "\trake gemnasium:push \t\t- to push your dependency files"
484
+ expect(output).to include "\trake gemnasium:create \t\t- to create your project on Gemnasium"
485
+ end
486
+ end
487
+
488
+ context 'with existing /lib/tasks foler' do
489
+ before { FileUtils.mkdir_p "#{project_path}/lib/tasks" }
490
+
491
+ context 'if the task file already exists' do
492
+ before do
493
+ FileUtils.touch("#{project_path}/lib/tasks/gemnasium.rake")
494
+ Gemnasium.install({ project_path: project_path, install_rake_task: true })
495
+ end
496
+
497
+ it 'informs the user that the rake task already exists.' do
498
+ expect(output).to include "The file #{project_path}/lib/tasks/gemnasium.rake already exists"
499
+ end
500
+ end
501
+
502
+ context 'if the task file does not exist' do
503
+ before { Gemnasium.install({ project_path: project_path, install_rake_task: true }) }
504
+
505
+ it_should_behave_like 'an installed file' do
506
+ let(:template_path) { File.expand_path("#{File.dirname(__FILE__)}../../lib/templates/gemnasium.rake") }
507
+ let(:target_path) { "#{project_path}/lib/tasks/gemnasium.rake" }
508
+ end
509
+
510
+ it 'informs the user on how to use the rake tasks' do
511
+ expect(output).to include 'Usage:'
512
+ expect(output).to include "\trake gemnasium:push \t\t- to push your dependency files"
513
+ expect(output).to include "\trake gemnasium:create \t\t- to create your project on Gemnasium"
514
+ end
515
+ end
516
+ end
517
+ end
518
+ end
519
+ end
520
+ end