webmachine 1.2.2 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +13 -11
- data/README.md +85 -89
- data/Rakefile +0 -1
- data/documentation/adapters.md +39 -0
- data/documentation/authentication-and-authorization.md +37 -0
- data/documentation/configurator.md +19 -0
- data/documentation/error-handling.md +86 -0
- data/documentation/examples.md +215 -0
- data/documentation/how-it-works.md +76 -0
- data/documentation/routes.md +97 -0
- data/documentation/validation.md +159 -0
- data/documentation/versioning-apis.md +74 -0
- data/documentation/visual-debugger.md +38 -0
- data/examples/application.rb +2 -2
- data/examples/debugger.rb +1 -1
- data/lib/webmachine.rb +3 -1
- data/lib/webmachine/adapter.rb +7 -13
- data/lib/webmachine/adapters.rb +1 -2
- data/lib/webmachine/adapters/httpkit.rb +74 -0
- data/lib/webmachine/adapters/lazy_request_body.rb +1 -2
- data/lib/webmachine/adapters/rack.rb +37 -21
- data/lib/webmachine/adapters/reel.rb +21 -23
- data/lib/webmachine/adapters/webrick.rb +16 -16
- data/lib/webmachine/application.rb +2 -2
- data/lib/webmachine/chunked_body.rb +3 -4
- data/lib/webmachine/constants.rb +75 -0
- data/lib/webmachine/decision/conneg.rb +12 -10
- data/lib/webmachine/decision/flow.rb +31 -21
- data/lib/webmachine/decision/fsm.rb +10 -18
- data/lib/webmachine/decision/helpers.rb +9 -37
- data/lib/webmachine/dispatcher.rb +13 -10
- data/lib/webmachine/dispatcher/route.rb +18 -8
- data/lib/webmachine/errors.rb +7 -1
- data/lib/webmachine/header_negotiation.rb +25 -0
- data/lib/webmachine/headers.rb +7 -2
- data/lib/webmachine/locale/en.yml +7 -5
- data/lib/webmachine/media_type.rb +10 -8
- data/lib/webmachine/request.rb +44 -15
- data/lib/webmachine/resource.rb +1 -1
- data/lib/webmachine/resource/callbacks.rb +6 -4
- data/lib/webmachine/spec/IO_response.body +1 -0
- data/lib/webmachine/spec/adapter_lint.rb +70 -36
- data/lib/webmachine/spec/test_resource.rb +10 -4
- data/lib/webmachine/streaming/fiber_encoder.rb +1 -5
- data/lib/webmachine/streaming/io_encoder.rb +6 -0
- data/lib/webmachine/trace.rb +1 -0
- data/lib/webmachine/trace/fsm.rb +20 -10
- data/lib/webmachine/trace/resource_proxy.rb +2 -0
- data/lib/webmachine/translation.rb +2 -1
- data/lib/webmachine/version.rb +3 -3
- data/memory_test.rb +37 -0
- data/spec/spec_helper.rb +9 -9
- data/spec/webmachine/adapter_spec.rb +14 -15
- data/spec/webmachine/adapters/httpkit_spec.rb +10 -0
- data/spec/webmachine/adapters/rack_spec.rb +6 -6
- data/spec/webmachine/adapters/reel_spec.rb +15 -11
- data/spec/webmachine/adapters/webrick_spec.rb +2 -2
- data/spec/webmachine/application_spec.rb +18 -17
- data/spec/webmachine/chunked_body_spec.rb +3 -3
- data/spec/webmachine/configuration_spec.rb +5 -5
- data/spec/webmachine/cookie_spec.rb +13 -13
- data/spec/webmachine/decision/conneg_spec.rb +48 -42
- data/spec/webmachine/decision/falsey_spec.rb +4 -4
- data/spec/webmachine/decision/flow_spec.rb +194 -144
- data/spec/webmachine/decision/fsm_spec.rb +17 -17
- data/spec/webmachine/decision/helpers_spec.rb +20 -20
- data/spec/webmachine/dispatcher/route_spec.rb +73 -27
- data/spec/webmachine/dispatcher_spec.rb +34 -24
- data/spec/webmachine/errors_spec.rb +1 -1
- data/spec/webmachine/etags_spec.rb +19 -19
- data/spec/webmachine/events_spec.rb +6 -6
- data/spec/webmachine/headers_spec.rb +14 -14
- data/spec/webmachine/media_type_spec.rb +36 -36
- data/spec/webmachine/request_spec.rb +33 -33
- data/spec/webmachine/resource/authentication_spec.rb +6 -6
- data/spec/webmachine/response_spec.rb +12 -12
- data/spec/webmachine/trace/fsm_spec.rb +8 -8
- data/spec/webmachine/trace/resource_proxy_spec.rb +9 -9
- data/spec/webmachine/trace/trace_store_spec.rb +5 -5
- data/spec/webmachine/trace_spec.rb +3 -3
- data/webmachine.gemspec +2 -6
- metadata +48 -206
- data/lib/webmachine/adapters/hatetepe.rb +0 -108
- data/lib/webmachine/adapters/mongrel.rb +0 -127
- data/lib/webmachine/dispatcher/not_found_resource.rb +0 -5
- data/lib/webmachine/fiber18.rb +0 -88
- data/spec/webmachine/adapters/hatetepe_spec.rb +0 -60
- data/spec/webmachine/adapters/mongrel_spec.rb +0 -16
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Webmachine::Decision::Falsey do
|
4
|
-
specify { (described_class.=== false).
|
5
|
-
specify { (described_class.=== nil).
|
6
|
-
specify { (described_class.=== true).
|
7
|
-
specify { (described_class.=== []).
|
4
|
+
specify { expect(described_class.=== false).to be(true) }
|
5
|
+
specify { expect(described_class.=== nil).to be(true) }
|
6
|
+
specify { expect(described_class.=== true).to be(false) }
|
7
|
+
specify { expect(described_class.=== []).to be(false) }
|
8
8
|
end
|
@@ -16,7 +16,7 @@ describe Webmachine::Decision::Flow do
|
|
16
16
|
# ... [except 1xx or 5xx]
|
17
17
|
after(:each) do
|
18
18
|
unless response.code < 200 || response.code >= 500
|
19
|
-
response.headers.
|
19
|
+
expect(response.headers).to have_key('Date')
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -46,7 +46,7 @@ describe Webmachine::Decision::Flow do
|
|
46
46
|
it "should respond with 503 when the service is unavailable" do
|
47
47
|
resource.available = false
|
48
48
|
subject.run
|
49
|
-
response.code.
|
49
|
+
expect(response.code).to eq 503
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -59,7 +59,7 @@ describe Webmachine::Decision::Flow do
|
|
59
59
|
|
60
60
|
it "should respond with 501 when the method is unknown" do
|
61
61
|
subject.run
|
62
|
-
response.code.
|
62
|
+
expect(response.code).to eq 501
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
@@ -72,7 +72,7 @@ describe Webmachine::Decision::Flow do
|
|
72
72
|
|
73
73
|
it "should respond with 414 when the URI is too long" do
|
74
74
|
subject.run
|
75
|
-
response.code.
|
75
|
+
expect(response.code).to eq 414
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
@@ -85,8 +85,8 @@ describe Webmachine::Decision::Flow do
|
|
85
85
|
|
86
86
|
it "should respond with 405 when the method is not allowed" do
|
87
87
|
subject.run
|
88
|
-
response.code.
|
89
|
-
response.headers['Allow'].
|
88
|
+
expect(response.code).to eq 405
|
89
|
+
expect(response.headers['Allow']).to eq "POST"
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
@@ -95,7 +95,7 @@ describe Webmachine::Decision::Flow do
|
|
95
95
|
|
96
96
|
it "should respond with 400 when the request is malformed" do
|
97
97
|
subject.run
|
98
|
-
response.code.
|
98
|
+
expect(response.code).to eq 400
|
99
99
|
end
|
100
100
|
|
101
101
|
context "when the Content-MD5 header is present" do
|
@@ -112,31 +112,61 @@ describe Webmachine::Decision::Flow do
|
|
112
112
|
let(:body) { "This is the body." }
|
113
113
|
let(:headers) { Webmachine::Headers["Content-Type" => "text/plain"] }
|
114
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
|
+
|
115
145
|
it "should respond with 400 when the request body does not match the header" do
|
116
|
-
headers['Content-MD5'] = "thiswillnotmatchthehash"
|
146
|
+
headers['Content-MD5'] = Base64.encode64 Digest::MD5.hexdigest("thiswillnotmatchthehash")
|
117
147
|
subject.run
|
118
|
-
response.code.
|
148
|
+
expect(response.code).to eq 400
|
119
149
|
end
|
120
150
|
|
121
151
|
it "should respond with 400 when the resource invalidates the checksum" do
|
122
152
|
resource.validation = false
|
123
|
-
headers['Content-MD5'] = "thiswillnotmatchthehash"
|
153
|
+
headers['Content-MD5'] = Base64.encode64 Digest::MD5.hexdigest("thiswillnotmatchthehash")
|
124
154
|
subject.run
|
125
|
-
response.code.
|
155
|
+
expect(response.code).to eq 400
|
126
156
|
end
|
127
157
|
|
128
158
|
it "should not respond with 400 when the resource validates the checksum" do
|
129
159
|
resource.validation = true
|
130
|
-
headers['Content-MD5'] = "thiswillnotmatchthehash"
|
160
|
+
headers['Content-MD5'] = Base64.encode64 Digest::MD5.hexdigest("thiswillnotmatchthehash")
|
131
161
|
subject.run
|
132
|
-
response.code.
|
162
|
+
expect(response.code).to_not eq 400
|
133
163
|
end
|
134
164
|
|
135
165
|
it "should respond with the given code when the resource returns a code while validating" do
|
136
166
|
resource.validation = 500
|
137
|
-
headers['Content-MD5'] = "thiswillnotmatchthehash"
|
167
|
+
headers['Content-MD5'] = Base64.encode64 Digest::MD5.hexdigest("thiswillnotmatchthehash")
|
138
168
|
subject.run
|
139
|
-
response.code.
|
169
|
+
expect(response.code).to eq 500
|
140
170
|
end
|
141
171
|
end
|
142
172
|
end
|
@@ -147,26 +177,26 @@ describe Webmachine::Decision::Flow do
|
|
147
177
|
it "should reply with 401 when the client is unauthorized" do
|
148
178
|
resource.auth = false
|
149
179
|
subject.run
|
150
|
-
response.code.
|
180
|
+
expect(response.code).to eq 401
|
151
181
|
end
|
152
182
|
|
153
183
|
it "should reply with 401 when the resource gives a challenge" do
|
154
184
|
resource.auth = "Basic realm=Webmachine"
|
155
185
|
subject.run
|
156
|
-
response.code.
|
157
|
-
response.headers['WWW-Authenticate'].
|
186
|
+
expect(response.code).to eq 401
|
187
|
+
expect(response.headers['WWW-Authenticate']).to eq "Basic realm=Webmachine"
|
158
188
|
end
|
159
189
|
|
160
190
|
it "should halt with the given code when the resource returns a status code" do
|
161
191
|
resource.auth = 400
|
162
192
|
subject.run
|
163
|
-
response.code.
|
193
|
+
expect(response.code).to eq 400
|
164
194
|
end
|
165
195
|
|
166
196
|
it "should not reply with 401 when the client is authorized" do
|
167
197
|
resource.auth = true
|
168
198
|
subject.run
|
169
|
-
response.code.
|
199
|
+
expect(response.code).to_not eq 401
|
170
200
|
end
|
171
201
|
end
|
172
202
|
|
@@ -176,19 +206,19 @@ describe Webmachine::Decision::Flow do
|
|
176
206
|
it "should reply with 403 when the request is forbidden" do
|
177
207
|
resource.forbid = true
|
178
208
|
subject.run
|
179
|
-
response.code.
|
209
|
+
expect(response.code).to eq 403
|
180
210
|
end
|
181
211
|
|
182
212
|
it "should not reply with 403 when the request is permitted" do
|
183
213
|
resource.forbid = false
|
184
214
|
subject.run
|
185
|
-
response.code.
|
215
|
+
expect(response.code).to_not eq 403
|
186
216
|
end
|
187
217
|
|
188
218
|
it "should halt with the given code when the resource returns a status code" do
|
189
219
|
resource.forbid = 400
|
190
220
|
subject.run
|
191
|
-
response.code.
|
221
|
+
expect(response.code).to eq 400
|
192
222
|
end
|
193
223
|
end
|
194
224
|
|
@@ -204,12 +234,12 @@ describe Webmachine::Decision::Flow do
|
|
204
234
|
it "should reply with 501 when an invalid Content-* header is present" do
|
205
235
|
headers['Content-Fail'] = "yup"
|
206
236
|
subject.run
|
207
|
-
response.code.
|
237
|
+
expect(response.code).to eq 501
|
208
238
|
end
|
209
239
|
|
210
240
|
it "should not reply with 501 when all Content-* headers are valid" do
|
211
241
|
subject.run
|
212
|
-
response.code.
|
242
|
+
expect(response.code).to_not eq 501
|
213
243
|
end
|
214
244
|
end
|
215
245
|
|
@@ -229,13 +259,13 @@ describe Webmachine::Decision::Flow do
|
|
229
259
|
it "should reply with 415 when the Content-Type is unknown" do
|
230
260
|
headers['Content-Type'] = "application/x-unknown-type"
|
231
261
|
subject.run
|
232
|
-
response.code.
|
262
|
+
expect(response.code).to eq 415
|
233
263
|
end
|
234
264
|
|
235
265
|
it "should not reply with 415 when the Content-Type is known" do
|
236
266
|
headers['Content-Type'] = "text/plain"
|
237
267
|
subject.run
|
238
|
-
response.code.
|
268
|
+
expect(response.code).to_not eq 415
|
239
269
|
end
|
240
270
|
end
|
241
271
|
|
@@ -254,7 +284,7 @@ describe Webmachine::Decision::Flow do
|
|
254
284
|
let(:body) { "Big" * 100 }
|
255
285
|
it "should reply with 413" do
|
256
286
|
subject.run
|
257
|
-
response.code.
|
287
|
+
expect(response.code).to eq 413
|
258
288
|
end
|
259
289
|
end
|
260
290
|
|
@@ -263,7 +293,7 @@ describe Webmachine::Decision::Flow do
|
|
263
293
|
|
264
294
|
it "should not reply with 413" do
|
265
295
|
subject.run
|
266
|
-
response.code.
|
296
|
+
expect(response.code).to_not eq 413
|
267
297
|
end
|
268
298
|
end
|
269
299
|
end
|
@@ -273,7 +303,7 @@ describe Webmachine::Decision::Flow do
|
|
273
303
|
let(:resource){ resource_with { def allowed_methods; %w[GET HEAD OPTIONS]; end } }
|
274
304
|
it "should reply with 200 when the request method is OPTIONS" do
|
275
305
|
subject.run
|
276
|
-
response.code.
|
306
|
+
expect(response.code).to eq 200
|
277
307
|
end
|
278
308
|
end
|
279
309
|
|
@@ -283,23 +313,23 @@ describe Webmachine::Decision::Flow do
|
|
283
313
|
it "should reply with 406 when the type is unacceptable" do
|
284
314
|
headers['Accept'] = "text/plain"
|
285
315
|
subject.run
|
286
|
-
response.code.
|
316
|
+
expect(response.code).to eq 406
|
287
317
|
end
|
288
318
|
|
289
319
|
it "should not reply with 406 when the type is acceptable" do
|
290
320
|
headers['Accept'] = "text/*"
|
291
321
|
subject.run
|
292
|
-
response.code.
|
293
|
-
response.headers['Content-Type'].
|
322
|
+
expect(response.code).to_not eq 406
|
323
|
+
expect(response.headers['Content-Type']).to eq "text/html"
|
294
324
|
end
|
295
325
|
end
|
296
326
|
|
297
327
|
context "when the Accept header does not exist" do
|
298
328
|
it "should not negotiate a media type" do
|
299
|
-
headers['Accept'].
|
300
|
-
subject.
|
329
|
+
expect(headers['Accept']).to be_nil
|
330
|
+
expect(subject).to_not receive(:c4)
|
301
331
|
subject.run
|
302
|
-
response.headers['Content-Type'].
|
332
|
+
expect(response.headers['Content-Type']).to eq 'text/html'
|
303
333
|
end
|
304
334
|
end
|
305
335
|
end
|
@@ -310,25 +340,25 @@ describe Webmachine::Decision::Flow do
|
|
310
340
|
it "should reply with 406 when the language is unacceptable" do
|
311
341
|
headers['Accept-Language'] = "es, de"
|
312
342
|
subject.run
|
313
|
-
response.code.
|
343
|
+
expect(response.code).to eq 406
|
314
344
|
end
|
315
345
|
|
316
346
|
it "should not reply with 406 when the language is acceptable" do
|
317
347
|
headers['Accept-Language'] = "en-GB, en;q=0.7"
|
318
348
|
subject.run
|
319
|
-
response.code.
|
320
|
-
response.headers['Content-Language'].
|
321
|
-
resource.instance_variable_get(:@language).
|
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'
|
322
352
|
end
|
323
353
|
end
|
324
354
|
|
325
355
|
context "when the Accept-Language header is absent" do
|
326
356
|
it "should not negotiate the language" do
|
327
|
-
headers['Accept-Language'].
|
328
|
-
subject.
|
357
|
+
expect(headers['Accept-Language']).to be_nil
|
358
|
+
expect(subject).to_not receive(:d5)
|
329
359
|
subject.run
|
330
|
-
response.headers['Content-Language'].
|
331
|
-
resource.instance_variable_get(:@language).
|
360
|
+
expect(response.headers['Content-Language']).to eq 'en-US'
|
361
|
+
expect(resource.instance_variable_get(:@language)).to eq 'en-US'
|
332
362
|
end
|
333
363
|
end
|
334
364
|
end
|
@@ -348,23 +378,23 @@ describe Webmachine::Decision::Flow do
|
|
348
378
|
it "should reply with 406 when the charset is unacceptable" do
|
349
379
|
headers['Accept-Charset'] = "utf-16"
|
350
380
|
subject.run
|
351
|
-
response.code.
|
381
|
+
expect(response.code).to eq 406
|
352
382
|
end
|
353
383
|
|
354
384
|
it "should not reply with 406 when the charset is acceptable" do
|
355
385
|
headers['Accept-Charset'] = "iso8859-1"
|
356
386
|
subject.run
|
357
|
-
response.code.
|
358
|
-
response.headers['Content-Type'].
|
387
|
+
expect(response.code).to_not eq 406
|
388
|
+
expect(response.headers['Content-Type']).to eq "text/html;charset=iso8859-1"
|
359
389
|
end
|
360
390
|
end
|
361
391
|
|
362
392
|
context "when the Accept-Charset header is absent" do
|
363
393
|
it "should not negotiate the language" do
|
364
|
-
headers['Accept-Charset'].
|
365
|
-
subject.
|
394
|
+
expect(headers['Accept-Charset']).to be_nil
|
395
|
+
expect(subject).to_not receive(:e6)
|
366
396
|
subject.run
|
367
|
-
response.headers['Content-Type'].
|
397
|
+
expect(response.headers['Content-Type']).to eq 'text/html;charset=iso8859-1'
|
368
398
|
end
|
369
399
|
end
|
370
400
|
end
|
@@ -382,27 +412,27 @@ describe Webmachine::Decision::Flow do
|
|
382
412
|
it "should reply with 406 if the encoding is unacceptable" do
|
383
413
|
headers['Accept-Encoding'] = 'deflate, identity;q=0.0'
|
384
414
|
subject.run
|
385
|
-
response.code.
|
415
|
+
expect(response.code).to eq 406
|
386
416
|
end
|
387
417
|
|
388
418
|
it "should not reply with 406 if the encoding is acceptable" do
|
389
419
|
headers['Accept-Encoding'] = 'gzip, deflate'
|
390
420
|
subject.run
|
391
|
-
response.code.
|
392
|
-
response.headers['Content-Encoding'].
|
421
|
+
expect(response.code).to_not eq 406
|
422
|
+
expect(response.headers['Content-Encoding']).to eq 'gzip'
|
393
423
|
# It should be compressed
|
394
|
-
response.body.
|
424
|
+
expect(response.body).to_not eq 'test resource'
|
395
425
|
end
|
396
426
|
end
|
397
427
|
|
398
428
|
context "when the Accept-Encoding header is not present" do
|
399
429
|
it "should not negotiate an encoding" do
|
400
|
-
headers['Accept-Encoding'].
|
401
|
-
subject.
|
430
|
+
expect(headers['Accept-Encoding']).to be_nil
|
431
|
+
expect(subject).to_not receive(:f7)
|
402
432
|
subject.run
|
403
|
-
response.code.
|
433
|
+
expect(response.code).to_not eq 406
|
404
434
|
# It should not be compressed
|
405
|
-
response.body.
|
435
|
+
expect(response.body).to eq 'test resource'
|
406
436
|
end
|
407
437
|
end
|
408
438
|
end
|
@@ -412,28 +442,28 @@ describe Webmachine::Decision::Flow do
|
|
412
442
|
|
413
443
|
it "should not enter conditional requests if missing (and eventually reply with 404)" do
|
414
444
|
resource.exist = false
|
415
|
-
subject.
|
445
|
+
expect(subject).to_not receive(:g8)
|
416
446
|
subject.run
|
417
|
-
response.code.
|
447
|
+
expect(response.code).to eq 404
|
418
448
|
end
|
419
449
|
|
420
450
|
it "should not reply with 404 if it does exist" do
|
421
451
|
resource.exist = true
|
422
|
-
subject.
|
452
|
+
expect(subject).to_not receive(:h7)
|
423
453
|
subject.run
|
424
|
-
response.code.
|
454
|
+
expect(response.code).to_not eq 404
|
425
455
|
end
|
426
456
|
|
427
457
|
it "should not reply with 404 for truthy non-booleans" do
|
428
458
|
resource.exist = []
|
429
459
|
subject.run
|
430
|
-
response.code.
|
460
|
+
expect(response.code).to_not eq 404
|
431
461
|
end
|
432
462
|
|
433
463
|
it "should reply with 404 for nil" do
|
434
464
|
resource.exist = nil
|
435
465
|
subject.run
|
436
|
-
response.code.
|
466
|
+
expect(response.code).to eq 404
|
437
467
|
end
|
438
468
|
end
|
439
469
|
|
@@ -441,26 +471,26 @@ describe Webmachine::Decision::Flow do
|
|
441
471
|
describe "#g8, #g9, #g10 (ETag match)" do
|
442
472
|
let(:resource) { resource_with { def generate_etag; "etag"; end } }
|
443
473
|
it "should skip ETag matching when If-Match is missing" do
|
444
|
-
headers['If-Match'].
|
445
|
-
subject.
|
446
|
-
subject.
|
474
|
+
expect(headers['If-Match']).to be_nil
|
475
|
+
expect(subject).to_not receive(:g9)
|
476
|
+
expect(subject).to_not receive(:g11)
|
447
477
|
subject.run
|
448
|
-
response.code.
|
478
|
+
expect(response.code).to_not eq 412
|
449
479
|
end
|
450
480
|
it "should not reply with 304 when If-Match is *" do
|
451
481
|
headers['If-Match'] = "*"
|
452
482
|
subject.run
|
453
|
-
response.code.
|
483
|
+
expect(response.code).to_not eq 412
|
454
484
|
end
|
455
485
|
it "should reply with 412 if the ETag is not in If-Match" do
|
456
486
|
headers['If-Match'] = '"notetag"'
|
457
487
|
subject.run
|
458
|
-
response.code.
|
488
|
+
expect(response.code).to eq 412
|
459
489
|
end
|
460
490
|
it "should not reply with 412 if the ETag is in If-Match" do
|
461
491
|
headers['If-Match'] = '"etag"'
|
462
492
|
subject.run
|
463
|
-
response.code.
|
493
|
+
expect(response.code).to_not eq 412
|
464
494
|
end
|
465
495
|
end
|
466
496
|
|
@@ -469,30 +499,30 @@ describe Webmachine::Decision::Flow do
|
|
469
499
|
before { @now = resource.now = Time.now }
|
470
500
|
|
471
501
|
it "should skip LM matching if IUMS is missing" do
|
472
|
-
headers['If-Unmodified-Since'].
|
473
|
-
subject.
|
474
|
-
subject.
|
502
|
+
expect(headers['If-Unmodified-Since']).to be_nil
|
503
|
+
expect(subject).to_not receive(:h11)
|
504
|
+
expect(subject).to_not receive(:h12)
|
475
505
|
subject.run
|
476
|
-
response.code.
|
506
|
+
expect(response.code).to_not eq 412
|
477
507
|
end
|
478
508
|
|
479
509
|
it "should skip LM matching if IUMS is an invalid date" do
|
480
510
|
headers['If-Unmodified-Since'] = "garbage"
|
481
|
-
subject.
|
511
|
+
expect(subject).to_not receive(:h12)
|
482
512
|
subject.run
|
483
|
-
response.code.
|
513
|
+
expect(response.code).to_not eq 412
|
484
514
|
end
|
485
515
|
|
486
516
|
it "should not reply with 412 if LM is <= IUMS" do
|
487
517
|
headers['If-Unmodified-Since'] = (@now + 100).httpdate
|
488
518
|
subject.run
|
489
|
-
response.code.
|
519
|
+
expect(response.code).to_not eq 412
|
490
520
|
end
|
491
521
|
|
492
522
|
it "should reply with 412 if LM is > IUMS" do
|
493
523
|
headers['If-Unmodified-Since'] = (@now - 100).httpdate
|
494
524
|
subject.run
|
495
|
-
response.code.
|
525
|
+
expect(response.code).to eq 412
|
496
526
|
end
|
497
527
|
end
|
498
528
|
|
@@ -506,18 +536,18 @@ describe Webmachine::Decision::Flow do
|
|
506
536
|
end
|
507
537
|
|
508
538
|
it "should skip ETag matching if If-None-Match is missing" do
|
509
|
-
headers['If-None-Match'].
|
539
|
+
expect(headers['If-None-Match']).to be_nil
|
510
540
|
%w{i13 k13 j18}.each do |m|
|
511
|
-
subject.
|
541
|
+
expect(subject).to_not receive(m.to_sym)
|
512
542
|
end
|
513
543
|
subject.run
|
514
|
-
[304, 412].
|
544
|
+
expect([304, 412]).to_not include(response.code)
|
515
545
|
end
|
516
546
|
|
517
547
|
it "should not reply with 412 or 304 if the ETag is not in If-None-Match" do
|
518
548
|
headers['If-None-Match'] = '"notetag"'
|
519
549
|
subject.run
|
520
|
-
[304, 412].
|
550
|
+
expect([304, 412]).to_not include(response.code)
|
521
551
|
end
|
522
552
|
|
523
553
|
context "when the method is GET or HEAD" do
|
@@ -528,7 +558,7 @@ describe Webmachine::Decision::Flow do
|
|
528
558
|
it "should reply with 304 when the ETag is in If-None-Match" do
|
529
559
|
headers['If-None-Match'] = '"etag", "foobar"'
|
530
560
|
end
|
531
|
-
after { subject.run; response.code.
|
561
|
+
after { subject.run; expect(response.code).to eq 304 }
|
532
562
|
end
|
533
563
|
|
534
564
|
context "when the method is not GET or HEAD" do
|
@@ -543,7 +573,27 @@ describe Webmachine::Decision::Flow do
|
|
543
573
|
it "should reply with 412 when the ETag is in If-None-Match" do
|
544
574
|
headers['If-None-Match'] = '"etag"'
|
545
575
|
end
|
546
|
-
after { subject.run; response.code.
|
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
|
547
597
|
end
|
548
598
|
end
|
549
599
|
|
@@ -551,41 +601,41 @@ describe Webmachine::Decision::Flow do
|
|
551
601
|
let(:resource) { resource_with { attr_accessor :now; def last_modified; @now; end } }
|
552
602
|
before { @now = resource.now = Time.now }
|
553
603
|
it "should skip LM matching if IMS is missing" do
|
554
|
-
headers['If-Modified-Since'].
|
604
|
+
expect(headers['If-Modified-Since']).to be_nil
|
555
605
|
%w{l14 l15 l17}.each do |m|
|
556
|
-
subject.
|
606
|
+
expect(subject).to_not receive(m.to_sym)
|
557
607
|
end
|
558
608
|
subject.run
|
559
|
-
response.code.
|
609
|
+
expect(response.code).to_not eq 304
|
560
610
|
end
|
561
611
|
|
562
612
|
it "should skip LM matching if IMS is an invalid date" do
|
563
613
|
headers['If-Modified-Since'] = "garbage"
|
564
614
|
%w{l15 l17}.each do |m|
|
565
|
-
subject.
|
615
|
+
expect(subject).to_not receive(m.to_sym)
|
566
616
|
end
|
567
617
|
subject.run
|
568
|
-
response.code.
|
618
|
+
expect(response.code).to_not eq 304
|
569
619
|
end
|
570
620
|
|
571
621
|
it "should skip LM matching if IMS is later than current time" do
|
572
622
|
headers['If-Modified-Since'] = (@now + 1000).httpdate
|
573
|
-
subject.
|
623
|
+
expect(subject).to_not receive(:l17)
|
574
624
|
subject.run
|
575
|
-
response.code.
|
625
|
+
expect(response.code).to_not eq 304
|
576
626
|
end
|
577
627
|
|
578
628
|
it "should reply with 304 if LM is <= IMS" do
|
579
629
|
headers['If-Modified-Since'] = (@now - 1).httpdate
|
580
630
|
resource.now = @now - 1000
|
581
631
|
subject.run
|
582
|
-
response.code.
|
632
|
+
expect(response.code).to eq 304
|
583
633
|
end
|
584
634
|
|
585
635
|
it "should not reply with 304 if LM is > IMS" do
|
586
636
|
headers['If-Modified-Since'] = (@now - 1000).httpdate
|
587
637
|
subject.run
|
588
|
-
response.code.
|
638
|
+
expect(response.code).to_not eq 304
|
589
639
|
end
|
590
640
|
end
|
591
641
|
|
@@ -595,13 +645,13 @@ describe Webmachine::Decision::Flow do
|
|
595
645
|
it "should reply with 412 when the If-Match header is *" do
|
596
646
|
headers['If-Match'] = '"*"'
|
597
647
|
subject.run
|
598
|
-
response.code.
|
648
|
+
expect(response.code).to eq 412
|
599
649
|
end
|
600
650
|
|
601
651
|
it "should not reply with 412 when the If-Match header is missing or not *" do
|
602
652
|
headers['If-Match'] = ['"etag"', nil][rand(1)]
|
603
653
|
subject.run
|
604
|
-
response.code.
|
654
|
+
expect(response.code).to_not eq 412
|
605
655
|
end
|
606
656
|
end
|
607
657
|
|
@@ -619,22 +669,22 @@ describe Webmachine::Decision::Flow do
|
|
619
669
|
let(:method){ "PUT" }
|
620
670
|
|
621
671
|
it "should not reach state k7" do
|
622
|
-
subject.
|
672
|
+
expect(subject).to_not receive(:k7)
|
623
673
|
subject.run
|
624
674
|
end
|
625
675
|
|
626
|
-
after { [404, 410, 303].
|
676
|
+
after { expect([404, 410, 303]).to_not include(response.code) }
|
627
677
|
end
|
628
678
|
|
629
679
|
context "when the method is not PUT" do
|
630
680
|
let(:method){ %W{GET HEAD POST DELETE}[rand(4)] }
|
631
681
|
|
632
682
|
it "should not reach state i4" do
|
633
|
-
subject.
|
683
|
+
expect(subject).to_not receive(:i4)
|
634
684
|
subject.run
|
635
685
|
end
|
636
686
|
|
637
|
-
after { response.code.
|
687
|
+
after { expect(response.code).to_not eq 409 }
|
638
688
|
end
|
639
689
|
end
|
640
690
|
|
@@ -653,14 +703,14 @@ describe Webmachine::Decision::Flow do
|
|
653
703
|
it "should reply with 301 when the resource has moved" do
|
654
704
|
resource.location = URI.parse("http://localhost:8098/newuri")
|
655
705
|
subject.run
|
656
|
-
response.code.
|
657
|
-
response.headers['Location'].
|
706
|
+
expect(response.code).to eq 301
|
707
|
+
expect(response.headers['Location']).to eq resource.location.to_s
|
658
708
|
end
|
659
709
|
|
660
710
|
it "should not reply with 301 when resource has not moved" do
|
661
711
|
resource.location = false
|
662
712
|
subject.run
|
663
|
-
response.code.
|
713
|
+
expect(response.code).to_not eq 301
|
664
714
|
end
|
665
715
|
end
|
666
716
|
|
@@ -682,13 +732,13 @@ describe Webmachine::Decision::Flow do
|
|
682
732
|
it "should reply with 301 when the resource has moved permanently" do
|
683
733
|
uri = resource.moved_perm = URI.parse("http://www.google.com/")
|
684
734
|
subject.run
|
685
|
-
response.code.
|
686
|
-
response.headers['Location'].
|
735
|
+
expect(response.code).to eq 301
|
736
|
+
expect(response.headers['Location']).to eq uri.to_s
|
687
737
|
end
|
688
738
|
it "should not reply with 301 when the resource has not moved permanently" do
|
689
739
|
resource.moved_perm = false
|
690
740
|
subject.run
|
691
|
-
response.code.
|
741
|
+
expect(response.code).to_not eq 301
|
692
742
|
end
|
693
743
|
end
|
694
744
|
|
@@ -697,34 +747,34 @@ describe Webmachine::Decision::Flow do
|
|
697
747
|
it "should reply with 307 when the resource has moved temporarily" do
|
698
748
|
uri = resource.moved_temp = URI.parse("http://www.basho.com/")
|
699
749
|
subject.run
|
700
|
-
response.code.
|
701
|
-
response.headers['Location'].
|
750
|
+
expect(response.code).to eq 307
|
751
|
+
expect(response.headers['Location']).to eq uri.to_s
|
702
752
|
end
|
703
753
|
it "should not reply with 307 when the resource has not moved temporarily" do
|
704
754
|
resource.moved_temp = false
|
705
755
|
subject.run
|
706
|
-
response.code.
|
756
|
+
expect(response.code).to_not eq 307
|
707
757
|
end
|
708
758
|
end
|
709
759
|
|
710
760
|
describe "#m5 (POST?), #n5 (POST to missing resource?)" do
|
711
761
|
before { resource.moved_perm = resource.moved_temp = false }
|
712
762
|
it "should reply with 410 when the method is not POST" do
|
713
|
-
method.
|
763
|
+
expect(method).to_not eq "POST"
|
714
764
|
subject.run
|
715
|
-
response.code.
|
765
|
+
expect(response.code).to eq 410
|
716
766
|
end
|
717
767
|
it "should reply with 410 when the resource disallows missing POSTs" do
|
718
768
|
@method = "POST"
|
719
769
|
resource.allow_missing = false
|
720
770
|
subject.run
|
721
|
-
response.code.
|
771
|
+
expect(response.code).to eq 410
|
722
772
|
end
|
723
773
|
it "should not reply with 410 when the resource allows missing POSTs" do
|
724
774
|
@method = "POST"
|
725
775
|
resource.allow_missing = true
|
726
776
|
subject.run
|
727
|
-
response.code.
|
777
|
+
expect(response.code).to eq 410
|
728
778
|
end
|
729
779
|
end
|
730
780
|
end
|
@@ -741,21 +791,21 @@ describe Webmachine::Decision::Flow do
|
|
741
791
|
end
|
742
792
|
let(:method){ @method || "GET" }
|
743
793
|
it "should reply with 404 when the method is not POST" do
|
744
|
-
method.
|
794
|
+
expect(method).to_not eq "POST"
|
745
795
|
subject.run
|
746
|
-
response.code.
|
796
|
+
expect(response.code).to eq 404
|
747
797
|
end
|
748
798
|
it "should reply with 404 when the resource disallows missing POSTs" do
|
749
799
|
@method = "POST"
|
750
800
|
resource.allow_missing = false
|
751
801
|
subject.run
|
752
|
-
response.code.
|
802
|
+
expect(response.code).to eq 404
|
753
803
|
end
|
754
804
|
it "should not reply with 404 when the resource allows missing POSTs" do
|
755
805
|
@method = "POST"
|
756
806
|
resource.allow_missing = true
|
757
807
|
subject.run
|
758
|
-
response.code.
|
808
|
+
expect(response.code).to_not eq 404
|
759
809
|
end
|
760
810
|
end
|
761
811
|
|
@@ -771,12 +821,12 @@ describe Webmachine::Decision::Flow do
|
|
771
821
|
it "should reply with 409 if the resource is in conflict" do
|
772
822
|
resource.conflict = true
|
773
823
|
subject.run
|
774
|
-
response.code.
|
824
|
+
expect(response.code).to eq 409
|
775
825
|
end
|
776
826
|
it "should not reply with 409 if the resource is in conflict" do
|
777
827
|
resource.conflict = false
|
778
828
|
subject.run
|
779
|
-
response.code.
|
829
|
+
expect(response.code).to_not eq 409
|
780
830
|
end
|
781
831
|
end
|
782
832
|
|
@@ -801,14 +851,14 @@ describe Webmachine::Decision::Flow do
|
|
801
851
|
it "should reply with 303 if the resource redirected" do
|
802
852
|
resource.new_loc = URI.parse("/foo/bar")
|
803
853
|
subject.run
|
804
|
-
response.code.
|
805
|
-
response.headers['Location'].
|
854
|
+
expect(response.code).to eq 303
|
855
|
+
expect(response.headers['Location']).to eq "/foo/bar"
|
806
856
|
end
|
807
857
|
|
808
858
|
it "should not reply with 303 if the resource did not redirect" do
|
809
859
|
resource.new_loc = nil
|
810
860
|
subject.run
|
811
|
-
response.code.
|
861
|
+
expect(response.code).to_not eq 303
|
812
862
|
end
|
813
863
|
end
|
814
864
|
end
|
@@ -845,13 +895,13 @@ describe Webmachine::Decision::Flow do
|
|
845
895
|
resource.exist = e
|
846
896
|
resource.new_loc = "http://ruby-doc.org/"
|
847
897
|
subject.run
|
848
|
-
response.code.
|
898
|
+
expect(response.code).to eq 201
|
849
899
|
end
|
850
900
|
it "should not reply with 201 when the Location header has been set" do
|
851
901
|
resource.exist = e
|
852
902
|
subject.run
|
853
|
-
response.headers['Location'].
|
854
|
-
response.code.
|
903
|
+
expect(response.headers['Location']).to be_nil
|
904
|
+
expect(response.code).to_not eq 201
|
855
905
|
end
|
856
906
|
end
|
857
907
|
end
|
@@ -866,19 +916,19 @@ describe Webmachine::Decision::Flow do
|
|
866
916
|
resource.new_loc = created = "/foo/bar/baz"
|
867
917
|
resource.create = true
|
868
918
|
subject.run
|
869
|
-
response.code.
|
870
|
-
response.headers['Location'].
|
919
|
+
expect(response.code).to eq 201
|
920
|
+
expect(response.headers['Location']).to eq created
|
871
921
|
end
|
872
922
|
it "should reply with 500 when post_is_create is true and create_path returns nil" do
|
873
923
|
resource.create = true
|
874
924
|
subject.run
|
875
|
-
response.code.
|
876
|
-
response.error.
|
925
|
+
expect(response.code).to eq 500
|
926
|
+
expect(response.error).to_not be_nil
|
877
927
|
end
|
878
928
|
it "should not reply with 201 when post_is_create is false" do
|
879
929
|
resource.create = false
|
880
930
|
subject.run
|
881
|
-
response.code.
|
931
|
+
expect(response.code).to_not eq 201
|
882
932
|
end
|
883
933
|
end
|
884
934
|
end
|
@@ -897,12 +947,12 @@ describe Webmachine::Decision::Flow do
|
|
897
947
|
it "should reply with 409 if the resource is in conflict" do
|
898
948
|
resource.conflict = true
|
899
949
|
subject.run
|
900
|
-
response.code.
|
950
|
+
expect(response.code).to eq 409
|
901
951
|
end
|
902
952
|
it "should not reply with 409 if the resource is in conflict" do
|
903
953
|
resource.conflict = false
|
904
954
|
subject.run
|
905
|
-
response.code.
|
955
|
+
expect(response.code).to_not eq 409
|
906
956
|
end
|
907
957
|
end
|
908
958
|
|
@@ -919,23 +969,23 @@ describe Webmachine::Decision::Flow do
|
|
919
969
|
it "should not reply with 202 if the method is not DELETE" do
|
920
970
|
@method = "GET"
|
921
971
|
subject.run
|
922
|
-
response.code.
|
972
|
+
expect(response.code).to_not eq 202
|
923
973
|
end
|
924
974
|
it "should reply with 500 if the DELETE fails" do
|
925
975
|
resource.deleted = false
|
926
976
|
subject.run
|
927
|
-
response.code.
|
977
|
+
expect(response.code).to eq 500
|
928
978
|
end
|
929
979
|
it "should reply with 202 if the DELETE succeeds but is not complete" do
|
930
980
|
resource.deleted = true
|
931
981
|
resource.completed = false
|
932
982
|
subject.run
|
933
|
-
response.code.
|
983
|
+
expect(response.code).to eq 202
|
934
984
|
end
|
935
985
|
it "should not reply with 202 if the DELETE succeeds and completes" do
|
936
986
|
resource.completed = resource.deleted = true
|
937
987
|
subject.run
|
938
|
-
response.code.
|
988
|
+
expect(response.code).to_not eq 202
|
939
989
|
end
|
940
990
|
end
|
941
991
|
|
@@ -980,13 +1030,13 @@ describe Webmachine::Decision::Flow do
|
|
980
1030
|
resource.multiple = false
|
981
1031
|
subject.run
|
982
1032
|
puts response.error if response.code == 500
|
983
|
-
response.code.
|
1033
|
+
expect(response.code).to eq 200
|
984
1034
|
end
|
985
1035
|
it "should reply with 300 if there are multiple representations" do
|
986
1036
|
resource.multiple = true
|
987
1037
|
subject.run
|
988
1038
|
puts response.error if response.code == 500
|
989
|
-
response.code.
|
1039
|
+
expect(response.code).to eq 300
|
990
1040
|
end
|
991
1041
|
end
|
992
1042
|
end
|
@@ -1028,7 +1078,7 @@ describe Webmachine::Decision::Flow do
|
|
1028
1078
|
@method = m
|
1029
1079
|
resource.exist = e
|
1030
1080
|
subject.run
|
1031
|
-
response.code.
|
1081
|
+
expect(response.code).to_not eq 204
|
1032
1082
|
end
|
1033
1083
|
end
|
1034
1084
|
end
|
@@ -1044,7 +1094,7 @@ describe Webmachine::Decision::Flow do
|
|
1044
1094
|
@method = m
|
1045
1095
|
resource.exist = e
|
1046
1096
|
subject.run
|
1047
|
-
response.code.
|
1097
|
+
expect(response.code).to eq 204
|
1048
1098
|
end
|
1049
1099
|
end
|
1050
1100
|
end
|
@@ -1061,13 +1111,13 @@ describe Webmachine::Decision::Flow do
|
|
1061
1111
|
end
|
1062
1112
|
|
1063
1113
|
it "calls handle_exception" do
|
1064
|
-
resource.
|
1114
|
+
expect(resource).to receive(:handle_exception).with instance_of(RuntimeError)
|
1065
1115
|
subject.run
|
1066
1116
|
end
|
1067
1117
|
|
1068
1118
|
it "sets the response code to 500" do
|
1069
1119
|
subject.run
|
1070
|
-
response.code.
|
1120
|
+
expect(response.code).to eq 500
|
1071
1121
|
end
|
1072
1122
|
end
|
1073
1123
|
|
@@ -1086,12 +1136,12 @@ describe Webmachine::Decision::Flow do
|
|
1086
1136
|
|
1087
1137
|
it "can define a response body" do
|
1088
1138
|
subject.run
|
1089
|
-
response.body.
|
1139
|
+
expect(response.body).to eq "error"
|
1090
1140
|
end
|
1091
1141
|
|
1092
1142
|
it "sets the response code to 500" do
|
1093
1143
|
subject.run
|
1094
|
-
response.code.
|
1144
|
+
expect(response.code).to eq 500
|
1095
1145
|
end
|
1096
1146
|
end
|
1097
1147
|
end
|