webmachine 1.5.0 → 2.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +4 -5
  4. data/documentation/adapters.md +2 -11
  5. data/documentation/examples.md +12 -6
  6. data/examples/debugger.rb +5 -3
  7. data/examples/logging.rb +2 -2
  8. data/examples/webrick.rb +2 -2
  9. data/lib/webmachine/adapter.rb +0 -3
  10. data/lib/webmachine/adapters/lazy_request_body.rb +1 -1
  11. data/lib/webmachine/adapters/rack.rb +48 -34
  12. data/lib/webmachine/adapters/rack_mapped.rb +1 -2
  13. data/lib/webmachine/adapters/webrick.rb +11 -12
  14. data/lib/webmachine/adapters.rb +0 -2
  15. data/lib/webmachine/application.rb +2 -2
  16. data/lib/webmachine/chunked_body.rb +1 -1
  17. data/lib/webmachine/configuration.rb +1 -1
  18. data/lib/webmachine/constants.rb +14 -14
  19. data/lib/webmachine/cookie.rb +20 -18
  20. data/lib/webmachine/decision/conneg.rb +26 -26
  21. data/lib/webmachine/decision/falsey.rb +0 -1
  22. data/lib/webmachine/decision/flow.rb +19 -20
  23. data/lib/webmachine/decision/fsm.rb +4 -4
  24. data/lib/webmachine/decision/helpers.rb +21 -21
  25. data/lib/webmachine/dispatcher/route.rb +30 -16
  26. data/lib/webmachine/dispatcher.rb +3 -2
  27. data/lib/webmachine/errors.rb +7 -8
  28. data/lib/webmachine/etags.rb +2 -1
  29. data/lib/webmachine/header_negotiation.rb +5 -6
  30. data/lib/webmachine/headers.rb +5 -5
  31. data/lib/webmachine/media_type.rb +5 -5
  32. data/lib/webmachine/quoted_string.rb +3 -3
  33. data/lib/webmachine/request.rb +7 -10
  34. data/lib/webmachine/rescueable_exception.rb +3 -3
  35. data/lib/webmachine/resource/authentication.rb +3 -4
  36. data/lib/webmachine/resource/callbacks.rb +3 -3
  37. data/lib/webmachine/resource/encodings.rb +3 -9
  38. data/lib/webmachine/resource.rb +1 -1
  39. data/lib/webmachine/response.rb +7 -9
  40. data/lib/webmachine/spec/adapter_lint.rb +67 -69
  41. data/lib/webmachine/spec/test_resource.rb +26 -21
  42. data/lib/webmachine/streaming/encoder.rb +3 -2
  43. data/lib/webmachine/streaming/io_encoder.rb +4 -3
  44. data/lib/webmachine/trace/fsm.rb +25 -18
  45. data/lib/webmachine/trace/resource_proxy.rb +10 -9
  46. data/lib/webmachine/trace/static/http-headers-status-v3.png +0 -0
  47. data/lib/webmachine/trace/trace_resource.rb +22 -24
  48. data/lib/webmachine/trace.rb +7 -6
  49. data/lib/webmachine/translation.rb +3 -3
  50. data/lib/webmachine/version.rb +1 -1
  51. metadata +55 -90
  52. data/.gitignore +0 -31
  53. data/Gemfile +0 -44
  54. data/Guardfile +0 -11
  55. data/RELEASING.md +0 -21
  56. data/Rakefile +0 -44
  57. data/bethtemp +0 -3
  58. data/lib/webmachine/adapters/httpkit.rb +0 -74
  59. data/lib/webmachine/adapters/reel.rb +0 -113
  60. data/memory_test.rb +0 -37
  61. data/pkg/webmachine-1.5.0.gem +0 -0
  62. data/spec/spec_helper.rb +0 -56
  63. data/spec/webmachine/adapter_spec.rb +0 -39
  64. data/spec/webmachine/adapters/httpkit_spec.rb +0 -10
  65. data/spec/webmachine/adapters/rack_mapped_spec.rb +0 -71
  66. data/spec/webmachine/adapters/rack_spec.rb +0 -57
  67. data/spec/webmachine/adapters/reel_spec.rb +0 -76
  68. data/spec/webmachine/adapters/webrick_spec.rb +0 -12
  69. data/spec/webmachine/application_spec.rb +0 -74
  70. data/spec/webmachine/chunked_body_spec.rb +0 -30
  71. data/spec/webmachine/configuration_spec.rb +0 -27
  72. data/spec/webmachine/cookie_spec.rb +0 -99
  73. data/spec/webmachine/decision/conneg_spec.rb +0 -166
  74. data/spec/webmachine/decision/falsey_spec.rb +0 -8
  75. data/spec/webmachine/decision/flow_spec.rb +0 -1148
  76. data/spec/webmachine/decision/fsm_spec.rb +0 -163
  77. data/spec/webmachine/decision/helpers_spec.rb +0 -216
  78. data/spec/webmachine/dispatcher/route_spec.rb +0 -248
  79. data/spec/webmachine/dispatcher_spec.rb +0 -104
  80. data/spec/webmachine/errors_spec.rb +0 -13
  81. data/spec/webmachine/etags_spec.rb +0 -75
  82. data/spec/webmachine/events_spec.rb +0 -58
  83. data/spec/webmachine/headers_spec.rb +0 -99
  84. data/spec/webmachine/media_type_spec.rb +0 -85
  85. data/spec/webmachine/request_spec.rb +0 -273
  86. data/spec/webmachine/rescueable_exception_spec.rb +0 -15
  87. data/spec/webmachine/resource/authentication_spec.rb +0 -68
  88. data/spec/webmachine/response_spec.rb +0 -51
  89. data/spec/webmachine/trace/fsm_spec.rb +0 -37
  90. data/spec/webmachine/trace/resource_proxy_spec.rb +0 -34
  91. data/spec/webmachine/trace/trace_store_spec.rb +0 -29
  92. data/spec/webmachine/trace_spec.rb +0 -17
  93. data/webmachine.gemspec +0 -25
@@ -1,1148 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Webmachine::Decision::Flow do
4
- subject { Webmachine::Decision::FSM.new(resource, request, response) }
5
- let(:method) { 'GET' }
6
- let(:uri) { URI.parse("http://localhost/") }
7
- let(:headers) { Webmachine::Headers.new }
8
- let(:body) { "" }
9
- let(:request) { Webmachine::Request.new(method, uri, headers, body) }
10
- let(:response) { Webmachine::Response.new }
11
- let(:default_resource) { resource_with }
12
- let(:missing_resource) { missing_resource_with }
13
-
14
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18:
15
- # Origin servers MUST include a Date header field in all responses
16
- # ... [except 1xx or 5xx]
17
- after(:each) do
18
- unless response.code < 200 || response.code >= 500
19
- expect(response.headers).to have_key('Date')
20
- end
21
- end
22
-
23
- def resource_with(&block)
24
- klass = Class.new(Webmachine::Resource) do
25
- def to_html; "test resource"; end
26
- end
27
- klass.module_eval(&block) if block_given?
28
- klass.new(request, response)
29
- end
30
-
31
- def missing_resource_with(&block)
32
- resource_with do
33
- def resource_exists?; false; end
34
- self.module_eval(&block) if block
35
- end
36
- end
37
-
38
- describe "#b13 (Service Available?)" do
39
- let(:resource) do
40
- resource_with do
41
- attr_accessor :available
42
- def service_available?; @available; end
43
- end
44
- end
45
-
46
- it "should respond with 503 when the service is unavailable" do
47
- resource.available = false
48
- subject.run
49
- expect(response.code).to eq 503
50
- end
51
- end
52
-
53
- describe "#b12 (Known method?)" do
54
- let(:resource) do
55
- resource_with do
56
- def known_methods; ['HEAD']; end
57
- end
58
- end
59
-
60
- it "should respond with 501 when the method is unknown" do
61
- subject.run
62
- expect(response.code).to eq 501
63
- end
64
- end
65
-
66
- describe "#b11 (URI too long?)" do
67
- let(:resource) do
68
- resource_with do
69
- def uri_too_long?(uri); true; end
70
- end
71
- end
72
-
73
- it "should respond with 414 when the URI is too long" do
74
- subject.run
75
- expect(response.code).to eq 414
76
- end
77
- end
78
-
79
- describe "#b10 (Method allowed?)" do
80
- let(:resource) do
81
- resource_with do
82
- def allowed_methods; ['POST']; end
83
- end
84
- end
85
-
86
- it "should respond with 405 when the method is not allowed" do
87
- subject.run
88
- expect(response.code).to eq 405
89
- expect(response.headers['Allow']).to eq "POST"
90
- end
91
- end
92
-
93
- describe "#b9 (Malformed request?)" do
94
- let(:resource) { resource_with { def malformed_request?; true; end } }
95
-
96
- it "should respond with 400 when the request is malformed" do
97
- subject.run
98
- expect(response.code).to eq 400
99
- end
100
-
101
- context "when the Content-MD5 header is present" do
102
- let(:resource) do
103
- resource_with do
104
- def allowed_methods; ['POST']; end;
105
- def process_post; true; end;
106
- attr_accessor :validation
107
- def validate_content_checksum; @validation; end
108
- end
109
- end
110
-
111
- let(:method) { "POST" }
112
- let(:body) { "This is the body." }
113
- let(:headers) { Webmachine::Headers["Content-Type" => "text/plain"] }
114
-
115
- it "should respond with 204 when the request body does match the header" do
116
- headers['Content-MD5'] = Base64.encode64 Digest::MD5.hexdigest(body)
117
- subject.run
118
- expect(response.code).to eq 204
119
- end
120
-
121
- it "should bypass validation when the header has a nil value" do
122
- headers['Content-MD5'] = nil
123
- subject.run
124
- expect(response.code).to eq 204
125
- end
126
-
127
- it "should respond with 400 when the header has a empty string value" do
128
- headers['Content-MD5'] = ""
129
- subject.run
130
- expect(response.code).to eq 400
131
- end
132
-
133
- it "should respond with 400 when the header has a non-hashed, non-encoded value" do
134
- headers["Content-MD5"] = body
135
- subject.run
136
- expect(response.code).to eq 400
137
- end
138
-
139
- it "should respond with 400 when the header is not encoded as Base64 but digest matches the body" do
140
- headers['Content-MD5'] = Digest::MD5.hexdigest(body)
141
- subject.run
142
- expect(response.code).to eq 400
143
- end
144
-
145
- it "should respond with 400 when the request body does not match the header" do
146
- headers['Content-MD5'] = Base64.encode64 Digest::MD5.hexdigest("thiswillnotmatchthehash")
147
- subject.run
148
- expect(response.code).to eq 400
149
- end
150
-
151
- it "should respond with 400 when the resource invalidates the checksum" do
152
- resource.validation = false
153
- headers['Content-MD5'] = Base64.encode64 Digest::MD5.hexdigest("thiswillnotmatchthehash")
154
- subject.run
155
- expect(response.code).to eq 400
156
- end
157
-
158
- it "should not respond with 400 when the resource validates the checksum" do
159
- resource.validation = true
160
- headers['Content-MD5'] = Base64.encode64 Digest::MD5.hexdigest("thiswillnotmatchthehash")
161
- subject.run
162
- expect(response.code).to_not eq 400
163
- end
164
-
165
- it "should respond with the given code when the resource returns a code while validating" do
166
- resource.validation = 500
167
- headers['Content-MD5'] = Base64.encode64 Digest::MD5.hexdigest("thiswillnotmatchthehash")
168
- subject.run
169
- expect(response.code).to eq 500
170
- end
171
- end
172
- end
173
-
174
- describe "#b8 (Authorized?)" do
175
- let(:resource) { resource_with { attr_accessor :auth; def is_authorized?(header); @auth; end } }
176
-
177
- it "should reply with 401 when the client is unauthorized" do
178
- resource.auth = false
179
- subject.run
180
- expect(response.code).to eq 401
181
- end
182
-
183
- it "should reply with 401 when the resource gives a challenge" do
184
- resource.auth = "Basic realm=Webmachine"
185
- subject.run
186
- expect(response.code).to eq 401
187
- expect(response.headers['WWW-Authenticate']).to eq "Basic realm=Webmachine"
188
- end
189
-
190
- it "should halt with the given code when the resource returns a status code" do
191
- resource.auth = 400
192
- subject.run
193
- expect(response.code).to eq 400
194
- end
195
-
196
- it "should not reply with 401 when the client is authorized" do
197
- resource.auth = true
198
- subject.run
199
- expect(response.code).to_not eq 401
200
- end
201
- end
202
-
203
- describe "#b7 (Forbidden?)" do
204
- let(:resource) { resource_with { attr_accessor :forbid; def forbidden?; @forbid; end } }
205
-
206
- it "should reply with 403 when the request is forbidden" do
207
- resource.forbid = true
208
- subject.run
209
- expect(response.code).to eq 403
210
- end
211
-
212
- it "should not reply with 403 when the request is permitted" do
213
- resource.forbid = false
214
- subject.run
215
- expect(response.code).to_not eq 403
216
- end
217
-
218
- it "should halt with the given code when the resource returns a status code" do
219
- resource.forbid = 400
220
- subject.run
221
- expect(response.code).to eq 400
222
- end
223
- end
224
-
225
- describe "#b6 (Unsupported Content-* header?)" do
226
- let(:resource) do
227
- resource_with do
228
- def valid_content_headers?(contents)
229
- contents['Content-Fail'].nil?
230
- end
231
- end
232
- end
233
-
234
- it "should reply with 501 when an invalid Content-* header is present" do
235
- headers['Content-Fail'] = "yup"
236
- subject.run
237
- expect(response.code).to eq 501
238
- end
239
-
240
- it "should not reply with 501 when all Content-* headers are valid" do
241
- subject.run
242
- expect(response.code).to_not eq 501
243
- end
244
- end
245
-
246
- describe "#b5 (Known Content-Type?)" do
247
- let(:method) { "POST" }
248
- let(:body) { "This is the body." }
249
- let(:resource) do
250
- resource_with do
251
- def known_content_type?(type) type !~ /unknown/; end;
252
- def process_post; true; end
253
- def allowed_methods; %w{POST}; end
254
- end
255
- end
256
-
257
- before { headers['Content-Length'] = body.length.to_s }
258
-
259
- it "should reply with 415 when the Content-Type is unknown" do
260
- headers['Content-Type'] = "application/x-unknown-type"
261
- subject.run
262
- expect(response.code).to eq 415
263
- end
264
-
265
- it "should not reply with 415 when the Content-Type is known" do
266
- headers['Content-Type'] = "text/plain"
267
- subject.run
268
- expect(response.code).to_not eq 415
269
- end
270
- end
271
-
272
- describe "#b4 (Request entity too large?)" do
273
- let(:resource) do
274
- resource_with do
275
- def allowed_methods; %w{POST}; end
276
- def process_post; true; end
277
- def valid_entity_length?(length); length.to_i < 100; end
278
- end
279
- end
280
- let(:method) { "POST" }
281
- before { headers['Content-Type'] = "text/plain"; headers['Content-Length'] = body.size.to_s }
282
-
283
- context "when the request body is too large" do
284
- let(:body) { "Big" * 100 }
285
- it "should reply with 413" do
286
- subject.run
287
- expect(response.code).to eq 413
288
- end
289
- end
290
-
291
- context "when the request body is not too large" do
292
- let(:body) { "small" }
293
-
294
- it "should not reply with 413" do
295
- subject.run
296
- expect(response.code).to_not eq 413
297
- end
298
- end
299
- end
300
-
301
- describe "#b3 (OPTIONS?)" do
302
- let(:method){ "OPTIONS" }
303
- let(:resource){ resource_with { def allowed_methods; %w[GET HEAD OPTIONS]; end } }
304
- it "should reply with 200 when the request method is OPTIONS" do
305
- subject.run
306
- expect(response.code).to eq 200
307
- end
308
- end
309
-
310
- describe "#c3, #c4 (Acceptable media types)" do
311
- let(:resource) { default_resource }
312
- context "when the Accept header exists" do
313
- it "should reply with 406 when the type is unacceptable" do
314
- headers['Accept'] = "text/plain"
315
- subject.run
316
- expect(response.code).to eq 406
317
- end
318
-
319
- it "should not reply with 406 when the type is acceptable" do
320
- headers['Accept'] = "text/*"
321
- subject.run
322
- expect(response.code).to_not eq 406
323
- expect(response.headers['Content-Type']).to eq "text/html"
324
- end
325
- end
326
-
327
- context "when the Accept header does not exist" do
328
- it "should not negotiate a media type" do
329
- expect(headers['Accept']).to be_nil
330
- expect(subject).to_not receive(:c4)
331
- subject.run
332
- expect(response.headers['Content-Type']).to eq 'text/html'
333
- end
334
- end
335
- end
336
-
337
- describe "#d4, #d5 (Acceptable languages)" do
338
- let(:resource) { resource_with { def languages_provided; %w{en-US fr}; end } }
339
- context "when the Accept-Language header exists" do
340
- it "should reply with 406 when the language is unacceptable" do
341
- headers['Accept-Language'] = "es, de"
342
- subject.run
343
- expect(response.code).to eq 406
344
- end
345
-
346
- it "should not reply with 406 when the language is acceptable" do
347
- headers['Accept-Language'] = "en-GB, en;q=0.7"
348
- subject.run
349
- expect(response.code).to_not eq 406
350
- expect(response.headers['Content-Language']).to eq "en-US"
351
- expect(resource.instance_variable_get(:@language)).to eq 'en-US'
352
- end
353
- end
354
-
355
- context "when the Accept-Language header is absent" do
356
- it "should not negotiate the language" do
357
- expect(headers['Accept-Language']).to be_nil
358
- expect(subject).to_not receive(:d5)
359
- subject.run
360
- expect(response.headers['Content-Language']).to eq 'en-US'
361
- expect(resource.instance_variable_get(:@language)).to eq 'en-US'
362
- end
363
- end
364
- end
365
-
366
- describe "#e5, #e6 (Acceptable charsets)" do
367
- let(:resource) do
368
- resource_with do
369
- def charsets_provided
370
- [["iso8859-1", :to_iso],["utf-8", :to_utf]];
371
- end
372
- def to_iso(chunk); chunk; end
373
- def to_utf(chunk); chunk; end
374
- end
375
- end
376
-
377
- context "when the Accept-Charset header exists" do
378
- it "should reply with 406 when the charset is unacceptable" do
379
- headers['Accept-Charset'] = "utf-16"
380
- subject.run
381
- expect(response.code).to eq 406
382
- end
383
-
384
- it "should not reply with 406 when the charset is acceptable" do
385
- headers['Accept-Charset'] = "iso8859-1"
386
- subject.run
387
- expect(response.code).to_not eq 406
388
- expect(response.headers['Content-Type']).to eq "text/html;charset=iso8859-1"
389
- end
390
- end
391
-
392
- context "when the Accept-Charset header is absent" do
393
- it "should not negotiate the language" do
394
- expect(headers['Accept-Charset']).to be_nil
395
- expect(subject).to_not receive(:e6)
396
- subject.run
397
- expect(response.headers['Content-Type']).to eq 'text/html;charset=iso8859-1'
398
- end
399
- end
400
- end
401
-
402
- describe "#f6, #f7 (Acceptable encodings)" do
403
- let(:resource) do
404
- resource_with do
405
- def encodings_provided
406
- super.merge("gzip" => :encode_gzip)
407
- end
408
- end
409
- end
410
-
411
- context "when the Accept-Encoding header is present" do
412
- it "should reply with 406 if the encoding is unacceptable" do
413
- headers['Accept-Encoding'] = 'deflate, identity;q=0.0'
414
- subject.run
415
- expect(response.code).to eq 406
416
- end
417
-
418
- it "should not reply with 406 if the encoding is acceptable" do
419
- headers['Accept-Encoding'] = 'gzip, deflate'
420
- subject.run
421
- expect(response.code).to_not eq 406
422
- expect(response.headers['Content-Encoding']).to eq 'gzip'
423
- # It should be compressed
424
- expect(response.body).to_not eq 'test resource'
425
- end
426
- end
427
-
428
- context "when the Accept-Encoding header is not present" do
429
- it "should not negotiate an encoding" do
430
- expect(headers['Accept-Encoding']).to be_nil
431
- expect(subject).to_not receive(:f7)
432
- subject.run
433
- expect(response.code).to_not eq 406
434
- # It should not be compressed
435
- expect(response.body).to eq 'test resource'
436
- end
437
- end
438
- end
439
-
440
- describe "#g7 (Resource exists?)" do
441
- let(:resource) { resource_with { attr_accessor :exist; def resource_exists?; @exist; end } }
442
-
443
- it "should not enter conditional requests if missing (and eventually reply with 404)" do
444
- resource.exist = false
445
- expect(subject).to_not receive(:g8)
446
- subject.run
447
- expect(response.code).to eq 404
448
- end
449
-
450
- it "should not reply with 404 if it does exist" do
451
- resource.exist = true
452
- expect(subject).to_not receive(:h7)
453
- subject.run
454
- expect(response.code).to_not eq 404
455
- end
456
-
457
- it "should not reply with 404 for truthy non-booleans" do
458
- resource.exist = []
459
- subject.run
460
- expect(response.code).to_not eq 404
461
- end
462
-
463
- it "should reply with 404 for nil" do
464
- resource.exist = nil
465
- subject.run
466
- expect(response.code).to eq 404
467
- end
468
- end
469
-
470
- # Conditional requests/preconditions
471
- describe "#g8, #g9, #g10 (ETag match)" do
472
- let(:resource) { resource_with { def generate_etag; "etag"; end } }
473
- it "should skip ETag matching when If-Match is missing" do
474
- expect(headers['If-Match']).to be_nil
475
- expect(subject).to_not receive(:g9)
476
- expect(subject).to_not receive(:g11)
477
- subject.run
478
- expect(response.code).to_not eq 412
479
- end
480
- it "should not reply with 304 when If-Match is *" do
481
- headers['If-Match'] = "*"
482
- subject.run
483
- expect(response.code).to_not eq 412
484
- end
485
- it "should reply with 412 if the ETag is not in If-Match" do
486
- headers['If-Match'] = '"notetag"'
487
- subject.run
488
- expect(response.code).to eq 412
489
- end
490
- it "should not reply with 412 if the ETag is in If-Match" do
491
- headers['If-Match'] = '"etag"'
492
- subject.run
493
- expect(response.code).to_not eq 412
494
- end
495
- end
496
-
497
- describe "#h10, #h11, #h12 (If-Unmodified-Since match [IUMS])" do
498
- let(:resource) { resource_with { attr_accessor :now; def last_modified; @now; end } }
499
- before { @now = resource.now = Time.now }
500
-
501
- it "should skip LM matching if IUMS is missing" do
502
- expect(headers['If-Unmodified-Since']).to be_nil
503
- expect(subject).to_not receive(:h11)
504
- expect(subject).to_not receive(:h12)
505
- subject.run
506
- expect(response.code).to_not eq 412
507
- end
508
-
509
- it "should skip LM matching if IUMS is an invalid date" do
510
- headers['If-Unmodified-Since'] = "garbage"
511
- expect(subject).to_not receive(:h12)
512
- subject.run
513
- expect(response.code).to_not eq 412
514
- end
515
-
516
- it "should not reply with 412 if LM is <= IUMS" do
517
- headers['If-Unmodified-Since'] = (@now + 100).httpdate
518
- subject.run
519
- expect(response.code).to_not eq 412
520
- end
521
-
522
- it "should reply with 412 if LM is > IUMS" do
523
- headers['If-Unmodified-Since'] = (@now - 100).httpdate
524
- subject.run
525
- expect(response.code).to eq 412
526
- end
527
- end
528
-
529
- describe "#i12, #i13, #k13, #j18 (If-None-Match match)" do
530
- let(:resource) do
531
- resource_with do
532
- def generate_etag; "etag"; end;
533
- def process_post; true; end
534
- def allowed_methods; %w{GET HEAD POST}; end
535
- end
536
- end
537
-
538
- it "should skip ETag matching if If-None-Match is missing" do
539
- expect(headers['If-None-Match']).to be_nil
540
- %w{i13 k13 j18}.each do |m|
541
- expect(subject).to_not receive(m.to_sym)
542
- end
543
- subject.run
544
- expect([304, 412]).to_not include(response.code)
545
- end
546
-
547
- it "should not reply with 412 or 304 if the ETag is not in If-None-Match" do
548
- headers['If-None-Match'] = '"notetag"'
549
- subject.run
550
- expect([304, 412]).to_not include(response.code)
551
- end
552
-
553
- context "when the method is GET or HEAD" do
554
- let(:method){ %w{GET HEAD}[rand(1)] }
555
- it "should reply with 304 when If-None-Match is *" do
556
- headers['If-None-Match'] = '*'
557
- end
558
- it "should reply with 304 when the ETag is in If-None-Match" do
559
- headers['If-None-Match'] = '"etag", "foobar"'
560
- end
561
- after { subject.run; expect(response.code).to eq 304 }
562
- end
563
-
564
- context "when the method is not GET or HEAD" do
565
- let(:method){ "POST" }
566
- let(:body) { "This is the body." }
567
- let(:headers){ Webmachine::Headers["Content-Type" => "text/plain"] }
568
-
569
- it "should reply with 412 when If-None-Match is *" do
570
- headers['If-None-Match'] = '*'
571
- end
572
-
573
- it "should reply with 412 when the ETag is in If-None-Match" do
574
- headers['If-None-Match'] = '"etag"'
575
- end
576
- after { subject.run; expect(response.code).to eq 412 }
577
- end
578
-
579
- context "when the resource does not define an ETag" do
580
- let(:resource) do
581
- resource_with do
582
- def generate_etag; nil; end
583
- end
584
- end
585
-
586
- it "should reply with 200 when If-None-Match is missing" do
587
- headers.delete 'If-None-Match'
588
- subject.run
589
- expect(response.code).to eq 200
590
- end
591
-
592
- it "should reply with 200 when If-None-Match is present" do
593
- headers['If-None-Match'] = '"etag"'
594
- subject.run
595
- expect(response.code).to eq 200
596
- end
597
- end
598
- end
599
-
600
- describe "#l13, #l14, #l15, #l17 (If-Modified-Since match)" do
601
- let(:resource) { resource_with { attr_accessor :now; def last_modified; @now; end } }
602
- before { @now = resource.now = Time.now }
603
- it "should skip LM matching if IMS is missing" do
604
- expect(headers['If-Modified-Since']).to be_nil
605
- %w{l14 l15 l17}.each do |m|
606
- expect(subject).to_not receive(m.to_sym)
607
- end
608
- subject.run
609
- expect(response.code).to_not eq 304
610
- end
611
-
612
- it "should skip LM matching if IMS is an invalid date" do
613
- headers['If-Modified-Since'] = "garbage"
614
- %w{l15 l17}.each do |m|
615
- expect(subject).to_not receive(m.to_sym)
616
- end
617
- subject.run
618
- expect(response.code).to_not eq 304
619
- end
620
-
621
- it "should skip LM matching if IMS is later than current time" do
622
- headers['If-Modified-Since'] = (@now + 1000).httpdate
623
- expect(subject).to_not receive(:l17)
624
- subject.run
625
- expect(response.code).to_not eq 304
626
- end
627
-
628
- it "should reply with 304 if LM is <= IMS" do
629
- headers['If-Modified-Since'] = (@now - 1).httpdate
630
- resource.now = @now - 1000
631
- subject.run
632
- expect(response.code).to eq 304
633
- end
634
-
635
- it "should not reply with 304 if LM is > IMS" do
636
- headers['If-Modified-Since'] = (@now - 1000).httpdate
637
- subject.run
638
- expect(response.code).to_not eq 304
639
- end
640
- end
641
-
642
- # Resource missing branch (upper right)
643
- describe "#h7 (If-Match: * exists?)" do
644
- let(:resource) { missing_resource }
645
- it "should reply with 412 when the If-Match header is *" do
646
- headers['If-Match'] = '"*"'
647
- subject.run
648
- expect(response.code).to eq 412
649
- end
650
-
651
- it "should not reply with 412 when the If-Match header is missing or not *" do
652
- headers['If-Match'] = ['"etag"', nil][rand(1)]
653
- subject.run
654
- expect(response.code).to_not eq 412
655
- end
656
- end
657
-
658
- describe "#i7 (PUT?)" do
659
- let(:resource) do
660
- missing_resource_with do
661
- def allowed_methods; %w{GET HEAD PUT POST}; end
662
- def process_post; true; end
663
- end
664
- end
665
- let(:body) { %W{GET HEAD DELETE}.include?(method) ? nil : "This is the body." }
666
- before { headers['Content-Type'] = 'text/plain' }
667
-
668
- context "when the method is PUT" do
669
- let(:method){ "PUT" }
670
-
671
- it "should not reach state k7" do
672
- expect(subject).to_not receive(:k7)
673
- subject.run
674
- end
675
-
676
- after { expect([404, 410, 303]).to_not include(response.code) }
677
- end
678
-
679
- context "when the method is not PUT" do
680
- let(:method){ %W{GET HEAD POST DELETE}[rand(4)] }
681
-
682
- it "should not reach state i4" do
683
- expect(subject).to_not receive(:i4)
684
- subject.run
685
- end
686
-
687
- after { expect(response.code).to_not eq 409 }
688
- end
689
- end
690
-
691
- describe "#i4 (Apply to a different URI?)" do
692
- let(:resource) do
693
- missing_resource_with do
694
- attr_accessor :location
695
- def moved_permanently?; @location; end
696
- def allowed_methods; %w[PUT]; end
697
- end
698
- end
699
- let(:method){ "PUT" }
700
- let(:body){ "This is the body." }
701
- let(:headers) { Webmachine::Headers["Content-Type" => "text/plain", "Content-Length" => body.size.to_s] }
702
-
703
- it "should reply with 301 when the resource has moved" do
704
- resource.location = URI.parse("http://localhost:8098/newuri")
705
- subject.run
706
- expect(response.code).to eq 301
707
- expect(response.headers['Location']).to eq resource.location.to_s
708
- end
709
-
710
- it "should not reply with 301 when resource has not moved" do
711
- resource.location = false
712
- subject.run
713
- expect(response.code).to_not eq 301
714
- end
715
- end
716
-
717
- describe "Redirection (Resource previously existed)" do
718
- let(:resource) do
719
- missing_resource_with do
720
- attr_writer :moved_perm, :moved_temp, :allow_missing
721
- def previously_existed?; true; end
722
- def moved_permanently?; @moved_perm; end
723
- def moved_temporarily?; @moved_temp; end
724
- def allow_missing_post?; @allow_missing; end
725
- def allowed_methods; %W{GET POST}; end
726
- def process_post; true; end
727
- end
728
- end
729
- let(:method){ @method || "GET" }
730
-
731
- describe "#k5 (Moved permanently?)" do
732
- it "should reply with 301 when the resource has moved permanently" do
733
- uri = resource.moved_perm = URI.parse("http://www.google.com/")
734
- subject.run
735
- expect(response.code).to eq 301
736
- expect(response.headers['Location']).to eq uri.to_s
737
- end
738
- it "should not reply with 301 when the resource has not moved permanently" do
739
- resource.moved_perm = false
740
- subject.run
741
- expect(response.code).to_not eq 301
742
- end
743
- end
744
-
745
- describe "#l5 (Moved temporarily?)" do
746
- before { resource.moved_perm = false }
747
- it "should reply with 307 when the resource has moved temporarily" do
748
- uri = resource.moved_temp = URI.parse("http://www.basho.com/")
749
- subject.run
750
- expect(response.code).to eq 307
751
- expect(response.headers['Location']).to eq uri.to_s
752
- end
753
- it "should not reply with 307 when the resource has not moved temporarily" do
754
- resource.moved_temp = false
755
- subject.run
756
- expect(response.code).to_not eq 307
757
- end
758
- end
759
-
760
- describe "#m5 (POST?), #n5 (POST to missing resource?)" do
761
- before { resource.moved_perm = resource.moved_temp = false }
762
- it "should reply with 410 when the method is not POST" do
763
- expect(method).to_not eq "POST"
764
- subject.run
765
- expect(response.code).to eq 410
766
- end
767
- it "should reply with 410 when the resource disallows missing POSTs" do
768
- @method = "POST"
769
- resource.allow_missing = false
770
- subject.run
771
- expect(response.code).to eq 410
772
- end
773
- it "should not reply with 410 when the resource allows missing POSTs" do
774
- @method = "POST"
775
- resource.allow_missing = true
776
- subject.run
777
- expect(response.code).to eq 410
778
- end
779
- end
780
- end
781
-
782
- describe "#l7 (POST?), #m7 (POST to missing resource?)" do
783
- let(:resource) do
784
- missing_resource_with do
785
- attr_accessor :allow_missing
786
- def allowed_methods; %W{GET POST}; end
787
- def previously_existed?; false; end
788
- def allow_missing_post?; @allow_missing; end
789
- def process_post; true; end
790
- end
791
- end
792
- let(:method){ @method || "GET" }
793
- it "should reply with 404 when the method is not POST" do
794
- expect(method).to_not eq "POST"
795
- subject.run
796
- expect(response.code).to eq 404
797
- end
798
- it "should reply with 404 when the resource disallows missing POSTs" do
799
- @method = "POST"
800
- resource.allow_missing = false
801
- subject.run
802
- expect(response.code).to eq 404
803
- end
804
- it "should not reply with 404 when the resource allows missing POSTs" do
805
- @method = "POST"
806
- resource.allow_missing = true
807
- subject.run
808
- expect(response.code).to_not eq 404
809
- end
810
- end
811
-
812
- describe "#p3 (Conflict?)" do
813
- let(:resource) do
814
- missing_resource_with do
815
- attr_writer :conflict
816
- def allowed_methods; %W{PUT}; end
817
- def is_conflict?; @conflict; end
818
- end
819
- end
820
- let(:method){ "PUT" }
821
- it "should reply with 409 if the resource is in conflict" do
822
- resource.conflict = true
823
- subject.run
824
- expect(response.code).to eq 409
825
- end
826
- it "should not reply with 409 if the resource is in conflict" do
827
- resource.conflict = false
828
- subject.run
829
- expect(response.code).to_not eq 409
830
- end
831
- end
832
-
833
- # Bottom right
834
- describe "#n11 (Redirect?)" do
835
- let(:method) { "POST" }
836
- let(:resource) do
837
- resource_with do
838
- attr_writer :new_loc, :exist
839
- def allowed_methods; %w{POST}; end
840
- def allow_missing_post?; true; end
841
- def process_post
842
- response.redirect_to(@new_loc) if @new_loc
843
- true
844
- end
845
- end
846
- end
847
- [true, false].each do |e|
848
- context "and the resource #{ e ? "does not exist" : 'exists'}" do
849
- before { resource.exist = e }
850
-
851
- it "should reply with 303 if the resource redirected" do
852
- resource.new_loc = URI.parse("/foo/bar")
853
- subject.run
854
- expect(response.code).to eq 303
855
- expect(response.headers['Location']).to eq "/foo/bar"
856
- end
857
-
858
- it "should not reply with 303 if the resource did not redirect" do
859
- resource.new_loc = nil
860
- subject.run
861
- expect(response.code).to_not eq 303
862
- end
863
- end
864
- end
865
- end
866
-
867
- describe "#p11 (New resource?)" do
868
- let(:resource) do
869
- resource_with do
870
- attr_writer :exist, :new_loc, :create
871
-
872
- def allowed_methods; %W{PUT POST}; end
873
- def resource_exists?; @exist; end
874
- def process_post; true; end
875
- def allow_missing_post?; true; end
876
- def post_is_create?; @create; end
877
- def create_path; @new_loc; end
878
- def content_types_accepted; [["text/plain", :accept_text]]; end
879
- def accept_text
880
- response.headers['Location'] = @new_loc.to_s if @new_loc
881
- true
882
- end
883
- end
884
- end
885
- let(:body) { "new content" }
886
- let(:headers){ Webmachine::Headers['content-type' => 'text/plain'] }
887
-
888
- context "when the method is PUT" do
889
- let(:method){ "PUT" }
890
- [true, false].each do |e|
891
- context "and the resource #{ e ? "does not exist" : 'exists'}" do
892
- before { resource.exist = e }
893
-
894
- it "should reply with 201 when the Location header has been set" do
895
- resource.exist = e
896
- resource.new_loc = "http://ruby-doc.org/"
897
- subject.run
898
- expect(response.code).to eq 201
899
- end
900
- it "should not reply with 201 when the Location header has been set" do
901
- resource.exist = e
902
- subject.run
903
- expect(response.headers['Location']).to be_nil
904
- expect(response.code).to_not eq 201
905
- end
906
- end
907
- end
908
- end
909
-
910
- context "when the method is POST" do
911
- let(:method){ "POST" }
912
- [true, false].each do |e|
913
- context "and the resource #{ e ? 'exists' : "does not exist"}" do
914
- before { resource.exist = e }
915
- it "should reply with 201 when post_is_create is true and create_path returns a URI" do
916
- resource.new_loc = created = "/foo/bar/baz"
917
- resource.create = true
918
- subject.run
919
- expect(response.code).to eq 201
920
- expect(response.headers['Location']).to eq created
921
- end
922
- it "should reply with 500 when post_is_create is true and create_path returns nil" do
923
- resource.create = true
924
- subject.run
925
- expect(response.code).to eq 500
926
- expect(response.error).to_not be_nil
927
- end
928
- it "should not reply with 201 when post_is_create is false" do
929
- resource.create = false
930
- subject.run
931
- expect(response.code).to_not eq 201
932
- end
933
- end
934
- end
935
- end
936
- end
937
-
938
- describe "#o14 (Conflict?)" do
939
- let(:resource) do
940
- resource_with do
941
- attr_writer :conflict
942
- def allowed_methods; %W{PUT}; end
943
- def is_conflict?; @conflict; end
944
- end
945
- end
946
- let(:method){ "PUT" }
947
- it "should reply with 409 if the resource is in conflict" do
948
- resource.conflict = true
949
- subject.run
950
- expect(response.code).to eq 409
951
- end
952
- it "should not reply with 409 if the resource is in conflict" do
953
- resource.conflict = false
954
- subject.run
955
- expect(response.code).to_not eq 409
956
- end
957
- end
958
-
959
- describe "#m16 (DELETE?), #m20 (Delete enacted?)" do
960
- let(:method){ @method || "DELETE" }
961
- let(:resource) do
962
- resource_with do
963
- attr_writer :deleted, :completed
964
- def allowed_methods; %w{GET DELETE}; end
965
- def delete_resource; @deleted; end
966
- def delete_completed?; @completed; end
967
- end
968
- end
969
- it "should not reply with 202 if the method is not DELETE" do
970
- @method = "GET"
971
- subject.run
972
- expect(response.code).to_not eq 202
973
- end
974
- it "should reply with 500 if the DELETE fails" do
975
- resource.deleted = false
976
- subject.run
977
- expect(response.code).to eq 500
978
- end
979
- it "should reply with 202 if the DELETE succeeds but is not complete" do
980
- resource.deleted = true
981
- resource.completed = false
982
- subject.run
983
- expect(response.code).to eq 202
984
- end
985
- it "should not reply with 202 if the DELETE succeeds and completes" do
986
- resource.completed = resource.deleted = true
987
- subject.run
988
- expect(response.code).to_not eq 202
989
- end
990
- end
991
-
992
- # These decisions are covered by dozens of other examples. Leaving
993
- # commented for now.
994
- # describe "#n16 (POST?)" do it; end
995
- # describe "#o16 (PUT?)" do it; end
996
-
997
- describe "#o18 (Multiple representations?)" do
998
- let(:resource) do
999
- resource_with do
1000
- attr_writer :exist, :multiple
1001
- def delete_resource
1002
- response.body = "Response content."
1003
- true
1004
- end
1005
- def delete_completed?; true; end
1006
- def allowed_methods; %W{GET HEAD PUT POST DELETE}; end
1007
- def resource_exists?; @exist; end
1008
- def allow_missing_post?; true; end
1009
- def content_types_accepted; [[request.content_type, :accept_all]]; end
1010
- def multiple_choices?; @multiple; end
1011
- def process_post
1012
- response.body = "Response content."
1013
- true
1014
- end
1015
- def accept_all
1016
- response.body = "Response content."
1017
- true
1018
- end
1019
- end
1020
- end
1021
-
1022
- [["GET", true],["HEAD", true],["PUT", true],["PUT", false],["POST",true],["POST",false],
1023
- ["DELETE", true]].each do |m, e|
1024
- context "when the method is #{m} and the resource #{e ? 'exists' : 'does not exist' }" do
1025
- let(:method){ m }
1026
- let(:body) { %W{PUT POST}.include?(m) ? "request body" : "" }
1027
- let(:headers) { %W{PUT POST}.include?(m) ? Webmachine::Headers['content-type' => 'text/plain'] : Webmachine::Headers.new }
1028
- before { resource.exist = e }
1029
- it "should reply with 200 if there are not multiple representations" do
1030
- resource.multiple = false
1031
- subject.run
1032
- puts response.error if response.code == 500
1033
- expect(response.code).to eq 200
1034
- end
1035
- it "should reply with 300 if there are multiple representations" do
1036
- resource.multiple = true
1037
- subject.run
1038
- puts response.error if response.code == 500
1039
- expect(response.code).to eq 300
1040
- end
1041
- end
1042
- end
1043
- end
1044
-
1045
- describe "#o20 (Response has entity?)" do
1046
- let(:resource) do
1047
- resource_with do
1048
- attr_writer :exist, :body
1049
- def delete_resource; true; end
1050
- def delete_completed?; true; end
1051
- def allowed_methods; %{GET PUT POST DELETE}; end
1052
- def resource_exists?; @exist; end
1053
- def allow_missing_post?; true; end
1054
- def content_types_accepted; [[request.content_type, :accept_all]]; end
1055
- def process_post
1056
- response.body = @body if @body
1057
- true
1058
- end
1059
- def accept_all
1060
- response.body = @body if @body
1061
- true
1062
- end
1063
- end
1064
- end
1065
- let(:method) { @method || "GET" }
1066
- let(:headers) { %{PUT POST}.include?(method) ? Webmachine::Headers["content-type" => "text/plain"] : Webmachine::Headers.new }
1067
- let(:body) { %{PUT POST}.include?(method) ? "This is the body." : nil }
1068
- context "when a response body is present" do
1069
- before { resource.body = "Hello, world!" }
1070
- [
1071
- ["PUT", false],
1072
- ["POST", false],
1073
- ["DELETE", true],
1074
- ["POST", true],
1075
- ["PUT", true]
1076
- ].each do |m, e|
1077
- it "should not reply with 204 (via exists:#{e}, #{m})" do
1078
- @method = m
1079
- resource.exist = e
1080
- subject.run
1081
- expect(response.code).to_not eq 204
1082
- end
1083
- end
1084
- end
1085
- context "when a response body is not present" do
1086
- [
1087
- ["PUT", false],
1088
- ["POST", false],
1089
- ["DELETE", true],
1090
- ["POST", true],
1091
- ["PUT", true]
1092
- ].each do |m, e|
1093
- it "should reply with 204 (via exists:#{e}, #{m})" do
1094
- @method = m
1095
- resource.exist = e
1096
- subject.run
1097
- expect(response.code).to eq 204
1098
- end
1099
- end
1100
- end
1101
- end
1102
-
1103
- describe "On error" do
1104
- context "handle_exception is inherited." do
1105
- let :resource do
1106
- resource_with do
1107
- def to_html
1108
- raise
1109
- end
1110
- end
1111
- end
1112
-
1113
- it "calls handle_exception" do
1114
- expect(resource).to receive(:handle_exception).with instance_of(RuntimeError)
1115
- subject.run
1116
- end
1117
-
1118
- it "sets the response code to 500" do
1119
- subject.run
1120
- expect(response.code).to eq 500
1121
- end
1122
- end
1123
-
1124
- context "handle_exception is defined" do
1125
- let :resource do
1126
- resource_with do
1127
- def handle_exception(e)
1128
- response.body = "error"
1129
- end
1130
-
1131
- def to_html
1132
- raise
1133
- end
1134
- end
1135
- end
1136
-
1137
- it "can define a response body" do
1138
- subject.run
1139
- expect(response.body).to eq "error"
1140
- end
1141
-
1142
- it "sets the response code to 500" do
1143
- subject.run
1144
- expect(response.code).to eq 500
1145
- end
1146
- end
1147
- end
1148
- end