deas 0.38.0 → 0.39.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/Gemfile +2 -1
  2. data/{LICENSE.txt → LICENSE} +0 -0
  3. data/deas.gemspec +4 -3
  4. data/lib/deas/deas_runner.rb +17 -12
  5. data/lib/deas/handler_proxy.rb +14 -8
  6. data/lib/deas/redirect_proxy.rb +12 -12
  7. data/lib/deas/runner.rb +169 -19
  8. data/lib/deas/server.rb +14 -10
  9. data/lib/deas/template_source.rb +2 -2
  10. data/lib/deas/test_runner.rb +59 -60
  11. data/lib/deas/version.rb +1 -1
  12. data/lib/deas/view_handler.rb +55 -30
  13. data/test/support/empty_view_handler.rb +7 -0
  14. data/test/support/factory.rb +5 -0
  15. data/test/support/fake_request.rb +29 -0
  16. data/test/support/fake_sinatra_call.rb +11 -16
  17. data/test/support/file1.txt +1 -0
  18. data/test/support/file2.txt +1 -0
  19. data/test/support/routes.rb +7 -7
  20. data/test/support/show.html +0 -0
  21. data/test/support/show.json +0 -0
  22. data/test/system/{rack_tests.rb → deas_tests.rb} +19 -14
  23. data/test/unit/deas_runner_tests.rb +209 -20
  24. data/test/unit/handler_proxy_tests.rb +27 -19
  25. data/test/unit/redirect_proxy_tests.rb +32 -33
  26. data/test/unit/respond_with_proxy_tests.rb +1 -2
  27. data/test/unit/route_proxy_tests.rb +1 -1
  28. data/test/unit/route_tests.rb +1 -1
  29. data/test/unit/router_tests.rb +5 -5
  30. data/test/unit/runner_tests.rb +619 -76
  31. data/test/unit/server_tests.rb +6 -1
  32. data/test/unit/sinatra_app_tests.rb +1 -1
  33. data/test/unit/test_runner_tests.rb +377 -106
  34. data/test/unit/url_tests.rb +1 -1
  35. data/test/unit/view_handler_tests.rb +325 -182
  36. metadata +43 -24
  37. data/lib/deas/sinatra_runner.rb +0 -55
  38. data/lib/deas/test_helpers.rb +0 -23
  39. data/test/support/view_handlers.rb +0 -83
  40. data/test/unit/sinatra_runner_tests.rb +0 -79
  41. data/test/unit/test_helpers_tests.rb +0 -53
@@ -2,7 +2,6 @@ require 'assert'
2
2
  require 'deas/redirect_proxy'
3
3
 
4
4
  require 'deas/handler_proxy'
5
- require 'deas/test_helpers'
6
5
  require 'deas/url'
7
6
  require 'deas/view_handler'
8
7
 
@@ -24,7 +23,7 @@ class Deas::RedirectProxy
24
23
  end
25
24
 
26
25
  class HandlerClassTests < UnitTests
27
- include Deas::TestHelpers
26
+ include Deas::ViewHandler::TestHelpers
28
27
 
29
28
  desc "handler class"
30
29
  setup do
@@ -32,7 +31,7 @@ class Deas::RedirectProxy
32
31
  end
33
32
  subject{ @handler_class }
34
33
 
35
- should have_accessor :router, :redirect_path
34
+ should have_accessor :router, :redirect_location
36
35
  should have_imeth :name
37
36
 
38
37
  should "be a view handler" do
@@ -45,16 +44,16 @@ class Deas::RedirectProxy
45
44
  assert_equal @router, subject.router
46
45
  end
47
46
 
48
- should "store its redirect path as a proc" do
49
- assert_kind_of Proc, subject.redirect_path
47
+ should "store its redirect location as a proc" do
48
+ assert_kind_of Proc, subject.redirect_location
50
49
 
51
50
  url = Deas::Url.new(:some_thing, '/:some/:thing')
52
51
  handler_class = Deas::RedirectProxy.new(@router, url).handler_class
53
- assert_kind_of Proc, handler_class.redirect_path
52
+ assert_kind_of Proc, handler_class.redirect_location
54
53
 
55
- path_proc = proc{ '/somewhere' }
56
- handler_class = Deas::RedirectProxy.new(@router, &path_proc).handler_class
57
- assert_kind_of Proc, handler_class.redirect_path
54
+ location_proc = proc{ '/somewhere' }
55
+ handler_class = Deas::RedirectProxy.new(@router, &location_proc).handler_class
56
+ assert_kind_of Proc, handler_class.redirect_location
58
57
  end
59
58
 
60
59
  should "know its name" do
@@ -70,48 +69,48 @@ class Deas::RedirectProxy
70
69
  end
71
70
  subject{ @handler }
72
71
 
73
- should have_reader :redirect_path
72
+ should have_reader :redirect_location
74
73
 
75
- should "know its redir path if from a path string" do
76
- exp_path = '/somewhere'
77
- assert_equal exp_path, subject.redirect_path
74
+ should "know its redir location if from a location string" do
75
+ exp_location = '/somewhere'
76
+ assert_equal exp_location, subject.redirect_location
78
77
 
79
78
  @router.base_url(@base_url)
80
79
  handler = test_handler(@handler_class)
81
- exp = @router.prepend_base_url(exp_path)
82
- assert_equal exp, handler.redirect_path
80
+ exp = @router.prepend_base_url(exp_location)
81
+ assert_equal exp, handler.redirect_location
83
82
  end
84
83
 
85
- should "know its redir path if from Url" do
84
+ should "know its redir location if from Url" do
86
85
  url = Deas::Url.new(:some_thing, '/:some/:thing')
87
86
  handler_class = Deas::RedirectProxy.new(@router, url).handler_class
88
87
  handler = test_handler(handler_class, {
89
88
  :params => { 'some' => 'a', 'thing' => 'goose' }
90
89
  })
91
90
 
92
- exp_path = '/a/goose'
93
- assert_equal exp_path, handler.redirect_path
91
+ exp_location = '/a/goose'
92
+ assert_equal exp_location, handler.redirect_location
94
93
 
95
94
  @router.base_url(@base_url)
96
95
  handler = test_handler(handler_class, {
97
96
  :params => { 'some' => 'a', 'thing' => 'goose' }
98
97
  })
99
- exp = @router.prepend_base_url(exp_path)
100
- assert_equal exp, handler.redirect_path
98
+ exp = @router.prepend_base_url(exp_location)
99
+ assert_equal exp, handler.redirect_location
101
100
  end
102
101
 
103
- should "know its redir path if from a block" do
104
- path_proc = proc{ '/from-block-arg' }
105
- handler_class = Deas::RedirectProxy.new(@router, &path_proc).handler_class
102
+ should "know its redir location if from a block" do
103
+ location_proc = proc{ '/from-block-arg' }
104
+ handler_class = Deas::RedirectProxy.new(@router, &location_proc).handler_class
106
105
  handler = test_handler(handler_class)
107
106
 
108
- exp_path = '/from-block-arg'
109
- assert_equal exp_path , handler.redirect_path
107
+ exp_location = '/from-block-arg'
108
+ assert_equal exp_location , handler.redirect_location
110
109
 
111
110
  @router.base_url(@base_url)
112
111
  handler = test_handler(handler_class)
113
- exp = @router.prepend_base_url(exp_path)
114
- assert_equal exp, handler.redirect_path
112
+ exp = @router.prepend_base_url(exp_location)
113
+ assert_equal exp, handler.redirect_location
115
114
  end
116
115
 
117
116
  end
@@ -119,14 +118,14 @@ class Deas::RedirectProxy
119
118
  class RunTests < HandlerClassTests
120
119
  desc "when run"
121
120
  setup do
122
- @runner = test_runner(@handler_class)
123
- @handler = @runner.handler
124
- @render_args = @runner.run
121
+ @runner = test_runner(@handler_class)
122
+ @handler = @runner.handler
123
+ @response = @runner.run
125
124
  end
126
125
 
127
- should "redirect to the handler's redirect path" do
128
- assert @render_args.redirect?
129
- assert_equal @handler.redirect_path, @render_args.path
126
+ should "redirect to the handler's redirect location" do
127
+ assert_true @response.redirect?
128
+ assert_equal @handler.redirect_location, @response.location
130
129
  end
131
130
 
132
131
  end
@@ -2,7 +2,6 @@ require 'assert'
2
2
  require 'deas/respond_with_proxy'
3
3
 
4
4
  require 'deas/handler_proxy'
5
- require 'deas/test_helpers'
6
5
  require 'deas/url'
7
6
  require 'deas/view_handler'
8
7
 
@@ -25,7 +24,7 @@ class Deas::RespondWithProxy
25
24
  end
26
25
 
27
26
  class HandlerClassTests < UnitTests
28
- include Deas::TestHelpers
27
+ include Deas::ViewHandler::TestHelpers
29
28
 
30
29
  desc "handler class"
31
30
  setup do
@@ -3,7 +3,7 @@ require 'deas/route_proxy'
3
3
 
4
4
  require 'deas/exceptions'
5
5
  require 'deas/handler_proxy'
6
- require 'test/support/view_handlers'
6
+ require 'test/support/empty_view_handler'
7
7
 
8
8
  class Deas::RouteProxy
9
9
 
@@ -3,7 +3,7 @@ require 'deas/route'
3
3
 
4
4
  require 'deas/exceptions'
5
5
  require 'deas/route_proxy'
6
- require 'test/support/view_handlers'
6
+ require 'test/support/empty_view_handler'
7
7
 
8
8
  class Deas::Route
9
9
 
@@ -3,8 +3,8 @@ require 'deas/router'
3
3
 
4
4
  require 'deas/exceptions'
5
5
  require 'deas/route'
6
- require 'deas/test_helpers'
7
- require 'test/support/view_handlers'
6
+ require 'deas/view_handler'
7
+ require 'test/support/empty_view_handler'
8
8
 
9
9
  class Deas::Router
10
10
 
@@ -22,7 +22,7 @@ class Deas::Router
22
22
  end
23
23
 
24
24
  class InitTests < UnitTests
25
- include Deas::TestHelpers
25
+ include Deas::ViewHandler::TestHelpers
26
26
 
27
27
  desc "when init"
28
28
  setup do
@@ -223,7 +223,7 @@ class Deas::Router
223
223
  proxy = redirect.handler_proxies[subject.default_request_type_name]
224
224
  handler = test_handler(proxy.handler_class)
225
225
  exp = subject.prepend_base_url(path2)
226
- assert_equal exp, handler.redirect_path
226
+ assert_equal exp, handler.redirect_location
227
227
  end
228
228
 
229
229
  should "prepend the base url when adding not founds" do
@@ -343,7 +343,7 @@ class Deas::Router
343
343
 
344
344
  handler = test_handler(proxy.handler_class)
345
345
  exp = subject.prepend_base_url(@path2)
346
- assert_equal exp, handler.redirect_path
346
+ assert_equal exp, handler.redirect_location
347
347
  end
348
348
 
349
349
  should "add not found routes" do
@@ -1,57 +1,542 @@
1
1
  require 'assert'
2
2
  require 'deas/runner'
3
3
 
4
+ require 'rack/utils'
4
5
  require 'deas/logger'
5
6
  require 'deas/router'
6
- require 'test/support/view_handlers'
7
+ require 'deas/template_source'
8
+ require 'test/support/empty_view_handler'
7
9
 
8
10
  class Deas::Runner
9
11
 
10
12
  class UnitTests < Assert::Context
11
13
  desc "Deas::Runner"
12
14
  setup do
13
- @runner_class = Deas::Runner
15
+ @handler_class = EmptyViewHandler
16
+ @runner_class = Deas::Runner
14
17
  end
15
18
  subject{ @runner_class }
16
19
 
20
+ should "know its default mime type" do
21
+ assert_equal 'application/octet-stream', subject::DEFAULT_MIME_TYPE
22
+ end
23
+
24
+ should "know its default charset" do
25
+ assert_equal 'utf-8', subject::DEFAULT_CHARSET
26
+ end
27
+
28
+ should "know its default status" do
29
+ assert_equal 200, subject::DEFAULT_STATUS
30
+ end
31
+
32
+ should "know its default body" do
33
+ assert_equal [], subject::DEFAULT_BODY
34
+ end
35
+
17
36
  end
18
37
 
19
38
  class InitTests < UnitTests
20
39
  desc "when init"
21
40
  setup do
22
- @runner = @runner_class.new(EmptyViewHandler)
41
+ @request = Factory.request
42
+ @runner = @runner_class.new(@handler_class, :request => @request)
23
43
  end
24
44
  subject{ @runner }
25
45
 
26
46
  should have_readers :handler_class, :handler
27
- should have_readers :request, :response, :session
28
- should have_readers :params, :logger, :router, :template_source
29
- should have_imeths :halt, :redirect, :content_type, :status, :headers
47
+ should have_readers :logger, :router, :template_source
48
+ should have_readers :request, :session, :params
49
+ should have_imeths :run, :to_rack
50
+ should have_imeths :status, :headers, :body, :content_type
51
+ should have_imeths :halt, :redirect, :send_file
30
52
  should have_imeths :render, :source_render, :partial, :source_partial
31
- should have_imeths :send_file
32
53
 
33
54
  should "know its handler and handler class" do
34
- assert_equal EmptyViewHandler, subject.handler_class
55
+ assert_equal @handler_class, subject.handler_class
35
56
  assert_instance_of subject.handler_class, subject.handler
36
57
  end
37
58
 
38
- should "default its settings" do
39
- assert_nil subject.request
40
- assert_nil subject.response
41
- assert_nil subject.session
42
- assert_equal ::Hash.new, subject.params
43
- assert_kind_of Deas::NullLogger, subject.logger
44
- assert_kind_of Deas::Router, subject.router
45
- assert_kind_of Deas::NullTemplateSource, subject.template_source
59
+ should "default its attrs" do
60
+ runner = @runner_class.new(@handler_class)
61
+ assert_kind_of Deas::NullLogger, runner.logger
62
+ assert_kind_of Deas::Router, runner.router
63
+ assert_kind_of Deas::NullTemplateSource, runner.template_source
64
+
65
+ assert_nil runner.request
66
+ assert_nil runner.session
67
+
68
+ assert_equal({}, runner.params)
69
+ end
70
+
71
+ should "know its attrs" do
72
+ args = {
73
+ :logger => 'a-logger',
74
+ :router => 'a-router',
75
+ :template_source => 'a-source',
76
+ :request => 'a-request',
77
+ :session => 'a-session',
78
+ :params => {}
79
+ }
80
+
81
+ runner = @runner_class.new(@handler_class, args)
82
+
83
+ assert_equal args[:logger], runner.logger
84
+ assert_equal args[:router], runner.router
85
+ assert_equal args[:template_source], runner.template_source
86
+ assert_equal args[:request], runner.request
87
+ assert_equal args[:session], runner.session
88
+ assert_equal args[:params], runner.params
89
+ end
90
+
91
+ should "not implement its run method" do
92
+ assert_raises(NotImplementedError){ subject.run }
93
+ end
94
+
95
+ should "know its `to_rack` representation" do
96
+ exp = [
97
+ subject.class::DEFAULT_STATUS,
98
+ subject.headers.to_hash,
99
+ subject.class::DEFAULT_BODY
100
+ ]
101
+ assert_equal exp, subject.to_rack
102
+
103
+ status = Factory.integer
104
+ Assert.stub(subject, :status){ status }
105
+
106
+ headers = { Factory.string => Factory.string }
107
+ Assert.stub(subject, :headers){ headers }
108
+
109
+ body = [Factory.string]
110
+ Assert.stub(subject, :body){ body }
111
+
112
+ exp = [status, headers, body]
113
+ assert_equal exp, subject.to_rack
114
+ end
115
+
116
+ should "know and set its response status" do
117
+ assert_nil subject.status
118
+
119
+ exp = Factory.integer
120
+ subject.status exp
121
+ assert_equal exp, subject.status
122
+ end
123
+
124
+ should "know and merge values on its response headers" do
125
+ assert_kind_of Rack::Utils::HeaderHash, subject.headers
126
+ assert_equal({}, subject.headers)
127
+
128
+ new_header_values = { Factory.string => Factory.string }
129
+ subject.headers(new_header_values)
130
+ assert_kind_of Rack::Utils::HeaderHash, subject.headers
131
+ assert_equal new_header_values, subject.headers
132
+
133
+ location = Factory.string
134
+ subject.headers['Location'] = location
135
+ exp = new_header_values.merge('Location' => location)
136
+ assert_equal exp, subject.headers
137
+ end
138
+
139
+ should "know and set its response body" do
140
+ assert_nil subject.body
141
+
142
+ exp = Factory.string
143
+ subject.body exp
144
+ assert_equal exp, subject.body
145
+
146
+ assert_raises(ArgumentError) do
147
+ subject.body Factory.integer
148
+ end
149
+ end
150
+
151
+ should "know and set its response content type header" do
152
+ extname = ".#{Factory.string}"
153
+
154
+ subject.content_type(extname) # unknown mime type extname
155
+ exp = subject.class::DEFAULT_MIME_TYPE
156
+ assert_equal exp, subject.headers['Content-Type']
157
+
158
+ mime_type = "#{Factory.string}/#{Factory.string}"
159
+ Assert.stub(Rack::Mime, :mime_type).with(
160
+ extname,
161
+ subject.class::DEFAULT_MIME_TYPE
162
+ ){ mime_type }
163
+ subject.content_type(extname) # known mime type extname
164
+ assert_equal mime_type, subject.headers['Content-Type']
165
+
166
+ params = {
167
+ Factory.string => Factory.string,
168
+ Factory.string => Factory.string
169
+ }
170
+ subject.content_type(extname, params)
171
+ exp = "#{mime_type};#{params.map{ |k,v| k + '=' + v }.join(',')}"
172
+ assert_equal exp, subject.headers['Content-Type']
173
+ end
174
+
175
+ end
176
+
177
+ class HaltTests < InitTests
178
+ desc "the `halt` method"
179
+ setup do
180
+ @status = Factory.integer
181
+ @headers = { Factory.string => Factory.string }
182
+ @body = [Factory.string]
183
+ end
184
+
185
+ should "set response attrs and halt execution" do
186
+ runner = runner_halted_with()
187
+ assert_nil runner.status
188
+ assert_equal({}, runner.headers)
189
+ assert_nil runner.body
190
+
191
+ runner = runner_halted_with(@status)
192
+ assert_equal @status, runner.status
193
+ assert_equal({}, runner.headers)
194
+ assert_nil runner.body
195
+
196
+ runner = runner_halted_with(@headers)
197
+ assert_nil runner.status
198
+ assert_equal @headers, runner.headers
199
+ assert_nil runner.body
200
+
201
+ runner = runner_halted_with(@body)
202
+ assert_nil runner.status
203
+ assert_equal({}, runner.headers)
204
+ assert_equal @body, runner.body
205
+
206
+ runner = runner_halted_with(@status, @headers)
207
+ assert_equal @status, runner.status
208
+ assert_equal @headers, runner.headers
209
+ assert_nil runner.body
210
+
211
+ runner = runner_halted_with(@status, @body)
212
+ assert_equal @status, runner.status
213
+ assert_equal({}, runner.headers)
214
+ assert_equal @body, runner.body
215
+
216
+ runner = runner_halted_with(@headers, @body)
217
+ assert_nil runner.status
218
+ assert_equal @headers, runner.headers
219
+ assert_equal @body, runner.body
220
+
221
+ runner = runner_halted_with(@status, @headers, @body)
222
+ assert_equal @status, runner.status
223
+ assert_equal @headers, runner.headers
224
+ assert_equal @body, runner.body
225
+ end
226
+
227
+ private
228
+
229
+ def runner_halted_with(*halt_args)
230
+ @runner_class.new(@handler_class).tap do |runner|
231
+ catch(:halt){ runner.halt(*halt_args) }
232
+ end
233
+ end
234
+
235
+ end
236
+
237
+ class HaltCalledWithTests < InitTests
238
+ setup do
239
+ @halt_called_with = nil
240
+ Assert.stub(@runner, :halt){ |*args| @halt_called_with = args; throw :halt }
241
+ end
242
+
243
+ end
244
+
245
+ class RedirectTests < HaltCalledWithTests
246
+ desc "the `redirect` method"
247
+ setup do
248
+ @location = Factory.boolean ? Factory.string : "/#{Factory.string}"
249
+ end
250
+
251
+ should "set response attrs and halt execution" do
252
+ catch(:halt){ subject.redirect(@location) }
253
+
254
+ assert_equal 302, subject.status
255
+
256
+ exp = get_absolute_url(@location)
257
+ assert_equal exp, subject.headers['Location']
258
+
259
+ assert_nil subject.body
260
+ assert_equal [], @halt_called_with
261
+ end
262
+
263
+ should "set response attrs and halt execution if called with halt args" do
264
+ halt_args = Factory.integer(3).times.map{ Factory.string }
265
+ catch(:halt){ subject.redirect(@location, *halt_args) }
266
+
267
+ assert_equal 302, subject.status
268
+
269
+ exp = get_absolute_url(@location)
270
+ assert_equal exp, subject.headers['Location']
271
+
272
+ assert_nil subject.body
273
+ assert_equal halt_args, @halt_called_with
274
+ end
275
+
276
+ private
277
+
278
+ def get_absolute_url(url)
279
+ File.join("#{@request.env['rack.url_scheme']}://#{@request.env['HTTP_HOST']}", url)
280
+ end
281
+
282
+ end
283
+
284
+ class SendFileSetupTests < HaltCalledWithTests
285
+ desc "the `send_file` method"
286
+
287
+ end
288
+
289
+ class NotFoundSendFileTests < SendFileSetupTests
290
+ desc "called with a file path that doesn't exist"
291
+ setup do
292
+ @not_found_path = Factory.path
293
+ end
294
+
295
+ should "halt 404" do
296
+ catch(:halt){ subject.send_file(@not_found_path) }
297
+ assert_equal [404, []], @halt_called_with
298
+ end
299
+
300
+ end
301
+
302
+ class ExistingFileSendFileTests < SendFileSetupTests
303
+ setup do
304
+ @file_path = TEST_SUPPORT_ROOT.join('file1.txt')
305
+ @path_name = Pathname.new(@file_path)
306
+ end
307
+
308
+ end
309
+
310
+ class NotModifiedSendFileTests < ExistingFileSendFileTests
311
+ desc "called with a file path that isn't modified"
312
+ setup do
313
+ @request.env['HTTP_IF_MODIFIED_SINCE'] = @path_name.mtime.httpdate.to_s
314
+ end
315
+
316
+ should "halt 304" do
317
+ catch(:halt){ subject.send_file(@file_path) }
318
+ assert_equal [304, []], @halt_called_with
319
+ end
320
+
321
+ end
322
+
323
+ class ModifiedSendFileTests < ExistingFileSendFileTests
324
+ desc "called with a modified file path"
325
+ setup do
326
+ path_name = @path_name
327
+ @file_content_type = @runner.instance_eval{ get_content_type(path_name.extname) }
328
+ @send_file_body = SendFileBody.new(@request.env, @path_name)
46
329
  end
47
330
 
48
- should "not implement its non-rendering actions" do
49
- assert_raises(NotImplementedError){ subject.halt }
50
- assert_raises(NotImplementedError){ subject.redirect }
51
- assert_raises(NotImplementedError){ subject.content_type }
52
- assert_raises(NotImplementedError){ subject.status }
53
- assert_raises(NotImplementedError){ subject.headers }
54
- assert_raises(NotImplementedError){ subject.send_file }
331
+ should "halt 200 with the proper headers and body" do
332
+ catch(:halt){ subject.send_file(@file_path) }
333
+
334
+ assert_equal [], @halt_called_with
335
+ assert_equal 200, subject.status
336
+
337
+ exp = {
338
+ 'Last-Modified' => @path_name.mtime.httpdate.to_s,
339
+ 'Content-Type' => @file_content_type,
340
+ 'Content-Length' => @send_file_body.size.to_s
341
+ }
342
+ assert_equal exp, subject.headers
343
+
344
+ assert_equal @send_file_body, subject.body
345
+ end
346
+
347
+ should "only update the content type header if it is not already set" do
348
+ custom_content_type = Factory.string
349
+ subject.headers['Content-Type'] = custom_content_type
350
+
351
+ catch(:halt){ subject.send_file(@file_path) }
352
+ assert_equal custom_content_type, subject.headers['Content-Type']
353
+ end
354
+
355
+ should "only update the content length header if it is not already set" do
356
+ custom_content_length = Factory.integer.to_s
357
+ subject.headers['Content-Length'] = custom_content_length
358
+
359
+ catch(:halt){ subject.send_file(@file_path) }
360
+ assert_equal custom_content_length, subject.headers['Content-Length']
361
+ end
362
+
363
+ should "halt with the proper header if the `:disposition` option is given" do
364
+ disposition = Factory.string
365
+ catch(:halt){ subject.send_file(@file_path, :disposition => disposition) }
366
+
367
+ exp = "#{disposition};filename=\"#{@path_name.basename}\""
368
+ assert_equal exp, subject.headers['Content-Disposition']
369
+ end
370
+
371
+ should "halt with the proper header if the `:filename` option is given" do
372
+ filename = Factory.string
373
+ catch(:halt){ subject.send_file(@file_path, :filename => filename) }
374
+
375
+ exp = "attachment;filename=\"#{filename}\""
376
+ assert_equal exp, subject.headers['Content-Disposition']
377
+ end
378
+
379
+ should "halt with the proper header if the `:disposition` and `:filename` options are given" do
380
+ disposition = Factory.string
381
+ filename = Factory.string
382
+ catch(:halt) do
383
+ subject.send_file(@file_path, {
384
+ :disposition => disposition,
385
+ :filename => filename
386
+ })
387
+ end
388
+
389
+ exp = "#{disposition};filename=\"#{filename}\""
390
+ assert_equal exp, subject.headers['Content-Disposition']
391
+ end
392
+
393
+ should "only update the content dispostion header if it is not already set" do
394
+ custom_disposition = Factory.string
395
+ subject.headers['Content-Disposition'] = custom_disposition
396
+
397
+ catch(:halt) do
398
+ subject.send_file(@file_path, {
399
+ :disposition => Factory.string,
400
+ :filename => Factory.string
401
+ })
402
+ end
403
+ assert_equal custom_disposition, subject.headers['Content-Disposition']
404
+ end
405
+
406
+ end
407
+
408
+ class PartialSendFileTests < ModifiedSendFileTests
409
+ desc "for a partial response"
410
+ setup do
411
+ Assert.stub(@send_file_body, :partial?){ true }
412
+ @content_range = Factory.string
413
+ Assert.stub(@send_file_body, :content_range){ @content_range }
414
+ Assert.stub(SendFileBody, :new){ @send_file_body }
415
+ end
416
+
417
+ should "halt 206 with the proper headers and body for partial requests" do
418
+ catch(:halt){ subject.send_file(@file_path) }
419
+
420
+ assert_equal [], @halt_called_with
421
+ assert_equal 206, subject.status
422
+ assert_equal @content_range, subject.headers['Content-Range']
423
+ assert_equal @send_file_body, subject.body
424
+ end
425
+
426
+ should "only update the content range header if it is not already set" do
427
+ custom_range = Factory.string
428
+ subject.headers['Content-Range'] = custom_range
429
+
430
+ catch(:halt){ subject.send_file(@file_path) }
431
+ assert_equal custom_range, subject.headers['Content-Range']
432
+ end
433
+
434
+ end
435
+
436
+ class RenderSetupTests < InitTests
437
+ setup do
438
+ @template_name = Factory.path
439
+ @locals = { Factory.string => Factory.string }
440
+ end
441
+
442
+ end
443
+
444
+ class RenderTests < RenderSetupTests
445
+ desc "render method"
446
+ setup do
447
+ @source_render_called_with = nil
448
+ Assert.stub(@runner, :source_render){ |*args| @source_render_called_with = args }
449
+ end
450
+
451
+ should "call to its `source_render` method with its template source" do
452
+ subject.render(@template_name, @locals)
453
+ exp = [subject.template_source, @template_name, @locals]
454
+ assert_equal exp, @source_render_called_with
455
+
456
+ subject.render(@template_name)
457
+ exp = [subject.template_source, @template_name, nil]
458
+ assert_equal exp, @source_render_called_with
459
+ end
460
+
461
+ end
462
+
463
+ class SourceRenderTests < RenderSetupTests
464
+ desc "source render method"
465
+ setup do
466
+ body = @body = Factory.text
467
+ @render_called_with = nil
468
+ @source = Deas::TemplateSource.new(Factory.path)
469
+ Assert.stub(@source, :render){ |*args| @render_called_with = args; body }
470
+ end
471
+
472
+ should "call to the given source's render method and set the return value as the body" do
473
+ subject.source_render(@source, @template_name, @locals)
474
+
475
+ exp = [@template_name, subject.handler, @locals]
476
+ assert_equal exp, @render_called_with
477
+
478
+ template_name = @template_name
479
+ exp = subject.instance_eval do
480
+ get_content_type(File.extname(template_name), 'charset' => DEFAULT_CHARSET)
481
+ end
482
+ assert_equal exp, subject.headers['Content-Type']
483
+
484
+ assert_equal @body, subject.body
485
+ end
486
+
487
+ should "default the locals if none given" do
488
+ subject.source_render(@source, @template_name)
489
+ exp = [@template_name, subject.handler, {}]
490
+ assert_equal exp, @render_called_with
491
+ end
492
+
493
+ should "only update the content type header if it is not already set" do
494
+ custom_content_type = Factory.string
495
+ subject.headers['Content-Type'] = custom_content_type
496
+
497
+ subject.source_render(@source, @template_name, @locals)
498
+ assert_equal custom_content_type, subject.headers['Content-Type']
499
+ end
500
+
501
+ end
502
+
503
+ class PartialTests < RenderSetupTests
504
+ desc "partial method"
505
+ setup do
506
+ @source_partial_called_with = nil
507
+ Assert.stub(@runner, :source_partial){ |*args| @source_partial_called_with = args }
508
+ end
509
+
510
+ should "call to its `source_partial` method with its template source" do
511
+ subject.partial(@template_name, @locals)
512
+ exp = [subject.template_source, @template_name, @locals]
513
+ assert_equal exp, @source_partial_called_with
514
+
515
+ subject.partial(@template_name)
516
+ exp = [subject.template_source, @template_name, nil]
517
+ assert_equal exp, @source_partial_called_with
518
+ end
519
+
520
+ end
521
+
522
+ class SourcePartialTests < RenderSetupTests
523
+ desc "source partial method"
524
+ setup do
525
+ @partial_called_with = nil
526
+ @source = Deas::TemplateSource.new(Factory.path)
527
+ Assert.stub(@source, :partial){ |*args| @partial_called_with = args }
528
+ end
529
+
530
+ should "call to the given source's partial method" do
531
+ subject.source_partial(@source, @template_name, @locals)
532
+ exp = [@template_name, @locals]
533
+ assert_equal exp, @partial_called_with
534
+ end
535
+
536
+ should "default the locals if none given" do
537
+ subject.source_partial(@source, @template_name)
538
+ exp = [@template_name, {}]
539
+ assert_equal exp, @partial_called_with
55
540
  end
56
541
 
57
542
  end
@@ -121,88 +606,146 @@ class Deas::Runner
121
606
 
122
607
  end
123
608
 
124
- class RenderSetupTests < InitTests
609
+ class SendFileBodyTests < UnitTests
610
+ desc "SendFileBody"
125
611
  setup do
126
- @template_name = Factory.path
127
- @locals = { Factory.string => Factory.string }
612
+ @env = Factory.request.env
613
+ @path_name = Pathname.new(TEST_SUPPORT_ROOT.join('file1.txt'))
614
+
615
+ @body = SendFileBody.new(@env, @path_name)
128
616
  end
617
+ subject{ @body }
129
618
 
130
- end
619
+ should have_readers :path_name, :size, :content_range
620
+ should have_imeths :partial?, :range_begin, :range_end
621
+ should have_imeths :each
131
622
 
132
- class RenderTests < RenderSetupTests
133
- desc "render method"
134
- setup do
135
- @render_args = nil
136
- Assert.stub(@runner.template_source, :render){ |*args| @render_args = args }
623
+ should "know its chunk size" do
624
+ assert_equal 8192, SendFileBody::CHUNK_SIZE
137
625
  end
138
626
 
139
- should "call to its template source render method" do
140
- subject.render(@template_name, @locals)
141
- exp = [@template_name, subject.handler, @locals]
142
- assert_equal exp, @render_args
627
+ should "know its path name" do
628
+ assert_equal @path_name, subject.path_name
629
+ end
143
630
 
144
- subject.render(@template_name)
145
- exp = [@template_name, subject.handler, {}]
146
- assert_equal exp, @render_args
631
+ should "know if it is equal to another body" do
632
+ same_path_same_range = SendFileBody.new(@env, @path_name)
633
+ Assert.stub(same_path_same_range, :range_begin){ subject.range_begin }
634
+ Assert.stub(same_path_same_range, :range_end){ subject.range_end }
635
+ assert_equal same_path_same_range, subject
636
+
637
+ other_path = Pathname.new(TEST_SUPPORT_ROOT.join('file2.txt'))
638
+ other_path_same_range = SendFileBody.new(@env, other_path)
639
+ Assert.stub(other_path_same_range, :range_begin){ subject.range_begin }
640
+ Assert.stub(other_path_same_range, :range_end){ subject.range_end }
641
+ assert_not_equal other_path_same_range, subject
642
+
643
+ same_path_other_range = SendFileBody.new(@env, @path_name)
644
+
645
+ Assert.stub(same_path_other_range, :range_begin){ Factory.integer }
646
+ Assert.stub(same_path_other_range, :range_end){ subject.range_end }
647
+ assert_not_equal same_path_other_range, subject
648
+
649
+ Assert.stub(same_path_other_range, :range_begin){ subject.range_begin }
650
+ Assert.stub(same_path_other_range, :range_end){ Factory.integer }
651
+ assert_not_equal same_path_other_range, subject
147
652
  end
148
653
 
149
654
  end
150
655
 
151
- class SourceRenderTests < RenderSetupTests
152
- desc "source render method"
656
+ class SendFileBodyIOTests < SendFileBodyTests
153
657
  setup do
154
- @source_render_args = nil
155
- @source = Deas::TemplateSource.new(Factory.path)
156
- Assert.stub(@source, :render){ |*args| @source_render_args = args }
157
- end
158
-
159
- should "call to the given source's render method" do
160
- subject.source_render(@source, @template_name, @locals)
161
- exp = [@template_name, subject.handler, @locals]
162
- assert_equal exp, @source_render_args
658
+ @min_num_chunks = 3
659
+ @num_chunks = @min_num_chunks + Factory.integer(3)
660
+ @path_name = Pathname.new(TEST_SUPPORT_ROOT.join('file3.txt'))
163
661
 
164
- subject.source_render(@source, @template_name)
165
- exp = [@template_name, subject.handler, {}]
166
- assert_equal exp, @source_render_args
662
+ @path_name.open('w') do |io|
663
+ io.write('a' * (@num_chunks * SendFileBody::CHUNK_SIZE))
664
+ end
665
+ end
666
+ teardown do
667
+ @path_name.delete
167
668
  end
168
669
 
169
670
  end
170
671
 
171
- class PartialTests < RenderSetupTests
172
- desc "partial method"
672
+ class NonPartialSendFileBodyTests < SendFileBodyIOTests
673
+ desc "for non/multi/invalid partial content requests"
173
674
  setup do
174
- @partial_args = nil
175
- Assert.stub(@runner.template_source, :partial){ |*args| @partial_args = args }
675
+ range = [nil, 'bytes=', 'bytes=0-1,2-3', 'bytes=3-2', 'bytes=abc'].choice
676
+ env = range.nil? ? {} : { 'HTTP_RANGE' => range }
677
+ @body = SendFileBody.new(env, @path_name)
176
678
  end
177
679
 
178
- should "call to its template source partial method" do
179
- subject.partial(@template_name, @locals)
180
- exp = [@template_name, @locals]
181
- assert_equal exp, @partial_args
680
+ should "not be partial" do
681
+ assert_false subject.partial?
682
+ end
182
683
 
183
- subject.partial(@template_name)
184
- exp = [@template_name, {}]
185
- assert_equal exp, @partial_args
684
+ should "be the full content size" do
685
+ assert_equal @path_name.size, subject.size
686
+ end
687
+
688
+ should "have no content range" do
689
+ assert_nil subject.content_range
690
+ end
691
+
692
+ should "have the full content size as its range" do
693
+ assert_equal 0, subject.range_begin
694
+ assert_equal subject.size-1, subject.range_end
695
+ end
696
+
697
+ should "chunk the full content when iterated" do
698
+ chunks = []
699
+ subject.each{ |chunk| chunks << chunk }
700
+
701
+ assert_equal @num_chunks, chunks.size
702
+ assert_equal subject.class::CHUNK_SIZE, chunks.first.size
703
+ assert_equal @path_name.read, chunks.join('')
186
704
  end
187
705
 
188
706
  end
189
707
 
190
- class SourcePartialTests < RenderSetupTests
191
- desc "source partial method"
708
+ class PartialSendFileBodyTests < SendFileBodyIOTests
709
+ desc "for a partial content request"
192
710
  setup do
193
- @source_partial_args = nil
194
- @source = Deas::TemplateSource.new(Factory.path)
195
- Assert.stub(@source, :partial){ |*args| @source_partial_args = args }
711
+ @start_chunk = Factory.boolean ? 0 : 1
712
+ @partial_begin = @start_chunk * SendFileBody::CHUNK_SIZE
713
+ @partial_chunks = @num_chunks - Factory.integer(@min_num_chunks)
714
+ @partial_size = @partial_chunks * SendFileBody::CHUNK_SIZE
715
+ @partial_end = @partial_begin + (@partial_size-1)
716
+
717
+ env = { 'HTTP_RANGE' => "bytes=#{@partial_begin}-#{@partial_end}" }
718
+ @body = SendFileBody.new(env, @path_name)
196
719
  end
720
+ subject{ @body }
197
721
 
198
- should "call to the given source's partial method" do
199
- subject.source_partial(@source, @template_name, @locals)
200
- exp = [@template_name, @locals]
201
- assert_equal exp, @source_partial_args
722
+ should "be partial" do
723
+ assert_true subject.partial?
724
+ end
202
725
 
203
- subject.source_partial(@source, @template_name)
204
- exp = [@template_name, {}]
205
- assert_equal exp, @source_partial_args
726
+ should "be the specified partial size" do
727
+ assert_equal @partial_size, subject.size
728
+ end
729
+
730
+ should "know its content range" do
731
+ exp = "bytes #{@partial_begin}-#{@partial_end}/#{@path_name.size}"
732
+ assert_equal exp, subject.content_range
733
+ end
734
+
735
+ should "know its range" do
736
+ assert_equal @partial_begin, subject.range_begin
737
+ assert_equal @partial_end, subject.range_end
738
+ end
739
+
740
+ should "chunk the range when iterated" do
741
+ chunks = []
742
+ subject.each{ |chunk| chunks << chunk }
743
+
744
+ assert_equal @partial_chunks, chunks.size
745
+ assert_equal subject.class::CHUNK_SIZE, chunks.first.size
746
+
747
+ exp = @path_name.read[@partial_begin..@partial_end]
748
+ assert_equal exp, chunks.join('')
206
749
  end
207
750
 
208
751
  end