1 require 'coquelicot_app'
8 UPLOAD_PASSWORD = 'secret'
10 describe 'Coquelicot' do
11 include Rack::Test::Methods
14 Coquelicot::Application
18 opts = { :file => Rack::Test::UploadedFile.new(__FILE__, 'text/x-script.ruby'),
19 :upload_token => JSON.dump({ 'upload_password' => UPLOAD_PASSWORD})
22 return nil unless last_response.redirect?
24 last_response.should be_ok
25 doc = Hpricot(last_response.body)
26 return (doc/'a').collect { |a| a.attributes['href'] }.
27 select { |h| h.start_with? "http://#{last_request.host}/" }[0]
31 app.set :environment, :test
32 app.authentication_method :simplepass, :upload_password => Digest::SHA1.hexdigest(UPLOAD_PASSWORD)
33 app.set :depot_path, Dir.mktmpdir('coquelicot')
37 FileUtils.remove_entry_secure Coquelicot.depot.path
40 it "should offer an upload form" do
42 last_response.should be_ok
43 doc = Hpricot(last_response.body)
44 (doc/"form#upload").should have(1).items
47 context "after a successful upload" do
52 it "should not store the file in cleartext" do
53 files = Dir.glob("#{Coquelicot.depot.path}/*")
54 files.should have(1).items
55 File.new(files[0]).read().should_not include('should not store an uploaded file')
58 it "should generate a random URL to download the file" do
59 @url.should_not include(File.basename(__FILE__))
62 it "should store the file with a different name than the one in URL" do
63 url_name = @url.split('/')[-1]
64 files = Dir.glob("#{Coquelicot.depot.path}/*")
65 files.should have(1).items
66 url_name.should_not eql(File.basename(files[0]))
69 it "should encode the encryption key in URL as no password has been specified" do
70 url_name = @url.split('/')[-1]
71 url_name.split('-').should have(2).items
74 it "should download when using extra Base32 characters in URL" do
75 splitted = @url.split('/')
76 name = splitted[-1].upcase.gsub(/O/, '0').gsub(/L/, '1')
77 get "#{splitted[0..-2].join '/'}/#{name}"
78 last_response.should be_ok
79 last_response['Content-Type'].should eql('text/x-script.ruby')
82 context "when the file has been downloaded" do
87 it "should be the same file as the uploaded" do
88 last_response.should be_ok
89 last_response['Content-Type'].should eql('text/x-script.ruby')
90 last_response.body.should eql(File.new(__FILE__).read)
93 it "should always has the same Last-Modified header" do
94 last_modified = last_response['Last-Modified']
95 last_modified.should_not be_nil
97 last_response['Last-Modified'].should eql(last_modified)
102 context "given an empty file" do
104 @empty_file = Tempfile.new('empty')
106 it "should not be accepted when uploaded" do
107 url = upload :file => Rack::Test::UploadedFile.new(@empty_file.path, 'text/plain')
109 last_response.should_not be_redirect
112 @empty_file.close true
116 it "should prevent upload without a password" do
117 url = upload :upload_token => JSON.dump({'upload_password' => ''})
119 last_response.status.should eql(403)
122 it "should prevent upload with a wrong password" do
123 url = upload :upload_token => JSON.dump({'upload_password' => 'bad'})
125 last_response.status.should eql(403)
128 it "should allow AJAX upload password verification" do
129 request "/authenticate", :method => "POST", :xhr => true,
130 :params => { :upload_token => { 'upload_password' => UPLOAD_PASSWORD } }
131 last_response.should be_ok
132 request "/authenticate", :method => "POST", :xhr => true,
133 :params => { :upload_token => '{}' }
134 last_response.status.should eql(403)
135 request "/authenticate", :method => "POST", :xhr => true,
136 :params => { :upload_token => JSON.dump({'upload_password' => 'wrong'}) }
137 last_response.status.should eql(403)
140 context "when a 'one time download' has been retrieved" do
142 @url = upload :one_time => true
146 it "should be the same as the uploaded file" do
147 last_response.should be_ok
148 last_response['Content-Type'].should eql('text/x-script.ruby')
149 last_response.body.should eql(File.new(__FILE__).read)
152 it "should not be downloadable any more" do
154 last_response.status.should eql(410)
157 it "should have zero'ed the file on the server" do
158 files = Dir.glob("#{Coquelicot.depot.path}/*")
159 files.should have(1).items
160 File.lstat(files[0]).size.should eql(0)
164 context "after a password protected upload" do
166 @url = upload :file_key => 'somethingSecret'
169 it "should not return an URL with the encryption key" do
170 url_name = @url.split('/')[-1]
171 url_name.split('-').should have(1).items
174 it "should offer a password form before download" do
176 last_response.should be_ok
177 last_response['Content-Type'].should eql('text/html')
178 doc = Hpricot(last_response.body)
179 (doc/'input#file_key').should have(1).items
182 context "when given the correct password" do
183 it "should download the same file" do
184 post @url, :file_key => 'somethingSecret'
185 last_response.should be_ok
186 last_response['Content-Type'].should eql('text/x-script.ruby')
187 last_response.body.should eql(File.new(__FILE__).read)
191 it "should prevent download without a password" do
193 last_response.status.should eql(403)
196 it "should prevent download with a wrong password" do
197 post @url, :file_key => 'BAD'
198 last_response.status.should eql(403)
202 context "after an upload with a time limit" do
204 @url = upload :expire => 60 # 1 hour
207 it "should prevent download after the time limit has expired" do
209 Timecop.travel(Date.today + 1) do
211 last_response.status.should eql(410)
216 it "should refuse an expiration time longer than the maximum" do
217 upload :expire => 60 * 24 * 31 * 12 # 1 year
218 last_response.status.should eql(403)
221 it "should cleanup expired files" do
222 url = upload :expire => 60, :file_key => 'test' # 1 hour
223 url_name = url.split('/')[-1]
224 Dir.glob("#{Coquelicot.depot.path}/*").should have(1).items
226 Timecop.travel(Date.today + 1) do
228 files = Dir.glob("#{Coquelicot.depot.path}/*")
229 files.should have(1).items
230 File.lstat(files[0]).size.should eql(0)
231 Coquelicot.depot.get_file(url_name).expired?.should be_true
233 # let's be after 'gone' period
234 Timecop.travel(Time.now + (Coquelicot.settings.gone_period * 60)) do
236 Dir.glob("#{Coquelicot.depot.path}/*").should have(0).items
237 Coquelicot.depot.get_file(url_name).should be_nil