alephant-broker 3.11.0 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60b6af683863285e27316bde5b7392d93439b20e
|
4
|
+
data.tar.gz: 7052275d91783c7b02ab1996cdced8e606b58e51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8307c2cbe4304ebf233878d20026529e21302cec83e78132b43a6bb7da377da977f85d2f48b447c76d4e3647d2f7915659ec56cdbb9b4ecda8b4e5e26b26777
|
7
|
+
data.tar.gz: 0c2ba375708bfb96376954db24ab557b8bc615db013d9e85bd4ad3833d5f03861c6efc1074cd72e4a890d25ca42181f57caaf5da74ea29c458bd16ad71a7dafe
|
@@ -25,7 +25,7 @@ module Alephant
|
|
25
25
|
@content = STATUS_CODE_MAPPING[status]
|
26
26
|
@headers = {
|
27
27
|
"Content-Type" => content_type,
|
28
|
-
"Access-Control-Allow-Headers" => "If-None-Match",
|
28
|
+
"Access-Control-Allow-Headers" => "If-None-Match, If-Modified-Since",
|
29
29
|
"Access-Control-Allow-Origin" => "*"
|
30
30
|
}
|
31
31
|
headers.merge!(Broker.config[:headers]) if Broker.config.has_key?(:headers)
|
@@ -62,6 +62,7 @@ module Alephant
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def self.component_not_modified(headers, request_env)
|
65
|
+
return false unless allow_not_modified_response_status
|
65
66
|
return false if request_env.post?
|
66
67
|
return false if request_env.if_modified_since.nil? && request_env.if_none_match.nil?
|
67
68
|
|
@@ -76,6 +77,10 @@ module Alephant
|
|
76
77
|
etag.to_s.gsub(/\A"|"\Z/, '')
|
77
78
|
end
|
78
79
|
|
80
|
+
def self.allow_not_modified_response_status
|
81
|
+
Broker.config[:allow_not_modified_response_status] || false
|
82
|
+
end
|
83
|
+
|
79
84
|
def log
|
80
85
|
logger.metric "BrokerNon200Response#{status}"
|
81
86
|
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
require "alephant/broker"
|
3
|
+
|
4
|
+
describe Alephant::Broker::Application do
|
5
|
+
include Rack::Test::Methods
|
6
|
+
|
7
|
+
let(:options) do
|
8
|
+
{
|
9
|
+
:lookup_table_name => "test_table",
|
10
|
+
:bucket_id => "test_bucket",
|
11
|
+
:path => "bucket_path",
|
12
|
+
:allow_not_modified_response_status => true
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:app) do
|
17
|
+
described_class.new(
|
18
|
+
Alephant::Broker::LoadStrategy::S3::Sequenced.new,
|
19
|
+
options
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:content) do
|
24
|
+
AWS::Core::Data.new(
|
25
|
+
:content_type => "test/content",
|
26
|
+
:content => "Test",
|
27
|
+
:meta => {
|
28
|
+
"head_ETag" => "123",
|
29
|
+
"head_Last-Modified" => "Mon, 11 Apr 2016 10:39:57 GMT"
|
30
|
+
}
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:sequencer_double) do
|
35
|
+
instance_double(
|
36
|
+
"Alephant::Sequencer::Sequencer",
|
37
|
+
:get_last_seen => "111"
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
let(:lookup_location_double) do
|
42
|
+
instance_double("Alephant::Lookup::Location", :location => "test/location")
|
43
|
+
end
|
44
|
+
let(:lookup_helper_double) do
|
45
|
+
instance_double(
|
46
|
+
"Alephant::Lookup::LookupHelper",
|
47
|
+
:read => lookup_location_double
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
let(:s3_double) { instance_double("Alephant::Storage", :get => content) }
|
52
|
+
|
53
|
+
let(:not_modified_status_code) { Alephant::Broker::Response::Base::NOT_MODIFIED_STATUS_CODE }
|
54
|
+
|
55
|
+
before do
|
56
|
+
allow_any_instance_of(Logger).to receive(:info)
|
57
|
+
allow_any_instance_of(Logger).to receive(:debug)
|
58
|
+
allow(Alephant::Lookup).to receive(:create) { lookup_helper_double }
|
59
|
+
allow(Alephant::Sequencer).to receive(:create) { sequencer_double }
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "single component not modified response" do
|
63
|
+
before do
|
64
|
+
allow(Alephant::Storage).to receive(:new) { s3_double }
|
65
|
+
get(
|
66
|
+
"/component/test_component",
|
67
|
+
{},
|
68
|
+
{
|
69
|
+
"HTTP_IF_MODIFIED_SINCE" => "Mon, 11 Apr 2016 10:39:57 GMT"
|
70
|
+
}
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
specify { expect(last_response.status).to eql not_modified_status_code }
|
75
|
+
specify { expect(last_response.body).to eql "" }
|
76
|
+
specify { expect(last_response.headers).to_not include("Cache-Control") }
|
77
|
+
specify { expect(last_response.headers).to_not include("Pragma") }
|
78
|
+
specify { expect(last_response.headers).to_not include("Expires") }
|
79
|
+
specify { expect(last_response.headers["ETag"]).to eq("123") }
|
80
|
+
specify { expect(last_response.headers["Last-Modified"]).to eq("Mon, 11 Apr 2016 10:39:57 GMT") }
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "Components POST unmodified '/components' response" do
|
84
|
+
let(:fixture_path) { "#{File.dirname(__FILE__)}/../fixtures/json" }
|
85
|
+
let(:batch_json) { IO.read("#{fixture_path}/batch.json").strip }
|
86
|
+
let(:batch_compiled_json) { IO.read("#{fixture_path}/batch_compiled.json").strip }
|
87
|
+
let(:s3_double_with_etag) { instance_double("Alephant::Storage") }
|
88
|
+
let(:lookup_location_double_for_options_request) do
|
89
|
+
instance_double("Alephant::Lookup::Location", :location => "test/location/with/options")
|
90
|
+
end
|
91
|
+
|
92
|
+
before do
|
93
|
+
allow(lookup_helper_double).to receive(:read)
|
94
|
+
.with("ni_council_results_table", { :foo => "bar" }, "111")
|
95
|
+
.and_return(lookup_location_double_for_options_request)
|
96
|
+
|
97
|
+
allow(s3_double_with_etag).to receive(:get)
|
98
|
+
.with("test/location")
|
99
|
+
.and_return(
|
100
|
+
content
|
101
|
+
)
|
102
|
+
|
103
|
+
allow(s3_double_with_etag).to receive(:get)
|
104
|
+
.with("test/location/with/options")
|
105
|
+
.and_return(
|
106
|
+
AWS::Core::Data.new(
|
107
|
+
:content_type => "test/content",
|
108
|
+
:content => "Test",
|
109
|
+
:meta => {
|
110
|
+
"head_ETag" => "abc",
|
111
|
+
"head_Last-Modified" => "Mon, 11 Apr 2016 09:39:57 GMT"
|
112
|
+
}
|
113
|
+
)
|
114
|
+
)
|
115
|
+
|
116
|
+
allow(Alephant::Storage).to receive(:new) { s3_double_with_etag }
|
117
|
+
end
|
118
|
+
|
119
|
+
context "when requesting an unmodified response" do
|
120
|
+
let(:path) { "/components/batch" }
|
121
|
+
let(:content_type) { "application/json" }
|
122
|
+
let(:etag) { '"34774567db979628363e6e865127623f"' }
|
123
|
+
|
124
|
+
before do
|
125
|
+
post(path, batch_json,
|
126
|
+
"CONTENT_TYPE" => content_type,
|
127
|
+
"HTTP_IF_NONE_MATCH" => etag)
|
128
|
+
end
|
129
|
+
|
130
|
+
specify { expect(last_response.status).to eql 200 }
|
131
|
+
specify { expect(last_response.body).to eq batch_compiled_json }
|
132
|
+
|
133
|
+
describe "response should have headers" do
|
134
|
+
it "should have a Content-Type header" do
|
135
|
+
expect(last_response.headers).to include("Content-Type")
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should have ETag cache header" do
|
139
|
+
expect(last_response.headers["ETag"]).to eq(etag)
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should have most recent Last-Modified header" do
|
143
|
+
expect(last_response.headers["Last-Modified"]).to eq("Mon, 11 Apr 2016 10:39:57 GMT")
|
144
|
+
end
|
145
|
+
|
146
|
+
it "shoud not have no cache headers" do
|
147
|
+
expect(last_response.headers).to_not include("Cache-Control")
|
148
|
+
expect(last_response.headers).to_not include("Pragma")
|
149
|
+
expect(last_response.headers).to_not include("Expires")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "Components GET unmodified '/components' response" do
|
156
|
+
let(:fixture_path) { "#{File.dirname(__FILE__)}/../fixtures/json" }
|
157
|
+
let(:batch_compiled_json) { IO.read("#{fixture_path}/batch_compiled.json").strip }
|
158
|
+
let(:s3_double_with_etag) { instance_double("Alephant::Storage") }
|
159
|
+
let(:lookup_location_double_for_options_request) do
|
160
|
+
instance_double("Alephant::Lookup::Location", :location => "test/location/with/options")
|
161
|
+
end
|
162
|
+
|
163
|
+
before do
|
164
|
+
allow(lookup_helper_double).to receive(:read)
|
165
|
+
.with("ni_council_results_table", { :foo => "bar" }, "111")
|
166
|
+
.and_return(lookup_location_double_for_options_request)
|
167
|
+
|
168
|
+
allow(s3_double_with_etag).to receive(:get)
|
169
|
+
.with("test/location")
|
170
|
+
.and_return(
|
171
|
+
content
|
172
|
+
)
|
173
|
+
|
174
|
+
allow(s3_double_with_etag).to receive(:get)
|
175
|
+
.with("test/location/with/options")
|
176
|
+
.and_return(
|
177
|
+
AWS::Core::Data.new(
|
178
|
+
:content_type => "test/content",
|
179
|
+
:content => "Test",
|
180
|
+
:meta => {
|
181
|
+
"head_ETag" => "abc",
|
182
|
+
"head_Last-Modified" => "Mon, 11 Apr 2016 09:39:57 GMT"
|
183
|
+
}
|
184
|
+
)
|
185
|
+
)
|
186
|
+
|
187
|
+
allow(Alephant::Storage).to receive(:new) { s3_double_with_etag }
|
188
|
+
end
|
189
|
+
|
190
|
+
context "when requesting an unmodified response with GET" do
|
191
|
+
let(:path) { "/components/batch?batch_id=baz&components[ni_council_results_table][component]=ni_council_results_table&components[ni_council_results_table][options][foo]=bar&components[ni_council_results_table_no_options][component]=ni_council_results_table" }
|
192
|
+
let(:content_type) { "application/json" }
|
193
|
+
let(:etag) { '"34774567db979628363e6e865127623f"' }
|
194
|
+
|
195
|
+
before do
|
196
|
+
get(
|
197
|
+
path,
|
198
|
+
{},
|
199
|
+
{
|
200
|
+
"CONTENT_TYPE" => content_type,
|
201
|
+
"HTTP_IF_NONE_MATCH" => etag
|
202
|
+
}
|
203
|
+
)
|
204
|
+
end
|
205
|
+
|
206
|
+
specify { expect(last_response.status).to eql not_modified_status_code }
|
207
|
+
specify { expect(last_response.body).to eq "" }
|
208
|
+
|
209
|
+
describe "response should have headers" do
|
210
|
+
it "should not have a Content-Type header" do
|
211
|
+
expect(last_response.headers).to_not include("Content-Type")
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should have ETag cache header" do
|
215
|
+
expect(last_response.headers["ETag"]).to eq(etag)
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should have most recent Last-Modified header" do
|
219
|
+
expect(last_response.headers["Last-Modified"]).to eq("Mon, 11 Apr 2016 10:39:57 GMT")
|
220
|
+
end
|
221
|
+
|
222
|
+
it "shoud not have no cache headers" do
|
223
|
+
expect(last_response.headers).to_not include("Cache-Control")
|
224
|
+
expect(last_response.headers).to_not include("Pragma")
|
225
|
+
expect(last_response.headers).to_not include("Expires")
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
@@ -96,27 +96,6 @@ describe Alephant::Broker::Application do
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
-
describe "single component not modified response" do
|
100
|
-
before do
|
101
|
-
allow(Alephant::Storage).to receive(:new) { s3_double }
|
102
|
-
get(
|
103
|
-
"/component/test_component",
|
104
|
-
{},
|
105
|
-
{
|
106
|
-
"HTTP_IF_MODIFIED_SINCE" => "Mon, 11 Apr 2016 10:39:57 GMT"
|
107
|
-
}
|
108
|
-
)
|
109
|
-
end
|
110
|
-
|
111
|
-
specify { expect(last_response.status).to eql not_modified_status_code }
|
112
|
-
specify { expect(last_response.body).to eql "" }
|
113
|
-
specify { expect(last_response.headers).to_not include("Cache-Control") }
|
114
|
-
specify { expect(last_response.headers).to_not include("Pragma") }
|
115
|
-
specify { expect(last_response.headers).to_not include("Expires") }
|
116
|
-
specify { expect(last_response.headers["ETag"]).to eq("123") }
|
117
|
-
specify { expect(last_response.headers["Last-Modified"]).to eq("Mon, 11 Apr 2016 10:39:57 GMT") }
|
118
|
-
end
|
119
|
-
|
120
99
|
describe "Components endpoint '/components' POST" do
|
121
100
|
let(:fixture_path) { "#{File.dirname(__FILE__)}/../fixtures/json" }
|
122
101
|
let(:batch_json) do
|
@@ -169,78 +148,6 @@ describe Alephant::Broker::Application do
|
|
169
148
|
end
|
170
149
|
end
|
171
150
|
|
172
|
-
describe "Components POST unmodified '/components' response" do
|
173
|
-
let(:fixture_path) { "#{File.dirname(__FILE__)}/../fixtures/json" }
|
174
|
-
let(:batch_json) { IO.read("#{fixture_path}/batch.json").strip }
|
175
|
-
let(:batch_compiled_json) { IO.read("#{fixture_path}/batch_compiled.json").strip }
|
176
|
-
let(:s3_double_with_etag) { instance_double("Alephant::Storage") }
|
177
|
-
let(:lookup_location_double_for_options_request) do
|
178
|
-
instance_double("Alephant::Lookup::Location", :location => "test/location/with/options")
|
179
|
-
end
|
180
|
-
|
181
|
-
before do
|
182
|
-
allow(lookup_helper_double).to receive(:read)
|
183
|
-
.with("ni_council_results_table", { :foo => "bar" }, "111")
|
184
|
-
.and_return(lookup_location_double_for_options_request)
|
185
|
-
|
186
|
-
allow(s3_double_with_etag).to receive(:get)
|
187
|
-
.with("test/location")
|
188
|
-
.and_return(
|
189
|
-
content
|
190
|
-
)
|
191
|
-
|
192
|
-
allow(s3_double_with_etag).to receive(:get)
|
193
|
-
.with("test/location/with/options")
|
194
|
-
.and_return(
|
195
|
-
AWS::Core::Data.new(
|
196
|
-
:content_type => "test/content",
|
197
|
-
:content => "Test",
|
198
|
-
:meta => {
|
199
|
-
"head_ETag" => "abc",
|
200
|
-
"head_Last-Modified" => "Mon, 11 Apr 2016 09:39:57 GMT"
|
201
|
-
}
|
202
|
-
)
|
203
|
-
)
|
204
|
-
|
205
|
-
allow(Alephant::Storage).to receive(:new) { s3_double_with_etag }
|
206
|
-
end
|
207
|
-
|
208
|
-
context "when requesting an unmodified response" do
|
209
|
-
let(:path) { "/components/batch" }
|
210
|
-
let(:content_type) { "application/json" }
|
211
|
-
let(:etag) { '"34774567db979628363e6e865127623f"' }
|
212
|
-
|
213
|
-
before do
|
214
|
-
post(path, batch_json,
|
215
|
-
"CONTENT_TYPE" => content_type,
|
216
|
-
"HTTP_IF_NONE_MATCH" => etag)
|
217
|
-
end
|
218
|
-
|
219
|
-
specify { expect(last_response.status).to eql 200 }
|
220
|
-
specify { expect(last_response.body).to eq batch_compiled_json }
|
221
|
-
|
222
|
-
describe "response should have headers" do
|
223
|
-
it "should have a Content-Type header" do
|
224
|
-
expect(last_response.headers).to include("Content-Type")
|
225
|
-
end
|
226
|
-
|
227
|
-
it "should have ETag cache header" do
|
228
|
-
expect(last_response.headers["ETag"]).to eq(etag)
|
229
|
-
end
|
230
|
-
|
231
|
-
it "should have most recent Last-Modified header" do
|
232
|
-
expect(last_response.headers["Last-Modified"]).to eq("Mon, 11 Apr 2016 10:39:57 GMT")
|
233
|
-
end
|
234
|
-
|
235
|
-
it "shoud not have no cache headers" do
|
236
|
-
expect(last_response.headers).to_not include("Cache-Control")
|
237
|
-
expect(last_response.headers).to_not include("Pragma")
|
238
|
-
expect(last_response.headers).to_not include("Expires")
|
239
|
-
end
|
240
|
-
end
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
151
|
describe "Components endpoint '/components' GET" do
|
245
152
|
let(:fixture_path) { "#{File.dirname(__FILE__)}/../fixtures/json" }
|
246
153
|
let(:batch_compiled_json) do
|
@@ -290,82 +197,6 @@ describe Alephant::Broker::Application do
|
|
290
197
|
end
|
291
198
|
end
|
292
199
|
|
293
|
-
describe "Components GET unmodified '/components' response" do
|
294
|
-
let(:fixture_path) { "#{File.dirname(__FILE__)}/../fixtures/json" }
|
295
|
-
let(:batch_compiled_json) { IO.read("#{fixture_path}/batch_compiled.json").strip }
|
296
|
-
let(:s3_double_with_etag) { instance_double("Alephant::Storage") }
|
297
|
-
let(:lookup_location_double_for_options_request) do
|
298
|
-
instance_double("Alephant::Lookup::Location", :location => "test/location/with/options")
|
299
|
-
end
|
300
|
-
|
301
|
-
before do
|
302
|
-
allow(lookup_helper_double).to receive(:read)
|
303
|
-
.with("ni_council_results_table", { :foo => "bar" }, "111")
|
304
|
-
.and_return(lookup_location_double_for_options_request)
|
305
|
-
|
306
|
-
allow(s3_double_with_etag).to receive(:get)
|
307
|
-
.with("test/location")
|
308
|
-
.and_return(
|
309
|
-
content
|
310
|
-
)
|
311
|
-
|
312
|
-
allow(s3_double_with_etag).to receive(:get)
|
313
|
-
.with("test/location/with/options")
|
314
|
-
.and_return(
|
315
|
-
AWS::Core::Data.new(
|
316
|
-
:content_type => "test/content",
|
317
|
-
:content => "Test",
|
318
|
-
:meta => {
|
319
|
-
"head_ETag" => "abc",
|
320
|
-
"head_Last-Modified" => "Mon, 11 Apr 2016 09:39:57 GMT"
|
321
|
-
}
|
322
|
-
)
|
323
|
-
)
|
324
|
-
|
325
|
-
allow(Alephant::Storage).to receive(:new) { s3_double_with_etag }
|
326
|
-
end
|
327
|
-
|
328
|
-
context "when requesting an unmodified response with GET" do
|
329
|
-
let(:path) { "/components/batch?batch_id=baz&components[ni_council_results_table][component]=ni_council_results_table&components[ni_council_results_table][options][foo]=bar&components[ni_council_results_table_no_options][component]=ni_council_results_table" }
|
330
|
-
let(:content_type) { "application/json" }
|
331
|
-
let(:etag) { '"34774567db979628363e6e865127623f"' }
|
332
|
-
|
333
|
-
before do
|
334
|
-
get(
|
335
|
-
path,
|
336
|
-
{},
|
337
|
-
{
|
338
|
-
"CONTENT_TYPE" => content_type,
|
339
|
-
"HTTP_IF_NONE_MATCH" => etag
|
340
|
-
}
|
341
|
-
)
|
342
|
-
end
|
343
|
-
|
344
|
-
specify { expect(last_response.status).to eql not_modified_status_code }
|
345
|
-
specify { expect(last_response.body).to eq "" }
|
346
|
-
|
347
|
-
describe "response should have headers" do
|
348
|
-
it "should not have a Content-Type header" do
|
349
|
-
expect(last_response.headers).to_not include("Content-Type")
|
350
|
-
end
|
351
|
-
|
352
|
-
it "should have ETag cache header" do
|
353
|
-
expect(last_response.headers["ETag"]).to eq(etag)
|
354
|
-
end
|
355
|
-
|
356
|
-
it "should have most recent Last-Modified header" do
|
357
|
-
expect(last_response.headers["Last-Modified"]).to eq("Mon, 11 Apr 2016 10:39:57 GMT")
|
358
|
-
end
|
359
|
-
|
360
|
-
it "shoud not have no cache headers" do
|
361
|
-
expect(last_response.headers).to_not include("Cache-Control")
|
362
|
-
expect(last_response.headers).to_not include("Pragma")
|
363
|
-
expect(last_response.headers).to_not include("Expires")
|
364
|
-
end
|
365
|
-
end
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
200
|
describe "S3 headers" do
|
370
201
|
let(:content) do
|
371
202
|
AWS::Core::Data.new(
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alephant-broker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- BBC News
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-04-
|
11
|
+
date: 2016-04-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -322,6 +322,7 @@ files:
|
|
322
322
|
- spec/fixtures/json/batch.json
|
323
323
|
- spec/fixtures/json/batch_compiled.json
|
324
324
|
- spec/http_spec.rb
|
325
|
+
- spec/integration/not_modified_response_spec.rb
|
325
326
|
- spec/integration/rack_spec.rb
|
326
327
|
- spec/integration/spec_helper.rb
|
327
328
|
- spec/spec_helper.rb
|
@@ -355,6 +356,7 @@ test_files:
|
|
355
356
|
- spec/fixtures/json/batch.json
|
356
357
|
- spec/fixtures/json/batch_compiled.json
|
357
358
|
- spec/http_spec.rb
|
359
|
+
- spec/integration/not_modified_response_spec.rb
|
358
360
|
- spec/integration/rack_spec.rb
|
359
361
|
- spec/integration/spec_helper.rb
|
360
362
|
- spec/spec_helper.rb
|