Skip to content

Commit 0ba2294

Browse files
committed
test: add spec to compare servlet env vs default env parsing of some edge cases
1 parent 84f2d49 commit 0ba2294

1 file changed

Lines changed: 90 additions & 0 deletions

File tree

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#--
2+
# This source code is available under the MIT license.
3+
# See the file LICENSE.txt for details.
4+
#++
5+
6+
require File.expand_path('../../spec_helper', File.dirname(__FILE__))
7+
8+
require 'rack'
9+
require 'rack/handler/servlet'
10+
11+
# Differential coverage: the "pure" ServletEnv maps servlet-parsed parameters
12+
# (HttpServletRequest#getParameterMap) into the Rack env, whereas DefaultEnv
13+
# lets Rack itself parse the QUERY_STRING. ServletEnv's whole reason to exist
14+
# is to produce the *same* params Rack would - so here we drive the same query
15+
# string through both and assert Rack::Request#GET comes out identical.
16+
#
17+
# The decoded values handed to addParameter are written as literals (simulating
18+
# what a servlet container decodes getParameterMap to), NOT computed via Rack's
19+
# unescaper - otherwise the escaping comparison would be circular.
20+
describe 'Rack::Handler::Servlet ServletEnv vs DefaultEnv (parsing parity)' do
21+
22+
before do
23+
@servlet_context = mock_servlet_context
24+
end
25+
26+
# Build Rack::Request#GET from a query string using the given env class.
27+
# +params+ are the (already url-decoded) name/value pairs the servlet
28+
# container would expose via getParameterMap; DefaultEnv ignores them.
29+
def get_params(env_class, query_string, params = [])
30+
request = org.springframework.mock.web.MockHttpServletRequest.new(@servlet_context)
31+
request.setMethod('GET')
32+
request.setRequestURI('/path')
33+
request.setQueryString(query_string)
34+
params.each { |name, value| request.addParameter(name, value) }
35+
response = org.springframework.mock.web.MockHttpServletResponse.new
36+
servlet_env = org.jruby.rack.servlet.ServletRackEnvironment.new(request, response, @rack_context)
37+
Rack::Request.new(env_class.create(servlet_env)).GET
38+
end
39+
40+
DefaultEnv = Rack::Handler::Servlet::DefaultEnv
41+
ServletEnv = Rack::Handler::Servlet::ServletEnv
42+
43+
# [ description, query_string, decoded getParameterMap pairs ]
44+
PARITY_CASES = [
45+
[ 'plain params', 'a=1&b=2', [ %w(a 1), %w(b 2) ] ],
46+
[ 'space via +', 'a=x+y', [ [ 'a', 'x y' ] ] ],
47+
[ 'space via %20', 'a=x%20y', [ [ 'a', 'x y' ] ] ],
48+
[ 'encoded & = #', 'a=%26%3D%23', [ [ 'a', '&=#' ] ] ],
49+
[ 'encoded = in value', 'a=b%3Dc', [ [ 'a', 'b=c' ] ] ],
50+
[ 'utf-8 name and value', 'caf%C3%A9=%C3%BC', [ [ "café", "ü" ] ] ],
51+
[ 'repeated flat key', 'a=1&a=2', [ %w(a 1), %w(a 2) ] ],
52+
[ 'array [] key', 'a%5B%5D=1&a%5B%5D=2', [ [ 'a[]', '1' ], [ 'a[]', '2' ] ] ],
53+
[ 'hash [k] key', 'a%5Bb%5D=1', [ [ 'a[b]', '1' ] ] ],
54+
[ 'empty value', 'a=', [ [ 'a', '' ] ] ],
55+
]
56+
57+
PARITY_CASES.each do |desc, query_string, params|
58+
it "matches DefaultEnv (real Rack) for #{desc}" do
59+
default_get = get_params(DefaultEnv, query_string)
60+
servlet_get = get_params(ServletEnv, query_string, params)
61+
expect(servlet_get).to eq(default_get)
62+
end
63+
end
64+
65+
# Known divergences of the hand-rolled ServletEnv#store_parameter from Rack's
66+
# own nested-query parsing. Documented here so the gap is explicit (and any
67+
# future change to store_parameter can turn these into parity cases).
68+
describe 'known divergences from Rack' do
69+
70+
it 'hash-in-array (a[][b]) is not parsed the same as Rack' do
71+
query_string = 'a%5B%5D%5Bb%5D=1'
72+
default_get = get_params(DefaultEnv, query_string) # Rack: {"a"=>[{"b"=>"1"}]}
73+
servlet_get = get_params(ServletEnv, query_string, [ [ 'a[][b]', '1' ] ])
74+
75+
expect(default_get).to eq({ 'a' => [ { 'b' => '1' } ] })
76+
expect(servlet_get).to_not eq(default_get) # documents the current gap
77+
end
78+
79+
it 'deep nesting (a[b][c]) is only nested one level by store_parameter' do
80+
query_string = 'a%5Bb%5D%5Bc%5D=x'
81+
default_get = get_params(DefaultEnv, query_string) # Rack: {"a"=>{"b"=>{"c"=>"x"}}}
82+
servlet_get = get_params(ServletEnv, query_string, [ [ 'a[b][c]', 'x' ] ])
83+
84+
expect(default_get).to eq({ 'a' => { 'b' => { 'c' => 'x' } } })
85+
expect(servlet_get).to eq({ 'a' => { 'b][c' => 'x' } }) # current (broken) behavior
86+
end
87+
88+
end
89+
90+
end

0 commit comments

Comments
 (0)