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 correctly set Last-Modified header when downloading" do
61 last_modified = last_response['Last-Modified']
62 last_modified.should_not be_nil
64 last_response['Last-Modified'].should eql(last_modified)
67 it "should prevent upload without a password" do
68 url = upload :upload_password => ''
70 last_response.status.should eql(403)
73 it "should prevent upload with a wrong password" do
74 url = upload :upload_password => "bad"
76 last_response.status.should eql(403)
79 it "should allow AJAX upload password verification" do
80 request "/authenticate", :method => "POST", :xhr => true,
81 :params => { :upload_password => UPLOAD_PASSWORD }
82 last_response.should be_ok
83 request "/authenticate", :method => "POST", :xhr => true,
84 :params => { :upload_password => '' }
85 last_response.status.should eql(403)
86 request "/authenticate", :method => "POST", :xhr => true,
87 :params => { :upload_password => 'wrong' }
88 last_response.status.should eql(403)
91 it "should not store an uploaded file in cleartext" do
93 files = Dir.glob("#{Coquelicot.depot.path}/*")
94 files.should have(1).items
95 File.new(files[0]).read().should_not include('should not store an uploaded file')
98 it "should generate a random URL to retrieve a file" do
100 url.should_not include(File.basename(__FILE__))
103 it "should store files with a different name than then one in URL" do
105 url_name = url.split('/')[-1]
106 files = Dir.glob("#{Coquelicot.depot.path}/*")
107 files.should have(1).items
108 url_name.should_not eql(File.basename(files[0]))
111 it "should encode the encryption key in URL when no password has been specified" do
113 url_name = url.split('/')[-1]
114 url_name.split('-').should have(2).items
117 it "should not encode the encryption key in URL when a password has been specified" do
118 url = upload :file_key => 'somethingSecret'
119 url_name = url.split('/')[-1]
120 url_name.split('-').should have(1).items
123 it "should only allow one time download to be retrieved once" do
124 url = upload :one_time => true
126 last_response.should be_ok
127 last_response['Content-Type'].should eql('text/x-script.ruby')
128 last_response.body.should eql(File.new(__FILE__).read)
130 last_response.status.should eql(410)
133 it "should have files zero'ed after 'one time' download" do
134 url = upload :one_time => true
136 files = Dir.glob("#{Coquelicot.depot.path}/*")
137 files.should have(1).items
138 File.lstat(files[0]).size.should eql(0)
141 it "should allow retrieval of a password protected file" do
142 url = upload :file_key => 'somethingSecret'
144 last_response.should be_ok
145 doc = Hpricot(last_response.body)
146 (doc/'input#file_key').should have(1).items
147 url = (doc/'form')[0].attributes['action']
148 post url, :file_key => 'somethingSecret'
149 last_response.should be_ok
150 last_response['Content-Type'].should eql('text/x-script.ruby')
151 last_response.body.should eql(File.new(__FILE__).read)
154 it "should not allow retrieval of a password protected file without the password" do
155 url = upload :file_key => 'somethingSecret'
157 last_response.should be_ok
158 last_response['Content-Type'].should_not eql('text/x-script.ruby')
160 last_response.status.should eql(403)
163 it "should not allow retrieval of a password protected file with a wrong password" do
164 url = upload :file_key => 'somethingSecret'
165 post url, :file_key => 'BAD'
166 last_response.status.should eql(403)
169 it "should not allow retrieval after the time limit has expired" do
170 url = upload :expire => 60 # 1 hour
172 Timecop.travel(Date.today + 1) do
174 last_response.status.should eql(410)
178 it "should not allow an expiration time longer than the maximum" do
179 upload :expire => 60 * 24 * 31 * 12 # 1 year
180 last_response.status.should eql(403)
183 it "should cleanup expired files" do
184 url = upload :expire => 60, :file_key => 'test' # 1 hour
185 url_name = url.split('/')[-1]
186 Dir.glob("#{Coquelicot.depot.path}/*").should have(1).items
188 Timecop.travel(Date.today + 1) do
190 files = Dir.glob("#{Coquelicot.depot.path}/*")
191 files.should have(1).items
192 File.lstat(files[0]).size.should eql(0)
193 Coquelicot.depot.get_file(url_name).expired?.should be_true
195 # let's be after 'gone' period
196 Timecop.travel(Time.now + (Coquelicot.settings.gone_period * 60)) do
198 Dir.glob("#{Coquelicot.depot.path}/*").should have(0).items
199 Coquelicot.depot.get_file(url_name).should be_nil
203 it "should map extra base32 characters to filenames" do
204 url = upload :expire => 60 # 1 hour
205 splitted = url.split('/')
206 name = splitted[-1].upcase.gsub(/O/, '0').gsub(/L/, '1')
207 get "#{splitted[0..-2].join '/'}/#{name}"
208 last_response.should be_ok
209 last_response['Content-Type'].should eql('text/x-script.ruby')