hurley 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.
@@ -0,0 +1,108 @@
1
+ require File.expand_path("../helper", __FILE__)
2
+
3
+ module Hurley
4
+ class HeaderTest < TestCase
5
+ def test_integration
6
+ client = Client.new "https://example.com"
7
+ client.header["Client"] = "1"
8
+ client.header["Override"] = "1"
9
+ client.connection = Test.new do |t|
10
+ t.get("/a") do |req|
11
+ output = []
12
+ req.header.each do |key, value|
13
+ output << "#{key}:#{value}"
14
+ end
15
+ [200, {}, output.join("\n")]
16
+ end
17
+ end
18
+
19
+ res = client.get("/a") do |req|
20
+ req.header["Request"] = "2"
21
+ req.header["Override"] = "2"
22
+ end
23
+
24
+ assert_equal 200, res.status_code
25
+ assert_equal "User-Agent:Hurley v0.1\nClient:1\nOverride:2\nRequest:2", res.body
26
+ end
27
+
28
+ def test_empty_initial
29
+ h = Header.new
30
+ assert !h.key?(:content_type)
31
+ assert_equal 0, h.size
32
+ assert_equal [], h.keys
33
+ end
34
+
35
+ def test_initial
36
+ h = Header.new :content_type => "text/plain"
37
+ assert_equal 1, h.size
38
+ assert_equal %w(Content-Type), h.keys
39
+ [
40
+ "Content-Type",
41
+ "content-type",
42
+ :content_type,
43
+ ].each do |key|
44
+ assert h.key?(key)
45
+ assert_equal "text/plain", h[key]
46
+ end
47
+ end
48
+
49
+ def test_set_and_get_key_with_shortcuts
50
+ ["Content-Type", "content-type", :content_type, "CONTENT-TYPE"].each do |key|
51
+ h = Header.new
52
+ assert_nil h[key]
53
+
54
+ h[key] = "text/plain"
55
+ assert_equal %w(Content-Type), h.keys
56
+ assert_equal "text/plain", h[key]
57
+ end
58
+ end
59
+
60
+ def test_set_and_get_custom_key
61
+ ["X-Hurley", "x-hurley", :x_hurley, "X-HURLEY"].each do |key|
62
+ h = Header.new
63
+ assert_nil h[key]
64
+
65
+ h[key] = "text/plain"
66
+ assert_equal %w(X-Hurley), h.keys
67
+ assert_equal "text/plain", h[key]
68
+ end
69
+ end
70
+
71
+ def test_delete_key
72
+ h = Header.new :content_type => "text/plain"
73
+ assert_equal %w(Content-Type), h.keys
74
+ assert_equal "text/plain", h[:content_type]
75
+
76
+ h.delete :content_type
77
+ assert_equal [], h.keys
78
+ end
79
+
80
+ def test_iterate_keys
81
+ h = Header.new :content_type => "text/plain", :content_length => 2
82
+ assert_equal %w(Content-Type Content-Length), h.keys
83
+ assert_equal "text/plain", h[:content_type]
84
+ assert_equal "2", h[:content_length]
85
+
86
+ pieces = []
87
+ h.each do |key, value|
88
+ pieces << "#{key}=#{value}"
89
+ end
90
+
91
+ assert_equal "Content-Type=text/plain\nContent-Length=2",
92
+ pieces.join("\n")
93
+ end
94
+
95
+ def test_dup
96
+ h1 = Header.new :content_type => "text/plain"
97
+ h2 = h1
98
+ h3 = h1.dup
99
+
100
+ h2[:content_type] = "text/plain; charset=utf-8"
101
+ h3[:content_type] = "image/gif"
102
+
103
+ assert_equal "text/plain; charset=utf-8", h1[:content_type]
104
+ assert_equal "text/plain; charset=utf-8", h2[:content_type]
105
+ assert_equal "image/gif", h3[:content_type]
106
+ end
107
+ end
108
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,14 @@
1
+ require "minitest/autorun"
2
+ require "rack"
3
+
4
+ if ENV["HURLEY_ADDRESSABLE"]
5
+ require "addressable/uri"
6
+ end
7
+
8
+ require File.expand_path("../../lib/hurley", __FILE__)
9
+ Hurley.require_lib "test", "test/integration"
10
+
11
+ module Hurley
12
+ class TestCase < MiniTest::Test
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ require File.expand_path("../../helper", __FILE__)
2
+
3
+ module Hurley
4
+ module Live
5
+ class NetHttpTest < TestCase
6
+ features = []
7
+ features << :Compression if RUBY_VERSION >= "1.9"
8
+
9
+ Hurley::Test::Integration.apply(self, *features)
10
+
11
+ def connection
12
+ Hurley.default_connection
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,306 @@
1
+ require File.expand_path("../helper", __FILE__)
2
+
3
+ module Hurley
4
+ class MultipartTest < TestCase
5
+ def test_build_form_with_flat_query
6
+ ctype, body = Query::Flat.new(:a => [1,2,3]).to_form
7
+ assert_equal "application/x-www-form-urlencoded", ctype
8
+ assert_equal "a=1&a=2&a=3", body.read
9
+ end
10
+
11
+ def test_build_form_with_nested_query
12
+ ctype, body = Query::Nested.new(:a => {:b => 2}).to_form
13
+ assert_equal "application/x-www-form-urlencoded", ctype
14
+ assert_equal "a%5Bb%5D=2", body.read
15
+ end
16
+
17
+ def test_build_multipart_with_set_boundary
18
+ options = RequestOptions.new
19
+ options.boundary = :wat
20
+
21
+ ctype, _ = Query::Nested.new(:file => UploadIO.new(__FILE__, "text/plain")).to_form(options)
22
+ assert_equal "multipart/form-data; boundary=wat", ctype
23
+ end
24
+
25
+ def test_build_multipart_with_flat_query
26
+ ctype, body = Query::Flat.new(
27
+ :a => 1,
28
+ :array => [1, 2],
29
+ :file => UploadIO.new(__FILE__, "text/plain"),
30
+ ).to_form
31
+
32
+ src = IO.read(__FILE__)
33
+
34
+ boundary = nil
35
+ if ctype =~ %r{\Amultipart/form-data; boundary=(Hurley\-.*)}
36
+ boundary = $1
37
+ else
38
+ fail "bad ctype: #{ctype.inspect}"
39
+ end
40
+
41
+ assert_match %r{\Amultipart/form-data; boundary=Hurley\-}, ctype
42
+ expected = %(--#{boundary}\r\n) +
43
+ %(Content-Disposition: form-data; name="a"\r\n) +
44
+ %(\r\n1\r\n) +
45
+
46
+ %(--#{boundary}\r\n) +
47
+ %(Content-Disposition: form-data; name="array"\r\n) +
48
+ %(\r\n1\r\n) +
49
+
50
+ %(--#{boundary}\r\n) +
51
+ %(Content-Disposition: form-data; name="array"\r\n) +
52
+ %(\r\n2\r\n) +
53
+
54
+ %(--#{boundary}\r\n) +
55
+ %(Content-Disposition: form-data; name="file"; filename="multipart_test.rb"\r\n) +
56
+ %(Content-Length: #{src.size}\r\n) +
57
+ %(Content-Type: text/plain\r\n) +
58
+ %(Content-Transfer-Encoding: binary\r\n) +
59
+ %(\r\n#{src}\r\n) +
60
+
61
+ %(--#{boundary}--\r\n\r\n)
62
+
63
+ assert_equal expected, body.read.to_s
64
+ end
65
+
66
+ def test_build_multipart_with_nested_query
67
+ ctype, body = Query::Nested.new(:a => {
68
+ :num => 1,
69
+ :arr => [1, 2],
70
+ :file => UploadIO.new(__FILE__, "text/plain"),
71
+ }).to_form
72
+
73
+ src = IO.read(__FILE__)
74
+
75
+ boundary = nil
76
+ if ctype =~ %r{\Amultipart/form-data; boundary=(Hurley\-.*)}
77
+ boundary = $1
78
+ else
79
+ fail "bad ctype: #{ctype.inspect}"
80
+ end
81
+
82
+ expected = %(--#{boundary}\r\n) +
83
+ %(Content-Disposition: form-data; name="a[num]"\r\n) +
84
+ %(\r\n1\r\n) +
85
+
86
+ %(--#{boundary}\r\n) +
87
+ %(Content-Disposition: form-data; name="a[arr][]"\r\n) +
88
+ %(\r\n1\r\n) +
89
+
90
+ %(--#{boundary}\r\n) +
91
+ %(Content-Disposition: form-data; name="a[arr][]"\r\n) +
92
+ %(\r\n2\r\n) +
93
+
94
+ %(--#{boundary}\r\n) +
95
+ %(Content-Disposition: form-data; name="a[file]"; filename="multipart_test.rb"\r\n) +
96
+ %(Content-Length: #{src.size}\r\n) +
97
+ %(Content-Type: text/plain\r\n) +
98
+ %(Content-Transfer-Encoding: binary\r\n) +
99
+ %(\r\n#{src}\r\n) +
100
+
101
+ %(--#{boundary}--\r\n\r\n)
102
+
103
+ assert_equal expected, body.read.to_s
104
+ end
105
+ end
106
+
107
+ class ParamPartTest < TestCase
108
+ def test_build_without_content_type
109
+ part = Multipart::Part.new("boundary", "foo", "bar", {})
110
+ expected = %(--boundary\r\n) +
111
+ %(Content-Disposition: form-data; name="foo"\r\n) +
112
+ %(\r\nbar\r\n)
113
+ assert_equal expected.size, part.length
114
+ assert_equal expected, part.to_io.read
115
+ end
116
+
117
+ def test_build_with_content_type
118
+ part = Multipart::Part.new("boundary", "foo", "bar",
119
+ :content_type => "text/plain")
120
+ expected = %(--boundary\r\n) +
121
+ %(Content-Disposition: form-data; name="foo"\r\n) +
122
+ %(Content-Type: text/plain\r\n) +
123
+ %(\r\nbar\r\n)
124
+ assert_equal expected.size, part.length
125
+ assert_equal expected, part.to_io.read
126
+ end
127
+ end
128
+
129
+ class FilePartTest < TestCase
130
+ def test_build_without_options
131
+ src = IO.read(__FILE__)
132
+ io = UploadIO.new(__FILE__, "text/plain")
133
+
134
+ part = Multipart::Part.new("boundary", "foo", io, {})
135
+
136
+ expected = %(--boundary\r\n) +
137
+ %(Content-Disposition: form-data; name="foo"; filename="multipart_test.rb"\r\n) +
138
+ %(Content-Length: #{src.size}\r\n) +
139
+ %(Content-Type: text/plain\r\n) +
140
+ %(Content-Transfer-Encoding: binary\r\n) +
141
+ %(\r\n#{src}\r\n)
142
+
143
+ assert_equal expected.size, part.length
144
+ assert_equal expected, part.to_io.read
145
+ end
146
+
147
+ def test_build_with_content_type
148
+ src = IO.read(__FILE__)
149
+ io = UploadIO.new(__FILE__, "text/plain")
150
+
151
+ part = Multipart::Part.new("boundary", "foo", io,
152
+ :content_type => "text/ruby")
153
+
154
+ expected = %(--boundary\r\n) +
155
+ %(Content-Disposition: form-data; name="foo"; filename="multipart_test.rb"\r\n) +
156
+ %(Content-Length: #{src.size}\r\n) +
157
+ %(Content-Type: text/ruby\r\n) +
158
+ %(Content-Transfer-Encoding: binary\r\n) +
159
+ %(\r\n#{src}\r\n)
160
+
161
+ assert_equal expected.size, part.length
162
+ assert_equal expected, part.to_io.read
163
+ end
164
+
165
+ def test_build_with_content_disposition
166
+ src = IO.read(__FILE__)
167
+ io = UploadIO.new(__FILE__, "text/plain")
168
+
169
+ part = Multipart::Part.new("boundary", "foo", io,
170
+ :content_disposition => "attachment")
171
+
172
+ expected = %(--boundary\r\n) +
173
+ %(Content-Disposition: attachment; name="foo"; filename="multipart_test.rb"\r\n) +
174
+ %(Content-Length: #{src.size}\r\n) +
175
+ %(Content-Type: text/plain\r\n) +
176
+ %(Content-Transfer-Encoding: binary\r\n) +
177
+ %(\r\n#{src}\r\n)
178
+
179
+ assert_equal expected.size, part.length
180
+ assert_equal expected, part.to_io.read
181
+ end
182
+
183
+ def test_build_with_content_transfer_encoding
184
+ src = IO.read(__FILE__)
185
+ io = UploadIO.new(__FILE__, "text/plain")
186
+
187
+ part = Multipart::Part.new("boundary", "foo", io,
188
+ :content_transfer_encoding => "rofl")
189
+
190
+ expected = %(--boundary\r\n) +
191
+ %(Content-Disposition: form-data; name="foo"; filename="multipart_test.rb"\r\n) +
192
+ %(Content-Length: #{src.size}\r\n) +
193
+ %(Content-Type: text/plain\r\n) +
194
+ %(Content-Transfer-Encoding: rofl\r\n) +
195
+ %(\r\n#{src}\r\n)
196
+
197
+ assert_equal expected.size, part.length
198
+ assert_equal expected, part.to_io.read
199
+ end
200
+
201
+ def test_build_with_content_id
202
+ src = IO.read(__FILE__)
203
+ io = UploadIO.new(__FILE__, "text/plain")
204
+
205
+ part = Multipart::Part.new("boundary", "foo", io,
206
+ :content_id => "abc123")
207
+
208
+ expected = %(--boundary\r\n) +
209
+ %(Content-Disposition: form-data; name="foo"; filename="multipart_test.rb"\r\n) +
210
+ %(Content-Length: #{src.size}\r\n) +
211
+ %(Content-ID: abc123\r\n) +
212
+ %(Content-Type: text/plain\r\n) +
213
+ %(Content-Transfer-Encoding: binary\r\n) +
214
+ %(\r\n#{src}\r\n)
215
+
216
+ assert_equal expected.size, part.length
217
+ assert_equal expected, part.to_io.read
218
+ end
219
+ end
220
+
221
+ class CompositeReadIOTest < TestCase
222
+ def test_read_all
223
+ io = CompositeReadIO.new StringIO.new("one"), StringIO.new("two")
224
+ assert_equal "onetwo", io.read
225
+ end
226
+
227
+ def test_read_chunks
228
+ io = CompositeReadIO.new StringIO.new("one"), StringIO.new("two")
229
+ assert_equal "on", io.read(2)
230
+ assert_equal "et", io.read(2)
231
+ assert_equal "wo", io.read(2)
232
+ assert_nil io.read(2)
233
+
234
+ io.rewind
235
+
236
+ assert_equal "on", io.read(2)
237
+ assert_equal "et", io.read(2)
238
+ assert_equal "wo", io.read(2)
239
+ assert_nil io.read(2)
240
+
241
+ io.rewind
242
+
243
+ assert_equal "one", io.read(3)
244
+ assert_equal "two", io.read(3)
245
+ assert_nil io.read(3)
246
+ end
247
+ end
248
+
249
+ class UploadIOTest < TestCase
250
+ def test_with_filename_and_no_custom_filename
251
+ io = UploadIO.new(__FILE__, "text/plain")
252
+ assert_equal "text/plain", io.content_type
253
+ assert_equal "multipart_test.rb", io.original_filename
254
+ assert_equal __FILE__, io.local_path
255
+ assert_empty io.opts
256
+ assert_equal IO.read(__FILE__), io.read
257
+ end
258
+
259
+ def test_with_filename_and_custom_filename
260
+ io = UploadIO.new(__FILE__, "text/plain", "wat.rb")
261
+ assert_equal "text/plain", io.content_type
262
+ assert_equal "wat.rb", io.original_filename
263
+ assert_equal __FILE__, io.local_path
264
+ assert_empty io.opts
265
+ assert_equal IO.read(__FILE__), io.read
266
+ end
267
+
268
+ def test_with_file_io_and_no_custom_filename
269
+ io = UploadIO.new(File.new(__FILE__), "text/plain")
270
+ assert_equal "text/plain", io.content_type
271
+ assert_equal "multipart_test.rb", io.original_filename
272
+ assert_equal __FILE__, io.local_path
273
+ assert_empty io.opts
274
+ assert_equal IO.read(__FILE__), io.read
275
+ end
276
+
277
+ def test_with_file_io_and_custom_filename
278
+ io = UploadIO.new(File.new(__FILE__), "text/plain", "wat.rb")
279
+ assert_equal "text/plain", io.content_type
280
+ assert_equal "wat.rb", io.original_filename
281
+ assert_equal __FILE__, io.local_path
282
+ assert_empty io.opts
283
+ assert_equal IO.read(__FILE__), io.read
284
+ end
285
+
286
+ def test_with_io_and_no_custom_filename
287
+ stringio = StringIO.new(IO.read(__FILE__))
288
+ io = UploadIO.new(stringio, "text/plain")
289
+ assert_equal "text/plain", io.content_type
290
+ assert_equal "local.path", io.original_filename
291
+ assert_equal "local.path", io.local_path
292
+ assert_empty io.opts
293
+ assert_equal IO.read(__FILE__), io.read
294
+ end
295
+
296
+ def test_with_io_and_custom_filename
297
+ stringio = StringIO.new(IO.read(__FILE__))
298
+ io = UploadIO.new(stringio, "text/plain", "wat.rb")
299
+ assert_equal "text/plain", io.content_type
300
+ assert_equal "wat.rb", io.original_filename
301
+ assert_equal "local.path", io.local_path
302
+ assert_empty io.opts
303
+ assert_equal IO.read(__FILE__), io.read
304
+ end
305
+ end
306
+ end
@@ -0,0 +1,189 @@
1
+ require File.expand_path("../helper", __FILE__)
2
+
3
+ module Hurley
4
+ class QueryTest < TestCase
5
+ def test_encode_and_parse_string_value
6
+ q = Query::Nested.new
7
+ q["a"] = 1
8
+
9
+ assert !q.key?("a+b")
10
+ assert_nil q["a+b"]
11
+ q["a+b"] = "1+2"
12
+ assert_equal "1+2", q["a+b"]
13
+ assert q.key?("a+b")
14
+
15
+ assert_equal "a=1&a%2Bb=1%2B2", q.to_query_string
16
+
17
+ q2 = Query.parse(q.to_query_string)
18
+ assert_equal %w(a a+b), q2.keys
19
+ assert_equal "1", q2["a"]
20
+ assert_equal "1+2", q2["a+b"]
21
+ end
22
+
23
+ def test_encode_and_parse_nil_value
24
+ q = Query::Nested.new
25
+ q["a"] = 1
26
+ assert !q.key?("a+b")
27
+ assert_nil q["a+b"]
28
+ q["a+b"] = nil
29
+ assert_nil q["a+b"]
30
+ assert q.key?("a+b")
31
+
32
+ assert_equal "a=1&a%2Bb", q.to_query_string
33
+
34
+ q2 = Query.parse(q.to_query_string)
35
+ assert_equal %w(a a+b), q2.keys
36
+ assert_equal "1", q2["a"]
37
+ assert_nil q2["a+b"]
38
+ end
39
+
40
+ def test_delete_value
41
+ q = Query::Nested.new
42
+ q["a+b"] = "1+2"
43
+ assert_equal "a%2Bb=1%2B2", q.to_query_string
44
+ q.delete "a+b"
45
+ assert_equal "", q.to_query_string
46
+ end
47
+
48
+ def test_encode_and_parse_array_value_in_nested_query
49
+ q = Query::Nested.new
50
+ q["a"] = 1
51
+ q["a+b"] = %w(1+1 2+2 3+3)
52
+ assert_query "a=1&a%2Bb[]=1%2B1&a%2Bb[]=2%2B2&a%2Bb[]=3%2B3", q.to_query_string
53
+
54
+ q2 = Query::Nested.parse(q.to_query_string)
55
+ assert_equal %w(a a+b), q2.keys
56
+ assert_equal "1", q2["a"]
57
+ assert_equal %w(1+1 2+2 3+3), q2["a+b"]
58
+ end
59
+
60
+ def test_encode_and_parse_array_value_in_flat_query
61
+ q = Query::Flat.new
62
+ q["a"] = 1
63
+ q["a+b"] = %w(1+1 2+2 3+3)
64
+ assert_equal "a=1&a%2Bb=1%2B1&a%2Bb=2%2B2&a%2Bb=3%2B3", q.to_query_string
65
+
66
+ q2 = Query::Flat.parse(q.to_query_string)
67
+ assert_equal %w(a a+b), q2.keys
68
+ assert_equal "1", q2["a"]
69
+ assert_equal %w(1+1 2+2 3+3), q2["a+b"]
70
+ end
71
+
72
+ def test_encode_and_parse_simple_hash_value_in_nested_query
73
+ q = Query::Nested.new
74
+ q["a"] = 1
75
+ q["a+b"] = {"1+1" => "a+a", "2+2" => "b+b"}
76
+ assert_query "a=1&a%2Bb[1%2B1]=a%2Ba&a%2Bb[2%2B2]=b%2Bb", q.to_query_string
77
+
78
+ q2 = Query::Nested.parse(q.to_query_string)
79
+ assert_equal %w(a a+b), q2.keys
80
+ assert_equal "1", q2["a"]
81
+ assert_equal %w(1+1 2+2), q2["a+b"].keys
82
+ assert_equal "a+a", q2["a+b"]["1+1"]
83
+ assert_equal "b+b", q2["a+b"]["2+2"]
84
+ end
85
+
86
+ def test_encode_and_parse_deeply_nested_hash
87
+ q = Query::Nested.new
88
+ q["a"] = 1
89
+ q["a+1"] = {
90
+ "b+2" => {
91
+ "c+3" => "d+4",
92
+ "c+wat" => "c",
93
+ },
94
+ "b+wat" => "b"
95
+ }
96
+
97
+ assert_query "a=1&a%2B1[b%2B2][c%2B3]=d%2B4&a%2B1[b%2B2][c%2Bwat]=c&a%2B1[b%2Bwat]=b", q.to_query_string
98
+ q2 = Query::Nested.parse(q.to_query_string)
99
+ assert_equal %w(a a+1), q2.keys
100
+ assert_equal "1", q2["a"]
101
+
102
+ assert_equal %w(b+2 b+wat), q2["a+1"].keys
103
+ assert_equal %w(c+3 c+wat), q2["a+1"]["b+2"].keys
104
+ assert_equal "b", q2["a+1"]["b+wat"]
105
+ assert_equal "c", q2["a+1"]["b+2"]["c+wat"]
106
+ assert_equal "d+4", q2["a+1"]["b+2"]["c+3"]
107
+ end
108
+
109
+ def test_encode_and_parse_deeply_nested_array
110
+ q = Query::Nested.new
111
+ q["a"] = 1
112
+ q["a+1"] = {
113
+ "b+2" => {
114
+ "c+3" => %w(d+4 d),
115
+ "c+wat" => "c",
116
+ },
117
+ "b+wat" => "b"
118
+ }
119
+
120
+ assert_query "a=1&a%2B1[b%2B2][c%2B3][]=d%2B4&a%2B1[b%2B2][c%2B3][]=d&a%2B1[b%2B2][c%2Bwat]=c&a%2B1[b%2Bwat]=b", q.to_query_string
121
+ q2 = Query::Nested.parse(q.to_query_string)
122
+ assert_equal %w(a a+1), q2.keys
123
+ assert_equal "1", q2["a"]
124
+
125
+ assert_equal %w(b+2 b+wat), q2["a+1"].keys
126
+ assert_equal %w(c+3 c+wat), q2["a+1"]["b+2"].keys
127
+ assert_equal "b", q2["a+1"]["b+wat"]
128
+ assert_equal "c", q2["a+1"]["b+2"]["c+wat"]
129
+ assert_equal %w(d+4 d), q2["a+1"]["b+2"]["c+3"]
130
+ end
131
+
132
+ def test_encode_and_parse_deeply_nested_key
133
+ q = Query::Nested.new
134
+ q["a"] = 1
135
+ q["a+1"] = [ # 1
136
+ { # 2
137
+ "b+2" => { # 3
138
+ "c+3" => [ # 4
139
+ {"d+4" => %w(e 5)}
140
+ ]
141
+ }
142
+ }
143
+ ]
144
+
145
+ assert_query "a=1&a%2B1[][b%2B2][c%2B3][][d%2B4][]=e&a%2B1[][b%2B2][c%2B3][][d%2B4][]=5", q.to_query_string
146
+ q2 = Query::Nested.parse(q.to_query_string)
147
+ assert_equal %w(a a+1), q2.keys
148
+ assert_equal "1", q2["a"]
149
+
150
+ arr1 = q2["a+1"]
151
+ assert_equal 2, arr1.size
152
+ hash2 = arr1[0]
153
+ assert_equal %w(b+2), hash2.keys
154
+ hash3 = hash2["b+2"]
155
+ assert_equal %w(c+3), hash3.keys
156
+ arr4 = hash3["c+3"]
157
+ assert_equal 1, arr4.size
158
+ hash5 = arr4[0]
159
+ assert_equal %w(e), hash5["d+4"]
160
+
161
+ hash2 = arr1[1]
162
+ assert_equal %w(b+2), hash2.keys
163
+ hash3 = hash2["b+2"]
164
+ assert_equal %w(c+3), hash3.keys
165
+ arr4 = hash3["c+3"]
166
+ assert_equal 1, arr4.size
167
+ hash5 = arr4[0]
168
+ assert_equal %w(5), hash5["d+4"]
169
+ end
170
+
171
+ def test_encode_hash_value_in_flat_query
172
+ q = Query::Flat.new
173
+ q["a"] = {1 => "a", 2 => "b"}
174
+ assert_raises NotImplementedError do
175
+ q.to_query_string
176
+ end
177
+ end
178
+
179
+ def test_parse_hash_value_in_flat_query
180
+ q = Query::Flat.parse("a=1&a%2Ba[1%2B1]=a%2Ba&a%2Ba[2%2B2]=b%2Bb")
181
+ assert_equal %w(a a+a[1+1] a+a[2+2]), q.keys
182
+ end
183
+
184
+ def assert_query(expected, actual)
185
+ expected.gsub! /\[|\]/, "[" => "%5B", "]" => "%5D"
186
+ assert_equal expected, actual
187
+ end
188
+ end
189
+ end