Merge pull request #121 from fkrull/master
[puppet_vcsrepo.git] / spec / unit / puppet / provider / vcsrepo / git_spec.rb
1 require 'spec_helper'
2
3 describe Puppet::Type.type(:vcsrepo).provider(:git_provider) do
4
5   let(:resource) { Puppet::Type.type(:vcsrepo).new({
6     :name     => 'test',
7     :ensure   => :present,
8     :provider => :git,
9     :revision => '2634',
10     :source   => 'git@repo',
11     :path     => '/tmp/test',
12   })}
13
14   let(:provider) { resource.provider }
15
16   before :each do
17     Puppet::Util.stubs(:which).with('git').returns('/usr/bin/git')
18   end
19
20   context 'creating' do
21     context "with a revision that is a remote branch" do
22       it "should execute 'git clone' and 'git checkout -b'" do
23         resource[:revision] = 'only/remote'
24         Dir.expects(:chdir).with('/').at_least_once.yields
25         Dir.expects(:chdir).with('/tmp/test').at_least_once.yields
26         provider.expects(:git).with('clone', resource.value(:source), resource.value(:path))
27         provider.expects(:update_submodules)
28         provider.expects(:git).with('branch', '-a').returns(resource.value(:revision))
29         provider.expects(:git).with('checkout', '--force', resource.value(:revision))
30         provider.create
31       end
32     end
33
34     context "with a remote not named 'origin'" do
35       it "should execute 'git clone --origin not_origin" do
36         resource[:remote] = 'not_origin'
37         Dir.expects(:chdir).with('/').at_least_once.yields
38         Dir.expects(:chdir).with('/tmp/test').at_least_once.yields
39         provider.expects(:git).with('clone', '--origin', 'not_origin', resource.value(:source), resource.value(:path))
40         provider.expects(:update_submodules)
41         provider.expects(:git).with('branch', '-a').returns(resource.value(:revision))
42         provider.expects(:git).with('checkout', '--force', resource.value(:revision))
43         provider.create
44       end
45     end
46
47     context "with shallow clone enable" do
48       it "should execute 'git clone --depth 1'" do
49         resource[:revision] = 'only/remote'
50         resource[:depth] = 1
51         Dir.expects(:chdir).with('/').at_least_once.yields
52         Dir.expects(:chdir).with('/tmp/test').at_least_once.yields
53         provider.expects(:git).with('clone', '--depth', '1', resource.value(:source), resource.value(:path))
54         provider.expects(:update_submodules)
55         provider.expects(:git).with('branch', '-a').returns(resource.value(:revision))
56         provider.expects(:git).with('checkout', '--force', resource.value(:revision))
57         provider.create
58       end
59     end
60
61     context "with a revision that is not a remote branch" do
62       it "should execute 'git clone' and 'git reset --hard'" do
63         resource[:revision] = 'a-commit-or-tag'
64         Dir.expects(:chdir).with('/').at_least_once.yields
65         Dir.expects(:chdir).with('/tmp/test').at_least_once.yields
66         provider.expects(:git).with('clone', resource.value(:source), resource.value(:path))
67         provider.expects(:update_submodules)
68         provider.expects(:git).with('branch', '-a').returns(resource.value(:revision))
69         provider.expects(:git).with('checkout', '--force', resource.value(:revision))
70         provider.create
71       end
72
73       it "should execute 'git clone' and submodule commands" do
74         resource.delete(:revision)
75         provider.expects(:git).with('clone', resource.value(:source), resource.value(:path))
76         provider.expects(:update_submodules)
77         provider.create
78       end
79     end
80
81     context "with an ensure of bare" do
82       context "with revision" do
83         it "should just execute 'git clone --bare'" do
84           resource[:ensure] = :bare
85           provider.expects(:git).with('clone', '--bare', resource.value(:source), resource.value(:path))
86           provider.create
87         end
88       end
89       context "without revision" do
90         it "should just execute 'git clone --bare'" do
91           resource[:ensure] = :bare
92           resource.delete(:revision)
93           provider.expects(:git).with('clone', '--bare', resource.value(:source), resource.value(:path))
94           provider.create
95         end
96       end
97     end
98
99     context "when a source is not given" do
100       context "when the path does not exist" do
101         it "should execute 'git init'" do
102           resource[:ensure] = :present
103           resource.delete(:source)
104           expects_mkdir
105           expects_chdir
106           expects_directory?(false)
107
108           provider.expects(:bare_exists?).returns(false)
109           provider.expects(:git).with('init')
110           provider.create
111         end
112       end
113
114       context "when the path is a bare repository" do
115         it "should convert it to a working copy" do
116           resource[:ensure] = :present
117           resource.delete(:source)
118           provider.expects(:bare_exists?).returns(true)
119           provider.expects(:convert_bare_to_working_copy)
120           provider.create
121         end
122       end
123
124       context "when the path is not empty and not a repository" do
125         it "should raise an exception" do
126           provider.expects(:path_exists?).returns(true)
127           provider.expects(:path_empty?).returns(false)
128           proc { provider.create }.should raise_error(Puppet::Error)
129         end
130       end
131     end
132
133     context "when the path does not exist" do
134       it "should execute 'git init --bare'" do
135         resource[:ensure] = :bare
136         resource.delete(:source)
137         expects_chdir
138         expects_mkdir
139         expects_directory?(false)
140         provider.expects(:working_copy_exists?).returns(false)
141         provider.expects(:git).with('init', '--bare')
142         provider.create
143       end
144     end
145
146     context "when the path is a working copy repository" do
147       it "should convert it to a bare repository" do
148         resource[:ensure] = :bare
149         resource.delete(:source)
150         provider.expects(:working_copy_exists?).returns(true)
151         provider.expects(:convert_working_copy_to_bare)
152         provider.create
153       end
154     end
155
156     context "when the path is not empty and not a repository" do
157       it "should raise an exception" do
158         expects_directory?(true)
159         provider.expects(:path_empty?).returns(false)
160         proc { provider.create }.should raise_error(Puppet::Error)
161       end
162     end
163   end
164
165
166   context 'destroying' do
167     it "it should remove the directory" do
168       #expects_rm_rf
169       provider.destroy
170     end
171   end
172
173   context "checking the revision property" do
174     before do
175       expects_chdir('/tmp/test')
176       resource[:revision] = 'currentsha'
177       resource.delete(:source)
178       provider.expects(:git).with('rev-parse', 'HEAD').returns('currentsha')
179     end
180
181     context "when its SHA is not different than the current SHA" do
182       it "should return the ref" do
183         provider.expects(:git).with('config', 'remote.origin.url').returns('')
184         provider.expects(:git).with('fetch', 'origin') # FIXME
185         provider.expects(:git).with('fetch', '--tags', 'origin')
186         provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('currentsha')
187         provider.expects(:git).with('tag', '-l').returns("Hello")
188         provider.revision.should == resource.value(:revision)
189       end
190     end
191
192     context "when its SHA is different than the current SHA" do
193       it "should return the current SHA" do
194         provider.expects(:git).with('config', 'remote.origin.url').returns('')
195         provider.expects(:git).with('fetch', 'origin') # FIXME
196         provider.expects(:git).with('fetch', '--tags', 'origin')
197         provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('othersha')
198         provider.expects(:git).with('tag', '-l').returns("Hello")
199         provider.revision.should == 'currentsha'
200       end
201     end
202
203     context "when its a ref to a remote head" do
204       it "should return the revision" do
205         provider.expects(:git).with('config', 'remote.origin.url').returns('')
206         provider.expects(:git).with('fetch', 'origin') # FIXME
207         provider.expects(:git).with('fetch', '--tags', 'origin')
208         provider.expects(:git).with('tag', '-l').returns("Hello")
209         provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('')
210         provider.expects(:git).with('ls-remote', '--heads', '--tags', 'origin', resource.value(:revision)).returns("newsha refs/heads/#{resource.value(:revision)}")
211         provider.revision.should == 'currentsha'
212       end
213     end
214
215     context "when its a ref to non existant remote head" do
216       it "should fail" do
217         provider.expects(:git).with('config', 'remote.origin.url').returns('')
218         provider.expects(:git).with('fetch', 'origin') # FIXME
219         provider.expects(:git).with('fetch', '--tags', 'origin')
220         provider.expects(:git).with('tag', '-l').returns("Hello")
221         provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('')
222         provider.expects(:git).with('ls-remote', '--heads', '--tags', 'origin', resource.value(:revision)).returns('')
223         expect { provider.revision }.to raise_error(Puppet::Error, /not a local or remote ref$/)
224       end
225     end
226
227     context "when the source is modified" do
228       it "should update the origin url" do
229         resource[:source] = 'git://git@foo.com/bar.git'
230         provider.expects(:git).with('config', 'remote.origin.url').returns('old')
231         provider.expects(:git).with('config', 'remote.origin.url', 'git://git@foo.com/bar.git')
232         provider.expects(:git).with('fetch', 'origin') # FIXME
233         provider.expects(:git).with('fetch', '--tags', 'origin')
234         provider.expects(:git).with('rev-parse', '--revs-only', resource.value(:revision)).returns('currentsha')
235         provider.expects(:git).with('tag', '-l').returns("Hello")
236         provider.revision.should == resource.value(:revision)
237       end
238     end
239   end
240
241   context "setting the revision property" do
242     before do
243       expects_chdir
244     end
245     context "when it's an existing local branch" do
246       it "should use 'git fetch' and 'git reset'" do
247         resource[:revision] = 'feature/foo'
248         provider.expects(:update_submodules)
249         provider.expects(:git).with('branch', '-a').returns(resource.value(:revision))
250         provider.expects(:git).with('checkout', '--force', resource.value(:revision))
251         provider.expects(:git).with('branch', '-a').returns(resource.value(:revision))
252         provider.expects(:git).with('reset', '--hard', "origin/#{resource.value(:revision)}")
253         provider.revision = resource.value(:revision)
254       end
255     end
256     context "when it's a remote branch" do
257       it "should use 'git fetch' and 'git reset'" do
258         resource[:revision] = 'only/remote'
259         provider.expects(:update_submodules)
260         provider.expects(:git).with('branch', '-a').returns(resource.value(:revision))
261         provider.expects(:git).with('checkout', '--force', resource.value(:revision))
262         provider.expects(:git).with('branch', '-a').returns(resource.value(:revision))
263         provider.expects(:git).with('reset', '--hard', "origin/#{resource.value(:revision)}")
264         provider.revision = resource.value(:revision)
265       end
266     end
267     context "when it's a commit or tag" do
268       it "should use 'git fetch' and 'git reset'" do
269         resource[:revision] = 'a-commit-or-tag'
270         provider.expects(:git).with('branch', '-a').returns(fixture(:git_branch_a))
271         provider.expects(:git).with('checkout', '--force', resource.value(:revision))
272         provider.expects(:git).with('branch', '-a').returns(fixture(:git_branch_a))
273         provider.expects(:git).with('branch', '-a').returns(fixture(:git_branch_a))
274         provider.expects(:git).with('submodule', 'update', '--init', '--recursive')
275         provider.revision = resource.value(:revision)
276       end
277     end
278   end
279
280   context "updating references" do
281     it "should use 'git fetch --tags'" do
282       resource.delete(:source)
283       expects_chdir
284       provider.expects(:git).with('config', 'remote.origin.url').returns('')
285       provider.expects(:git).with('fetch', 'origin')
286       provider.expects(:git).with('fetch', '--tags', 'origin')
287       provider.update_references
288     end
289   end
290
291   context "checking if revision" do
292     before do
293       expects_chdir
294       provider.expects(:git).with('branch', '-a').returns(fixture(:git_branch_a))
295     end
296     context "is a local branch" do
297       context "when it's listed in 'git branch -a'" do
298         it "should return true" do
299           resource[:revision] = 'feature/foo'
300           provider.should be_local_branch_revision
301         end
302       end
303       context "when it's not listed in 'git branch -a'" do
304         it "should return false" do
305           resource[:revision] = 'feature/notexist'
306           provider.should_not be_local_branch_revision
307         end
308       end
309     end
310     context "is a remote branch" do
311       context "when it's listed in 'git branch -a' with an 'origin/' prefix" do
312         it "should return true" do
313           resource[:revision] = 'only/remote'
314           provider.should be_remote_branch_revision
315         end
316       end
317       context "when it's not listed in 'git branch -a' with an 'origin/' prefix" do
318         it "should return false" do
319           resource[:revision] = 'only/local'
320           provider.should_not be_remote_branch_revision
321         end
322       end
323     end
324   end
325
326   context "retrieving the current revision" do
327     before do
328       expects_chdir
329       provider.expects(:git).with('rev-parse', '--abbrev-ref', 'HEAD').returns("foo\n")
330     end
331
332     it "will strip trailing newlines" do
333       provider.expects(:get_revision).with('origin/foo')
334       provider.latest
335     end
336   end
337
338   describe 'latest?' do
339     before do
340       expects_chdir('/tmp/test')
341     end
342     context 'when true' do
343       it do
344         provider.expects(:revision).returns('testrev')
345         provider.expects(:latest).returns('testrev')
346         provider.latest?.should be_true
347       end
348     end
349     context 'when false' do
350       it do
351         provider.expects(:revision).returns('master')
352         provider.expects(:latest).returns('testrev')
353         provider.latest?.should be_false
354       end
355     end
356   end
357
358   describe 'latest' do
359     before do
360       provider.expects(:get_revision).returns('master')
361       expects_chdir
362     end
363     context 'on master' do
364       it do
365         provider.expects(:git).with('rev-parse', '--abbrev-ref', 'HEAD').returns(fixture(:git_branch_a))
366         provider.latest.should == 'master'
367       end
368     end
369     context 'no branch' do
370       it do
371         provider.expects(:git).with('rev-parse', '--abbrev-ref', 'HEAD').returns(fixture(:git_branch_none))
372         provider.latest.should == 'master'
373       end
374     end
375     context 'feature/bar' do
376       it do
377         provider.expects(:git).with('rev-parse', '--abbrev-ref', 'HEAD').returns(fixture(:git_branch_feature_bar))
378         provider.latest.should == 'master'
379       end
380     end
381   end
382
383   describe 'convert_working_copy_to_bare' do
384     it do
385       FileUtils.expects(:mv).returns(true)
386       FileUtils.expects(:rm_rf).returns(true)
387       FileUtils.expects(:mv).returns(true)
388
389       provider.instance_eval { convert_working_copy_to_bare }
390     end
391   end
392
393   describe 'convert_bare_to_working_copy' do
394     it do
395       FileUtils.expects(:mv).returns(true)
396       FileUtils.expects(:mkdir).returns(true)
397       FileUtils.expects(:mv).returns(true)
398       provider.expects(:commits_in?).returns(true)
399       # If you forget to stub these out you lose 3 hours of rspec work.
400       provider.expects(:reset).with('HEAD').returns(true)
401       provider.expects(:git_with_identity).returns(true)
402       provider.expects(:update_owner_and_excludes).returns(true)
403
404       provider.instance_eval { convert_bare_to_working_copy }
405     end
406   end
407
408 end