1 $:.unshift File.join(File.dirname(__FILE__), '../rack-test/lib')
2 $:.unshift File.join(File.dirname(__FILE__), '../timecop/lib')
4 require 'coquelicot_app'
11 UPLOAD_PASSWORD = 'secret'
13 describe 'Coquelicot' do
14 include Rack::Test::Methods
17 Coquelicot::Application
21 opts = { :file => Rack::Test::UploadedFile.new(__FILE__, 'text/x-script.ruby'),
22 :upload_password => UPLOAD_PASSWORD
25 return nil unless last_response.redirect?
27 last_response.should be_ok
28 doc = Hpricot(last_response.body)
29 return (doc/'a').collect { |a| a.attributes['href'] }.
30 select { |h| h.start_with? "http://#{last_request.host}/" }[0]
34 app.set :environment, :test
35 app.set :upload_password, Digest::SHA1.hexdigest(UPLOAD_PASSWORD)
36 app.set :depot_path, Dir.mktmpdir('coquelicot')
40 FileUtils.remove_entry_secure Coquelicot.depot.path
43 it "should offer an upload form" do
45 last_response.should be_ok
46 doc = Hpricot(last_response.body)
47 (doc/"form#upload").should have(1).items
50 it "should allow retrieval of an uploaded file" do
53 last_response.should be_ok
54 last_response['Content-Type'].should eql('text/x-script.ruby')
55 last_response.body.should eql(File.new(__FILE__).read)
58 it "should refuse an empty file" do
59 empty_file = Tempfile.new('empty')
61 url = upload :file => Rack::Test::UploadedFile.new(empty_file.path, 'text/plain')
63 last_response.redirect?.should be_false
69 it "should correctly set Last-Modified header when downloading" do
72 last_modified = last_response['Last-Modified']
73 last_modified.should_not be_nil
75 last_response['Last-Modified'].should eql(last_modified)
78 it "should prevent upload without a password" do
79 url = upload :upload_password => ''
81 last_response.status.should eql(403)
84 it "should prevent upload with a wrong password" do
85 url = upload :upload_password => "bad"
87 last_response.status.should eql(403)
90 it "should allow AJAX upload password verification" do
91 request "/authenticate", :method => "POST", :xhr => true,
92 :params => { :upload_password => UPLOAD_PASSWORD }
93 last_response.should be_ok
94 request "/authenticate", :method => "POST", :xhr => true,
95 :params => { :upload_password => '' }
96 last_response.status.should eql(403)
97 request "/authenticate", :method => "POST", :xhr => true,
98 :params => { :upload_password => 'wrong' }
99 last_response.status.should eql(403)
102 it "should not store an uploaded file in cleartext" do
104 files = Dir.glob("#{Coquelicot.depot.path}/*")
105 files.should have(1).items
106 File.new(files[0]).read().should_not include('should not store an uploaded file')
109 it "should generate a random URL to retrieve a file" do
111 url.should_not include(File.basename(__FILE__))
114 it "should store files with a different name than then one in URL" do
116 url_name = url.split('/')[-1]
117 files = Dir.glob("#{Coquelicot.depot.path}/*")
118 files.should have(1).items
119 url_name.should_not eql(File.basename(files[0]))
122 it "should encode the encryption key in URL when no password has been specified" do
124 url_name = url.split('/')[-1]
125 url_name.split('-').should have(2).items
128 it "should not encode the encryption key in URL when a password has been specified" do
129 url = upload :file_key => 'somethingSecret'
130 url_name = url.split('/')[-1]
131 url_name.split('-').should have(1).items
134 context "when a 'one time download' has been retrieved" do
136 @url = upload :one_time => true
140 it "should be the same as the uploaded file" do
141 last_response.should be_ok
142 last_response['Content-Type'].should eql('text/x-script.ruby')
143 last_response.body.should eql(File.new(__FILE__).read)
146 it "should not be downloadable any more" do
148 last_response.status.should eql(410)
151 it "should have zero'ed the file on the server" do
152 files = Dir.glob("#{Coquelicot.depot.path}/*")
153 files.should have(1).items
154 File.lstat(files[0]).size.should eql(0)
158 it "should allow retrieval of a password protected file" do
159 url = upload :file_key => 'somethingSecret'
161 last_response.should be_ok
162 doc = Hpricot(last_response.body)
163 (doc/'input#file_key').should have(1).items
164 url = (doc/'form')[0].attributes['action']
165 post url, :file_key => 'somethingSecret'
166 last_response.should be_ok
167 last_response['Content-Type'].should eql('text/x-script.ruby')
168 last_response.body.should eql(File.new(__FILE__).read)
171 it "should not allow retrieval of a password protected file without the password" do
172 url = upload :file_key => 'somethingSecret'
174 last_response.should be_ok
175 last_response['Content-Type'].should_not eql('text/x-script.ruby')
177 last_response.status.should eql(403)
180 it "should not allow retrieval of a password protected file with a wrong password" do
181 url = upload :file_key => 'somethingSecret'
182 post url, :file_key => 'BAD'
183 last_response.status.should eql(403)
186 it "should not allow retrieval after the time limit has expired" do
187 url = upload :expire => 60 # 1 hour
189 Timecop.travel(Date.today + 1) do
191 last_response.status.should eql(410)
195 it "should not allow an expiration time longer than the maximum" do
196 upload :expire => 60 * 24 * 31 * 12 # 1 year
197 last_response.status.should eql(403)
200 it "should cleanup expired files" do
201 url = upload :expire => 60, :file_key => 'test' # 1 hour
202 url_name = url.split('/')[-1]
203 Dir.glob("#{Coquelicot.depot.path}/*").should have(1).items
205 Timecop.travel(Date.today + 1) do
207 files = Dir.glob("#{Coquelicot.depot.path}/*")
208 files.should have(1).items
209 File.lstat(files[0]).size.should eql(0)
210 Coquelicot.depot.get_file(url_name).expired?.should be_true
212 # let's be after 'gone' period
213 Timecop.travel(Time.now + (Coquelicot.settings.gone_period * 60)) do
215 Dir.glob("#{Coquelicot.depot.path}/*").should have(0).items
216 Coquelicot.depot.get_file(url_name).should be_nil
220 it "should map extra base32 characters to filenames" do
221 url = upload :expire => 60 # 1 hour
222 splitted = url.split('/')
223 name = splitted[-1].upcase.gsub(/O/, '0').gsub(/L/, '1')
224 get "#{splitted[0..-2].join '/'}/#{name}"
225 last_response.should be_ok
226 last_response['Content-Type'].should eql('text/x-script.ruby')