rack-radar 0.0.1
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 +7 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +283 -0
- data/Rakefile +14 -0
- data/lib/rack-radar.rb +1 -0
- data/lib/rack/radar.rb +127 -0
- data/lib/rack/radar/cookies.rb +128 -0
- data/lib/rack/radar/session.rb +190 -0
- data/rack-radar.gemspec +21 -0
- data/test/app_test.rb +52 -0
- data/test/auth_test.rb +150 -0
- data/test/cookies_test.rb +280 -0
- data/test/follow_redirect_test.rb +44 -0
- data/test/headers_test.rb +82 -0
- data/test/map_test.rb +64 -0
- data/test/params_test.rb +45 -0
- data/test/request_methods_test.rb +52 -0
- data/test/session_test.rb +42 -0
- data/test/test_setup.rb +162 -0
- metadata +105 -0
@@ -0,0 +1,280 @@
|
|
1
|
+
require 'test_setup'
|
2
|
+
|
3
|
+
class CookiesTest
|
4
|
+
include Minispec
|
5
|
+
|
6
|
+
class App < Air
|
7
|
+
|
8
|
+
def index names
|
9
|
+
cookies.values_at(*names.split(',')).inspect
|
10
|
+
end
|
11
|
+
|
12
|
+
def post_index
|
13
|
+
post_params.each_pair do |n, v|
|
14
|
+
v.is_a?(Hash) &&
|
15
|
+
(expires = v['expires']) &&
|
16
|
+
(v['expires'] = ::Time.parse(expires))
|
17
|
+
response.set_cookie n, v
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def delete_index names
|
22
|
+
names.split(',').each do |n|
|
23
|
+
response.delete_cookie n, params[n] || {}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def post_deeper__path
|
28
|
+
post_params.each_pair { |n, v| response.set_cookie n, v }
|
29
|
+
end
|
30
|
+
|
31
|
+
def deeper name
|
32
|
+
cookies[name]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
helper :persisted? do
|
37
|
+
assert(cookies[@var]) == @val
|
38
|
+
end
|
39
|
+
|
40
|
+
helper :not_persisted do
|
41
|
+
is(cookies[@var]).nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
helper :disposed? do
|
45
|
+
assert(last_response.body) == @expected
|
46
|
+
end
|
47
|
+
|
48
|
+
helper :not_disposed do
|
49
|
+
assert(last_response.body) == @expected_nil
|
50
|
+
end
|
51
|
+
|
52
|
+
before_all { app(App) }
|
53
|
+
before { cookies.clear }
|
54
|
+
|
55
|
+
context 'set/get via HTTP' do
|
56
|
+
|
57
|
+
before do
|
58
|
+
@var, @val = 2.times.map { 5.times.map { (('a'..'z').to_a + ('A'..'Z').to_a + (1..50).to_a)[rand(100)] }.join }
|
59
|
+
@expected, @expected_nil = [@val].inspect, [nil].inspect
|
60
|
+
end
|
61
|
+
|
62
|
+
should 'persist/dispose cause by default cookie`s path is set to current URI path' do
|
63
|
+
post @var => @val
|
64
|
+
is.persisted?
|
65
|
+
|
66
|
+
get @var
|
67
|
+
is.disposed?
|
68
|
+
end
|
69
|
+
|
70
|
+
should 'persist/dispose cause given path matches default cookie`s path' do
|
71
|
+
post @var => {:value => @val, :path => '/'}
|
72
|
+
is.persisted?
|
73
|
+
|
74
|
+
get @var
|
75
|
+
is.disposed?
|
76
|
+
end
|
77
|
+
|
78
|
+
should 'NOT persist/dispose cause wrong path provided - /blah is not a prefix of /' do
|
79
|
+
post @var => {:value => @val, :path => '/blah'}
|
80
|
+
assure.not_persisted
|
81
|
+
|
82
|
+
get @var
|
83
|
+
assure.not_disposed
|
84
|
+
end
|
85
|
+
|
86
|
+
should 'persist/dispose cause /deeper is a prefix of /deeper/path' do
|
87
|
+
post '/deeper/path', @var => @val
|
88
|
+
is.persisted?
|
89
|
+
|
90
|
+
get '/deeper', @var
|
91
|
+
assert(last_response.body) == @val
|
92
|
+
end
|
93
|
+
|
94
|
+
should 'persist but not dispose cause path set automatically to /deeper, and it is not a prefix for /' do
|
95
|
+
post '/deeper/path', @var => @val
|
96
|
+
is.persisted?
|
97
|
+
|
98
|
+
get @var
|
99
|
+
assure.not_disposed
|
100
|
+
end
|
101
|
+
|
102
|
+
should 'persist and dispose for /deeper path' do
|
103
|
+
post '/deeper/path', @var => @val
|
104
|
+
is.persisted?
|
105
|
+
|
106
|
+
get '/deeper', @var
|
107
|
+
assert(last_response.body) == @val
|
108
|
+
end
|
109
|
+
|
110
|
+
should 'persist/dispose cause path set to /' do
|
111
|
+
post '/deeper/path', @var => {:value => @val, :path => '/'}
|
112
|
+
is.persisted?
|
113
|
+
|
114
|
+
get @var
|
115
|
+
is.disposed?
|
116
|
+
end
|
117
|
+
|
118
|
+
should 'NOT dispose - expires is in past' do
|
119
|
+
post @var => {:value => @val, :expires => ::Rack::Utils.rfc2822(::Time.now.gmtime - 1)}
|
120
|
+
is.persisted?
|
121
|
+
|
122
|
+
get @var
|
123
|
+
assure.not_disposed
|
124
|
+
end
|
125
|
+
|
126
|
+
should 'NOT persist/dispose cause inner domain given' do
|
127
|
+
post @var => {:value => @val, :domain => 'x.org'}
|
128
|
+
assure.not_persisted
|
129
|
+
|
130
|
+
get @var
|
131
|
+
assure.not_disposed
|
132
|
+
end
|
133
|
+
|
134
|
+
should 'persist/dispose when domains matches' do
|
135
|
+
header['HTTP_HOST'] = 'some.tld'
|
136
|
+
post 'http://' + header['HTTP_HOST'], @var => @val
|
137
|
+
get 'http://' + header['HTTP_HOST'], @var
|
138
|
+
is.disposed?
|
139
|
+
headers.delete 'HTTP_HOST'
|
140
|
+
end
|
141
|
+
|
142
|
+
should 'prefer more specific cookies' do
|
143
|
+
domain = 'http://foo.bar'
|
144
|
+
subdomain = 'http://a.foo.bar'
|
145
|
+
|
146
|
+
header['HTTP_HOST'] = 'a.foo.bar'
|
147
|
+
post subdomain, 'var' => subdomain
|
148
|
+
|
149
|
+
header['HTTP_HOST'] = 'foo.bar'
|
150
|
+
post domain, 'var' => domain
|
151
|
+
|
152
|
+
get domain, 'var'
|
153
|
+
is(last_response.body) == [domain].inspect
|
154
|
+
|
155
|
+
header['HTTP_HOST'] = 'a.foo.bar'
|
156
|
+
get subdomain, 'var'
|
157
|
+
is(last_response.body) == [subdomain].inspect
|
158
|
+
headers.delete 'HTTP_HOST'
|
159
|
+
end
|
160
|
+
|
161
|
+
should 'NOT persist/dispose cause secure cookie are accessed via un-secure connection' do
|
162
|
+
post @var => {:value => @val, :secure => 'true'}
|
163
|
+
is(cookies[@var]).nil?
|
164
|
+
|
165
|
+
get @var
|
166
|
+
assure.not_disposed
|
167
|
+
end
|
168
|
+
|
169
|
+
should 'persist/dispose cause secure cookie are accessed via secure connection' do
|
170
|
+
s_post @var => {:value => @val, :secure => 'true'}
|
171
|
+
is(cookies[@var]) == @val
|
172
|
+
|
173
|
+
s_get @var
|
174
|
+
expect(last_response.body) == @expected
|
175
|
+
end
|
176
|
+
|
177
|
+
should 'delete a cookie' do
|
178
|
+
|
179
|
+
post @var => @val
|
180
|
+
get @var
|
181
|
+
is.persisted?
|
182
|
+
|
183
|
+
delete @var
|
184
|
+
get @var
|
185
|
+
assure.not_disposed
|
186
|
+
end
|
187
|
+
|
188
|
+
should 'set/get multiple cookies at once' do
|
189
|
+
vars, vals = 2.times.map { 5.times.map { 5.times.map { ('a'..'z').to_a[rand(26)] }.join } }
|
190
|
+
params = Hash[vars.zip vals]
|
191
|
+
post params
|
192
|
+
get vars.join(',')
|
193
|
+
assert(last_response.body) == params.values_at(*vars).inspect
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'jars are domain specific' do
|
198
|
+
|
199
|
+
should 'use separate jar for each domain' do
|
200
|
+
|
201
|
+
header['HTTP_HOST'] = 'foo.bar'
|
202
|
+
domain = 'http://' + header['HTTP_HOST']
|
203
|
+
post domain, 'var' => domain
|
204
|
+
get domain, 'var'
|
205
|
+
is(last_response.body) == [domain].inspect
|
206
|
+
headers.delete 'HTTP_HOST'
|
207
|
+
|
208
|
+
header['HTTP_HOST'] = 'bar.foo'
|
209
|
+
domain = 'http://' + header['HTTP_HOST']
|
210
|
+
post domain, 'var' => domain
|
211
|
+
get domain, 'var'
|
212
|
+
is(last_response.body) == [domain].inspect
|
213
|
+
headers.delete 'HTTP_HOST'
|
214
|
+
|
215
|
+
header['HTTP_HOST'] = 'foo.bar'
|
216
|
+
domain = 'http://' + header['HTTP_HOST']
|
217
|
+
get domain, 'var'
|
218
|
+
is(last_response.body) == [domain].inspect
|
219
|
+
headers.delete 'HTTP_HOST'
|
220
|
+
end
|
221
|
+
|
222
|
+
should 'use same jar for a domain and its subdomains' do
|
223
|
+
%w[a b c a.b a.b.c].each do |subdomain|
|
224
|
+
header['HTTP_HOST'] = '%s.foo.bar' % subdomain
|
225
|
+
domain = 'http://' + header['HTTP_HOST']
|
226
|
+
get domain, 'var'
|
227
|
+
is(last_response.body) == ['http://foo.bar'].inspect
|
228
|
+
headers.delete 'HTTP_HOST'
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
should 'domains names are case insensitive' do
|
233
|
+
%w[BAR.foo bar.FOO BAR.FOO].each do |host|
|
234
|
+
header['HTTP_HOST'] = host
|
235
|
+
domain = 'http://' + header['HTTP_HOST']
|
236
|
+
get domain, 'var'
|
237
|
+
is(last_response.body) == ['http://bar.foo'].inspect
|
238
|
+
headers.delete 'HTTP_HOST'
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
243
|
+
|
244
|
+
context 'set/get directly' do
|
245
|
+
|
246
|
+
before_all { app(App) }
|
247
|
+
|
248
|
+
should 'set/get cookies properly' do
|
249
|
+
n, size = 10, cookies.size
|
250
|
+
n.times do
|
251
|
+
var, val = 2.times.map { 5.times.map { ('a'..'z').to_a[rand(26)] }.join }
|
252
|
+
cookies[var] = val
|
253
|
+
expect(cookies[var]) == val
|
254
|
+
end
|
255
|
+
expect(cookies.size) == size + n
|
256
|
+
end
|
257
|
+
|
258
|
+
should 'delete a cookie' do
|
259
|
+
|
260
|
+
cookies['foo'] = 'bar'
|
261
|
+
expect(cookies['foo']) == 'bar'
|
262
|
+
|
263
|
+
cookies.delete 'foo'
|
264
|
+
is(cookies['foo']).nil?
|
265
|
+
end
|
266
|
+
|
267
|
+
should 'clear cookies' do
|
268
|
+
cookies.clear
|
269
|
+
expect(cookies.size) == 0
|
270
|
+
end
|
271
|
+
|
272
|
+
should 'escape values' do
|
273
|
+
cookies['var'] = 'foo;bar'
|
274
|
+
expect(cookies['var']) == 'foo;bar'
|
275
|
+
|
276
|
+
cookies['var'] = ';bar;foo;'
|
277
|
+
expect(cookies['var']) == ';bar;foo;'
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'test_setup'
|
2
|
+
|
3
|
+
class FollowRedirectTest
|
4
|
+
include Minispec
|
5
|
+
|
6
|
+
class App < Air
|
7
|
+
|
8
|
+
def index
|
9
|
+
redirect :destination
|
10
|
+
end
|
11
|
+
|
12
|
+
def destination
|
13
|
+
__method__
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
before_all { app(App) }
|
18
|
+
|
19
|
+
should 'follow redirect' do
|
20
|
+
|
21
|
+
get
|
22
|
+
is(last_response.status) == 302
|
23
|
+
|
24
|
+
follow_redirect!
|
25
|
+
is(last_response.status) == 200
|
26
|
+
is(last_response.body) == 'destination'
|
27
|
+
end
|
28
|
+
|
29
|
+
should 'keep scheme' do
|
30
|
+
s_get
|
31
|
+
is(last_response.status) == 302
|
32
|
+
|
33
|
+
follow_redirect!
|
34
|
+
is(last_request.env['HTTPS']) == 'on'
|
35
|
+
is(last_response.status) == 200
|
36
|
+
is(last_response.body) == 'destination'
|
37
|
+
end
|
38
|
+
|
39
|
+
should 'raise an error if last response is not an redirect' do
|
40
|
+
get :destination
|
41
|
+
does { follow_redirect! }.raise_error?
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'test_setup'
|
2
|
+
|
3
|
+
class HeadersTest
|
4
|
+
include Minispec
|
5
|
+
|
6
|
+
class App < Air
|
7
|
+
|
8
|
+
def index headers
|
9
|
+
env.values_at(*headers.split(',')).inspect
|
10
|
+
end
|
11
|
+
|
12
|
+
def post_index
|
13
|
+
content_type
|
14
|
+
end
|
15
|
+
|
16
|
+
def dry
|
17
|
+
end
|
18
|
+
|
19
|
+
def post_dry
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
before_all { app(App) }
|
24
|
+
|
25
|
+
testing 'User-Agent' do
|
26
|
+
|
27
|
+
ua = 'Chrome'
|
28
|
+
header['User-Agent'] = ua
|
29
|
+
get 'HTTP_USER_AGENT'
|
30
|
+
is(last_response.body) == [ua].inspect
|
31
|
+
|
32
|
+
ua = 'Safari'
|
33
|
+
header['User-Agent'] = ua
|
34
|
+
get 'HTTP_USER_AGENT'
|
35
|
+
is(last_response.body) == [ua].inspect
|
36
|
+
end
|
37
|
+
|
38
|
+
testing 'Content-Type' do
|
39
|
+
ct = 'text/plain'
|
40
|
+
header['Content-Type'] = ct
|
41
|
+
get 'CONTENT_TYPE'
|
42
|
+
is(last_response.body) == [ct].inspect
|
43
|
+
end
|
44
|
+
|
45
|
+
should 'not override explicit CONTENT_TYPE on POST requests' do
|
46
|
+
ct = 'text/plain'
|
47
|
+
header['Content-Type'] = ct
|
48
|
+
post
|
49
|
+
is(last_response.body) == ct
|
50
|
+
end
|
51
|
+
|
52
|
+
should 'have 127.0.0.1 as default REMOTE_ADDR' do
|
53
|
+
get
|
54
|
+
expect(last_request.env['REMOTE_ADDR']) == '127.0.0.1'
|
55
|
+
end
|
56
|
+
|
57
|
+
should 'use default SERVER_NAME' do
|
58
|
+
get
|
59
|
+
expect(last_request.env['SERVER_NAME']) == Rack::Radar::RACK_RADAR__DEFAULT_HOST
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'Rack-related headers' do
|
63
|
+
|
64
|
+
it 'should override default rack.input' do
|
65
|
+
header['rack.input'] = StringIO.new('someString')
|
66
|
+
get :dry
|
67
|
+
is(last_request.env['rack.input']) == header['rack.input']
|
68
|
+
end
|
69
|
+
|
70
|
+
should 'not add multipart content type on post requests' do
|
71
|
+
post :dry
|
72
|
+
refute(last_request.env['CONTENT_TYPE']) =~ /x\-www\-form\-urlencoded/
|
73
|
+
end
|
74
|
+
|
75
|
+
should 'keep explicitly set rack.errors' do
|
76
|
+
errors = StringIO.new
|
77
|
+
header['rack.errors'] = errors
|
78
|
+
get
|
79
|
+
expect { last_request.env['rack.errors'].__id__ } == errors.__id__
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/test/map_test.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'test_setup'
|
2
|
+
|
3
|
+
class MapTest
|
4
|
+
include Minispec
|
5
|
+
|
6
|
+
class App < Air
|
7
|
+
|
8
|
+
def news a1
|
9
|
+
a1
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
before_all { app(App) }
|
14
|
+
|
15
|
+
testing 'without map' do
|
16
|
+
get '/news/1'
|
17
|
+
is(last_response).ok?
|
18
|
+
is(last_response.body) == '1'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should work only with arguments' do
|
22
|
+
map '/news'
|
23
|
+
get 1
|
24
|
+
is(last_response).ok?
|
25
|
+
is(last_response.body) == '1'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should work without arguments' do
|
29
|
+
map '/news/foo'
|
30
|
+
get
|
31
|
+
expect(last_response.status) == 200
|
32
|
+
is(last_response.body) == 'foo'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should require full url when map set to nil' do
|
36
|
+
map nil
|
37
|
+
get
|
38
|
+
is(last_response).not_found?
|
39
|
+
|
40
|
+
get 'foo'
|
41
|
+
is(last_response).not_found?
|
42
|
+
|
43
|
+
get '/news/foo'
|
44
|
+
is(last_response).ok?
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should ignore base URL set by map' do
|
48
|
+
map '/blah'
|
49
|
+
|
50
|
+
get 'news/foo'
|
51
|
+
is(last_response).not_found?
|
52
|
+
|
53
|
+
get '/news/foo'
|
54
|
+
is(last_response).ok?
|
55
|
+
|
56
|
+
get 'http://blah.org/news/foo'
|
57
|
+
is(last_response).not_found?
|
58
|
+
|
59
|
+
header['HTTP_HOST'] = 'blah.org'
|
60
|
+
get 'http://blah.org/news/foo'
|
61
|
+
is(last_response).ok?
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|