add simple spec for translations
[coquelicot.git] / spec / coquelicot_spec.rb
1 ENV['RACK_ENV'] = 'test'
2
3 require 'rubygems'
4 require 'bundler'
5 Bundler.setup
6
7 require 'rack/test'
8 require 'rspec'
9 require 'timecop'
10 require 'hpricot'
11 require 'tmpdir'
12
13 require 'coquelicot'
14
15 UPLOAD_PASSWORD = 'secret'
16
17 describe 'Coquelicot' do
18   include Rack::Test::Methods
19
20   def app
21     Coquelicot::Application
22   end
23
24   def upload(opts={})
25     opts = { :file => Rack::Test::UploadedFile.new(__FILE__, 'text/x-script.ruby'),
26              :upload_password => UPLOAD_PASSWORD
27            }.merge(opts)
28     post '/upload', opts
29     return nil unless last_response.redirect?
30     follow_redirect!
31     last_response.should be_ok
32     doc = Hpricot(last_response.body)
33     return (doc/'a').collect { |a| a.attributes['href'] }.
34              select { |h| h.start_with? "http://#{last_request.host}/" }[0]
35   end
36
37   before do
38     app.set :environment, :test
39   end
40
41   around(:each) do |example|
42     path = Dir.mktmpdir('coquelicot')
43     begin
44       app.set :depot_path, path
45       example.run
46     ensure
47       FileUtils.remove_entry_secure Coquelicot.depot.path
48     end
49   end
50
51   it "should offer an upload form" do
52     get '/'
53     last_response.should be_ok
54     doc = Hpricot(last_response.body)
55     (doc/"form#upload").should have(1).items
56   end
57
58   context "when I explicitely ask for french" do
59     it "should offer an upload form in french" do
60       get '/', :lang => 'fr'
61       last_response.should be_ok
62       doc = Hpricot(last_response.body)
63       (doc/"input.submit").attr('value').should == 'Partager¬†!'
64     end
65   end
66
67   context "when using 'simpleauth' authentication mechanism" do
68     before(:each) do
69       app.set :authentication_method, :name => :simplepass,
70                                       :upload_password => Digest::SHA1.hexdigest(UPLOAD_PASSWORD)
71     end
72
73     context "after a successful upload" do
74       before(:each) do
75         @url = upload
76       end
77
78       it "should not store the file in cleartext" do
79         files = Dir.glob("#{Coquelicot.depot.path}/*")
80         files.should have(1).items
81         File.new(files[0]).read().should_not include('should not store an uploaded file')
82       end
83
84       it "should generate a random URL to download the file" do
85         @url.should_not include(File.basename(__FILE__))
86       end
87
88       it "should store the file with a different name than the one in URL" do
89         url_name = @url.split('/')[-1]
90         files = Dir.glob("#{Coquelicot.depot.path}/*")
91         files.should have(1).items
92         url_name.should_not eql(File.basename(files[0]))
93       end
94
95       it "should encode the encryption key in URL as no password has been specified" do
96         url_name = @url.split('/')[-1]
97         url_name.split('-').should have(2).items
98       end
99
100       it "should download when using extra Base32 characters in URL" do
101         splitted = @url.split('/')
102         name = splitted[-1].upcase.gsub(/O/, '0').gsub(/L/, '1')
103         get "#{splitted[0..-2].join '/'}/#{name}"
104         last_response.should be_ok
105         last_response['Content-Type'].should eql('text/x-script.ruby')
106       end
107
108       context "when the file has been downloaded" do
109         before(:each) do
110           get @url
111         end
112
113         it "should be the same file as the uploaded" do
114           last_response.should be_ok
115           last_response['Content-Type'].should eql('text/x-script.ruby')
116           last_response.body.should eql(File.new(__FILE__).read)
117         end
118
119         it "should always has the same Last-Modified header" do
120           last_modified = last_response['Last-Modified']
121           last_modified.should_not be_nil
122           get @url
123           last_response['Last-Modified'].should eql(last_modified)
124         end
125       end
126     end
127
128     context "given an empty file" do
129       before do
130         @empty_file = Tempfile.new('empty')
131       end
132       it "should not be accepted when uploaded" do
133         url = upload :file => Rack::Test::UploadedFile.new(@empty_file.path, 'text/plain')
134         url.should be_nil
135         last_response.should_not be_redirect
136       end
137       after do
138         @empty_file.close true
139       end
140     end
141
142     it "should prevent upload without a password" do
143       url = upload :upload_password => ''
144       url.should be_nil
145       last_response.status.should eql(403)
146     end
147
148     it "should prevent upload with a wrong password" do
149       url = upload :upload_password => 'bad'
150       url.should be_nil
151       last_response.status.should eql(403)
152     end
153
154     context "when using AJAX to verify upload password" do
155       context "when sending the right password" do
156         before do
157           request "/authenticate", :method => "POST", :xhr => true,
158                                    :params => { :upload_password => UPLOAD_PASSWORD }
159         end
160         subject { last_response }
161         it { should be_ok }
162       end
163       context "when sending no password" do
164         before do
165           request "/authenticate", :method => "POST", :xhr => true,
166                                    :params => { :upload_password => '' }
167         end
168         subject { last_response.status }
169         it { should == 403 }
170       end
171       context "when sending a JSON dump of the wrong password" do
172         before do
173           request "/authenticate", :method => "POST", :xhr => true,
174                                    :params => { :upload_password => 'wrong'}
175         end
176         subject { last_response.status }
177         it { should == 403 }
178       end
179     end
180
181     context "when a 'one time download' has been retrieved" do
182       before(:each) do
183         @url = upload :one_time => true
184         get @url
185       end
186
187       it "should be the same as the uploaded file" do
188         last_response.should be_ok
189         last_response['Content-Type'].should eql('text/x-script.ruby')
190         last_response.body.should eql(File.new(__FILE__).read)
191       end
192
193       it "should not be downloadable any more" do
194         get @url
195         last_response.status.should eql(410)
196       end
197
198       it "should have zero'ed the file on the server" do
199         files = Dir.glob("#{Coquelicot.depot.path}/*")
200         files.should have(1).items
201         File.lstat(files[0]).size.should eql(0)
202       end
203     end
204
205     context "after a password protected upload" do
206       before(:each) do
207         @url = upload :file_key => 'somethingSecret'
208       end
209
210       it "should not return an URL with the encryption key" do
211         url_name = @url.split('/')[-1]
212         url_name.split('-').should have(1).items
213       end
214
215       it "should offer a password form before download" do
216         get @url
217         last_response.should be_ok
218         last_response['Content-Type'].should eql('text/html;charset=utf-8')
219         doc = Hpricot(last_response.body)
220         (doc/'input#file_key').should have(1).items
221       end
222
223       context "when given the correct password" do
224         it "should download the same file" do
225           post @url, :file_key => 'somethingSecret'
226           last_response.should be_ok
227           last_response['Content-Type'].should eql('text/x-script.ruby')
228           last_response.body.should eql(File.new(__FILE__).read)
229         end
230       end
231
232       it "should prevent download without a password" do
233         post @url
234         last_response.status.should eql(403)
235       end
236
237       it "should prevent download with a wrong password" do
238         post @url, :file_key => 'BAD'
239         last_response.status.should eql(403)
240       end
241     end
242
243     context "after an upload with a time limit" do
244       before(:each) do
245         @url = upload :expire => 60 # 1 hour
246       end
247
248       it "should prevent download after the time limit has expired" do
249         # let's be the day after tomorrow
250         Timecop.travel(Date.today + 2) do
251           get @url
252           last_response.status.should eql(410)
253         end
254       end
255     end
256
257     it "should refuse an expiration time longer than the maximum" do
258       upload :expire => 60 * 24 * 31 * 12 # 1 year
259       last_response.status.should eql(403)
260     end
261
262     it "should cleanup expired files" do
263       url = upload :expire => 60, :file_key => 'test' # 1 hour
264       url_name = url.split('/')[-1]
265       Dir.glob("#{Coquelicot.depot.path}/*").should have(1).items
266       # let's be the day after tomorrow
267       Timecop.travel(Date.today + 2) do
268         Coquelicot.depot.gc!
269         files = Dir.glob("#{Coquelicot.depot.path}/*")
270         files.should have(1).items
271         File.lstat(files[0]).size.should eql(0)
272         Coquelicot.depot.get_file(url_name).expired?.should be_true
273       end
274       # let's be after 'gone' period
275       Timecop.travel(Time.now + (Coquelicot.settings.gone_period * 60)) do
276         Coquelicot.depot.gc!
277         Dir.glob("#{Coquelicot.depot.path}/*").should have(0).items
278         Coquelicot.depot.get_file(url_name).should be_nil
279       end
280     end
281   end
282
283   context "when using 'imap' authentication mechanism" do
284     before(:each) do
285       app.set :authentication_method, :name => 'imap',
286                                       :imap_server => 'example.org',
287                                       :imap_port => 993
288     end
289
290     it "should try to login to the IMAP server when using AJAX" do
291       imap = stub('Net::Imap').as_null_object
292       imap.should_receive(:login).with('user', 'password')
293       Net::IMAP.should_receive(:new).with('example.org', 993, true).and_return(imap)
294
295       request "/authenticate", :method => "POST", :xhr => true,
296                                :params => { :imap_user     => 'user',
297                                             :imap_password => 'password' }
298       last_response.should be_ok
299     end
300   end
301 end