gemnasium 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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