reshuffle code around
[coquelicot.git] / test_coquelicot.rb
1 $:.unshift File.join(File.dirname(__FILE__), '../rack-test/lib')
2 $:.unshift File.join(File.dirname(__FILE__), '../timecop/lib')
3
4 require 'sinatra'
5 require 'coquelicot_app'
6 require 'spec'
7 require 'rack/test'
8 require 'timecop'
9 require 'hpricot'
10 require 'tmpdir'
11
12 UPLOAD_PASSWORD = 'secret'
13
14 set :environment, :test
15 set :upload_password, Digest::SHA1.hexdigest(UPLOAD_PASSWORD)
16
17 describe 'Coquelicot' do
18   include Rack::Test::Methods
19
20   def app
21     Sinatra::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     Coquelicot.setup :depot_path => Dir.mktmpdir('coquelicot') #"#{Time.now.to_f}"
39   end
40
41   after do
42     FileUtils.remove_entry_secure Coquelicot.depot.path
43   end
44
45   it "should offer an upload form" do
46     get '/'
47     last_response.should be_ok
48     doc = Hpricot(last_response.body)
49     (doc/"form#upload").should have(1).items
50   end
51
52   it "should allow retrieval of an uploaded file" do
53     url = upload
54     get url
55     last_response.should be_ok
56     last_response['Content-Type'].should eql('text/x-script.ruby')
57     last_response.body.should eql(File.new(__FILE__).read)
58   end
59
60   it "should correctly set Last-Modified header when downloading" do
61     url = upload
62     get url
63     last_modified = last_response['Last-Modified']
64     last_modified.should_not be_nil
65     get url
66     last_response['Last-Modified'].should eql(last_modified)
67   end
68
69   it "should prevent upload without a password" do
70     url = upload :upload_password => ''
71     url.should be_nil
72     last_response.status.should eql(403)
73   end
74
75   it "should prevent upload with a wrong password" do
76     url = upload :upload_password => "bad"
77     url.should be_nil
78     last_response.status.should eql(403)
79   end
80
81   it "should not store an uploaded file in cleartext" do
82     upload
83     files = Dir.glob("#{Coquelicot.depot.path}/*")
84     files.should have(1).items
85     File.new(files[0]).read().should_not include('should not store an uploaded file')
86   end
87
88   it "should generate a random URL to retrieve a file" do
89     url = upload
90     url.should_not include(File.basename(__FILE__))
91   end
92
93   it "should store files with a different name than then one in URL" do
94     url = upload
95     url_name = url.split('/')[-1]
96     files = Dir.glob("#{Coquelicot.depot.path}/*")
97     files.should have(1).items
98     url_name.should_not eql(File.basename(files[0]))
99   end
100
101   it "should encode the encryption key in URL when no password has been specified" do
102     url = upload
103     url_name = url.split('/')[-1]
104     url_name.split('-').should have(2).items
105   end
106
107   it "should not encode the encryption key in URL when a password has been specified" do
108     url = upload :file_key => 'somethingSecret'
109     url_name = url.split('/')[-1]
110     url_name.split('-').should have(1).items
111   end
112
113   it "should only allow one time download to be retrieved once" do
114     url = upload :one_time => true
115     get url
116     last_response.should be_ok
117     last_response['Content-Type'].should eql('text/x-script.ruby')
118     last_response.body.should eql(File.new(__FILE__).read)
119     get url
120     last_response.status.should eql(410)
121   end
122
123   it "should allow retrieval of a password protected file" do
124     url = upload :file_key => 'somethingSecret'
125     get url
126     last_response.should be_ok
127     doc = Hpricot(last_response.body)
128     (doc/'input#file_key').should have(1).items
129     url = (doc/'form')[0].attributes['action']
130     post url, :file_key => 'somethingSecret'
131     last_response.should be_ok
132     last_response['Content-Type'].should eql('text/x-script.ruby')
133     last_response.body.should eql(File.new(__FILE__).read)
134   end
135
136   it "should not allow retrieval of a password protected file without the password" do
137     url = upload :file_key => 'somethingSecret'
138     get url
139     last_response.should be_ok
140     last_response['Content-Type'].should_not eql('text/x-script.ruby')
141     post url
142     last_response.status.should eql(403)
143   end
144
145   it "should not allow retrieval of a password protected file with a wrong password" do
146     url = upload :file_key => 'somethingSecret'
147     post url, :file_key => 'BAD'
148     last_response.status.should eql(403)
149   end
150
151   it "should not allow retrieval after the time limit has expired" do
152     url = upload :expire => 60 # 1 hour
153     # let's be tomorrow
154     Timecop.travel(Date.today + 1) do
155       get url
156       last_response.status.should eql(410)
157     end
158   end
159
160   it "should cleanup expired files" do
161     url = upload :expire => 60, :file_key => 'test' # 1 hour
162     url_name = url.split('/')[-1]
163     Dir.glob("#{Coquelicot.depot.path}/*").should have(1).items
164     # let's be tomorrow
165     Timecop.travel(Date.today + 1) do
166       Coquelicot.depot.gc!
167       files = Dir.glob("#{Coquelicot.depot.path}/*")
168       files.should have(1).items
169       File.lstat(files[0]).size.should eql(0)
170       Coquelicot.depot.get_file(url_name).expired?.should be_true
171     end
172     # let's be after 'gone' period
173     Timecop.travel(Time.now + (Coquelicot.settings.gone_period * 60)) do
174       Coquelicot.depot.gc!
175       Dir.glob("#{Coquelicot.depot.path}/*").should have(0).items
176       Coquelicot.depot.get_file(url_name).should be_nil
177     end
178   end
179
180   it "should map extra base32 characters to filenames" do
181     url = upload :expire => 60 # 1 hour
182     splitted = url.split('/')
183     name = splitted[-1].upcase.gsub(/O/, '0').gsub(/L/, '1')
184     get "#{splitted[0..-2].join '/'}/#{name}"
185     last_response.should be_ok
186     last_response['Content-Type'].should eql('text/x-script.ruby')
187   end
188 end