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