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.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +28 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +28 -0
- data/LICENSE.md +20 -0
- data/README.md +317 -0
- data/Rakefile +1 -0
- data/contributors.yaml +8 -0
- data/hurley.gemspec +29 -0
- data/lib/hurley.rb +104 -0
- data/lib/hurley/addressable.rb +9 -0
- data/lib/hurley/client.rb +349 -0
- data/lib/hurley/connection.rb +123 -0
- data/lib/hurley/header.rb +144 -0
- data/lib/hurley/multipart.rb +235 -0
- data/lib/hurley/options.rb +142 -0
- data/lib/hurley/query.rb +252 -0
- data/lib/hurley/tasks.rb +111 -0
- data/lib/hurley/test.rb +101 -0
- data/lib/hurley/test/integration.rb +249 -0
- data/lib/hurley/test/server.rb +102 -0
- data/lib/hurley/url.rb +197 -0
- data/script/bootstrap +2 -0
- data/script/package +7 -0
- data/script/test +168 -0
- data/test/client_test.rb +585 -0
- data/test/header_test.rb +108 -0
- data/test/helper.rb +14 -0
- data/test/live/net_http_test.rb +16 -0
- data/test/multipart_test.rb +306 -0
- data/test/query_test.rb +189 -0
- data/test/test_test.rb +38 -0
- data/test/url_test.rb +443 -0
- metadata +181 -0
data/test/header_test.rb
ADDED
@@ -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
|
data/test/query_test.rb
ADDED
@@ -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
|