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 context "after a successful upload" do
55 it "should not store the file in cleartext" do
56 files = Dir.glob("#{Coquelicot.depot.path}/*")
57 files.should have(1).items
58 File.new(files[0]).read().should_not include('should not store an uploaded file')
61 it "should generate a random URL to download the file" do
62 @url.should_not include(File.basename(__FILE__))
65 it "should store the file with a different name than the one in URL" do
66 url_name = @url.split('/')[-1]
67 files = Dir.glob("#{Coquelicot.depot.path}/*")
68 files.should have(1).items
69 url_name.should_not eql(File.basename(files[0]))
72 it "should encode the encryption key in URL as no password has been specified" do
73 url_name = @url.split('/')[-1]
74 url_name.split('-').should have(2).items
77 it "should download when using extra Base32 characters in URL" do
78 splitted = @url.split('/')
79 name = splitted[-1].upcase.gsub(/O/, '0').gsub(/L/, '1')
80 get "#{splitted[0..-2].join '/'}/#{name}"
81 last_response.should be_ok
82 last_response['Content-Type'].should eql('text/x-script.ruby')
85 context "when the file has been downloaded" do
90 it "should be the same file as the uploaded" do
91 last_response.should be_ok
92 last_response['Content-Type'].should eql('text/x-script.ruby')
93 last_response.body.should eql(File.new(__FILE__).read)
96 it "should always has the same Last-Modified header" do
97 last_modified = last_response['Last-Modified']
98 last_modified.should_not be_nil
100 last_response['Last-Modified'].should eql(last_modified)
105 context "given an empty file" do
107 @empty_file = Tempfile.new('empty')
109 it "should not be accepted when uploaded" do
110 url = upload :file => Rack::Test::UploadedFile.new(@empty_file.path, 'text/plain')
112 last_response.should_not be_redirect
115 @empty_file.close true
119 it "should prevent upload without a password" do
120 url = upload :upload_password => ''
122 last_response.status.should eql(403)
125 it "should prevent upload with a wrong password" do
126 url = upload :upload_password => "bad"
128 last_response.status.should eql(403)
131 it "should allow AJAX upload password verification" do
132 request "/authenticate", :method => "POST", :xhr => true,
133 :params => { :upload_password => UPLOAD_PASSWORD }
134 last_response.should be_ok
135 request "/authenticate", :method => "POST", :xhr => true,
136 :params => { :upload_password => '' }
137 last_response.status.should eql(403)
138 request "/authenticate", :method => "POST", :xhr => true,
139 :params => { :upload_password => 'wrong' }
140 last_response.status.should eql(403)
143 context "when a 'one time download' has been retrieved" do
145 @url = upload :one_time => true
149 it "should be the same as the uploaded file" do
150 last_response.should be_ok
151 last_response['Content-Type'].should eql('text/x-script.ruby')
152 last_response.body.should eql(File.new(__FILE__).read)
155 it "should not be downloadable any more" do
157 last_response.status.should eql(410)
160 it "should have zero'ed the file on the server" do
161 files = Dir.glob("#{Coquelicot.depot.path}/*")
162 files.should have(1).items
163 File.lstat(files[0]).size.should eql(0)
167 context "after a password protected upload" do
169 @url = upload :file_key => 'somethingSecret'
172 it "should not return an URL with the encryption key" do
173 url_name = @url.split('/')[-1]
174 url_name.split('-').should have(1).items
177 it "should offer a password form before download" do
179 last_response.should be_ok
180 last_response['Content-Type'].should eql('text/html')
181 doc = Hpricot(last_response.body)
182 (doc/'input#file_key').should have(1).items
185 context "when given the correct password" do
186 it "should download the same file" do
187 post @url, :file_key => 'somethingSecret'
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)
194 it "should prevent download without a password" do
196 last_response.status.should eql(403)
199 it "should prevent download with a wrong password" do
200 post @url, :file_key => 'BAD'
201 last_response.status.should eql(403)
205 context "after an upload with a time limit" do
207 @url = upload :expire => 60 # 1 hour
210 it "should prevent download after the time limit has expired" do
212 Timecop.travel(Date.today + 1) do
214 last_response.status.should eql(410)
219 it "should refuse an expiration time longer than the maximum" do
220 upload :expire => 60 * 24 * 31 * 12 # 1 year
221 last_response.status.should eql(403)
224 it "should cleanup expired files" do
225 url = upload :expire => 60, :file_key => 'test' # 1 hour
226 url_name = url.split('/')[-1]
227 Dir.glob("#{Coquelicot.depot.path}/*").should have(1).items
229 Timecop.travel(Date.today + 1) do
231 files = Dir.glob("#{Coquelicot.depot.path}/*")
232 files.should have(1).items
233 File.lstat(files[0]).size.should eql(0)
234 Coquelicot.depot.get_file(url_name).expired?.should be_true
236 # let's be after 'gone' period
237 Timecop.travel(Time.now + (Coquelicot.settings.gone_period * 60)) do
239 Dir.glob("#{Coquelicot.depot.path}/*").should have(0).items
240 Coquelicot.depot.get_file(url_name).should be_nil