heartland-retail 5.0.0

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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +16 -0
  5. data/.yardopts +1 -0
  6. data/Gemfile +15 -0
  7. data/Gemfile.lock +84 -0
  8. data/LICENSE +21 -0
  9. data/README.md +272 -0
  10. data/Rakefile +18 -0
  11. data/heartland-retail.gemspec +17 -0
  12. data/lib/heartland-retail.rb +1 -0
  13. data/lib/heartland/client.rb +299 -0
  14. data/lib/heartland/client/body.rb +13 -0
  15. data/lib/heartland/client/collection.rb +121 -0
  16. data/lib/heartland/client/errors.rb +17 -0
  17. data/lib/heartland/client/resource.rb +215 -0
  18. data/lib/heartland/client/response.rb +84 -0
  19. data/lib/heartland/client/uri.rb +117 -0
  20. data/spec/heartland/client/body_spec.rb +31 -0
  21. data/spec/heartland/client/resource_spec.rb +318 -0
  22. data/spec/heartland/client/response_spec.rb +105 -0
  23. data/spec/heartland/client/uri_spec.rb +157 -0
  24. data/spec/heartland/client_spec.rb +361 -0
  25. data/spec/shared_client_context.rb +5 -0
  26. data/spec/spec_helper.rb +17 -0
  27. data/vendor/cache/addressable-2.2.8.gem +0 -0
  28. data/vendor/cache/codeclimate-test-reporter-1.0.8.gem +0 -0
  29. data/vendor/cache/coderay-1.1.0.gem +0 -0
  30. data/vendor/cache/coveralls-0.7.11.gem +0 -0
  31. data/vendor/cache/crack-0.4.2.gem +0 -0
  32. data/vendor/cache/diff-lcs-1.2.5.gem +0 -0
  33. data/vendor/cache/docile-1.1.5.gem +0 -0
  34. data/vendor/cache/faraday-0.17.3.gem +0 -0
  35. data/vendor/cache/hashie-4.1.0.gem +0 -0
  36. data/vendor/cache/json-2.3.0.gem +0 -0
  37. data/vendor/cache/method_source-0.8.2.gem +0 -0
  38. data/vendor/cache/mime-types-2.4.3.gem +0 -0
  39. data/vendor/cache/multi_json-1.11.0.gem +0 -0
  40. data/vendor/cache/multipart-post-2.1.1.gem +0 -0
  41. data/vendor/cache/netrc-0.10.3.gem +0 -0
  42. data/vendor/cache/pry-0.10.1.gem +0 -0
  43. data/vendor/cache/rake-10.4.2.gem +0 -0
  44. data/vendor/cache/rest-client-1.7.3.gem +0 -0
  45. data/vendor/cache/rspec-3.2.0.gem +0 -0
  46. data/vendor/cache/rspec-core-3.2.2.gem +0 -0
  47. data/vendor/cache/rspec-expectations-3.2.0.gem +0 -0
  48. data/vendor/cache/rspec-mocks-3.2.1.gem +0 -0
  49. data/vendor/cache/rspec-support-3.2.2.gem +0 -0
  50. data/vendor/cache/safe_yaml-1.0.4.gem +0 -0
  51. data/vendor/cache/simplecov-0.9.2.gem +0 -0
  52. data/vendor/cache/simplecov-html-0.9.0.gem +0 -0
  53. data/vendor/cache/slop-3.6.0.gem +0 -0
  54. data/vendor/cache/term-ansicolor-1.3.0.gem +0 -0
  55. data/vendor/cache/thor-0.19.1.gem +0 -0
  56. data/vendor/cache/tins-1.3.5.gem +0 -0
  57. data/vendor/cache/webmock-1.17.4.gem +0 -0
  58. metadata +140 -0
@@ -0,0 +1,318 @@
1
+ require 'spec_helper'
2
+
3
+ describe HeartlandRetail::Client::Resource do
4
+ include_context "client"
5
+
6
+ let(:resource_path) { '/some/path' }
7
+ let(:resource) { HeartlandRetail::Client::Resource.new(client, resource_path) }
8
+
9
+ describe "[]" do
10
+ it "should return a new resource" do
11
+ expect(resource["subpath"]).to be_a HeartlandRetail::Client::Resource
12
+ expect(resource["subpath"].object_id).not_to eq(resource.object_id)
13
+ end
14
+
15
+ it "should return a resource with the given subpath appended to its URI" do
16
+ expect(resource["subpath"].uri.to_s).to eq("#{base_url}/some/path/subpath")
17
+ end
18
+
19
+ it "should return a resource with the same client instance" do
20
+ expect(resource["subpath"].client).to be === resource.client
21
+ end
22
+
23
+ it "should accept a symbol as a path" do
24
+ expect(resource[:subpath].uri.to_s).to eq("#{base_url}/some/path/subpath")
25
+ end
26
+
27
+ it "should URI encode the given subpath" do
28
+ expect(resource["subpath with spaces"].uri.to_s).to eq(
29
+ "#{base_url}/some/path/subpath%20with%20spaces"
30
+ )
31
+ end
32
+ end
33
+
34
+ %w{query params}.each do |method|
35
+ describe method do
36
+ describe "when called with a hash" do
37
+ it "should set the query string parameters" do
38
+ expect(resource.__send__(method, :a => 1, :b => 2).uri.to_s).to eq("#{base_url}/some/path?a=1&b=2")
39
+ end
40
+
41
+ it "should URL encode the given keys and values" do
42
+ expect(resource.__send__(method, "i have spaces" => "so do i: duh").uri.to_s).to eq(
43
+ "#{base_url}/some/path?i+have+spaces=so+do+i%3A+duh"
44
+ )
45
+ end
46
+
47
+ it "should add bracket notation for array parameters" do
48
+ expect(resource.__send__(method, :somearray => [1, 2, 3]).uri.to_s).to eq(
49
+ "#{base_url}/some/path?somearray%5B%5D=1&somearray%5B%5D=2&somearray%5B%5D=3"
50
+ )
51
+ end
52
+
53
+ it "should return a new resource without modifying the existing URI" do
54
+ new_resource = resource.query(per_page: 1)
55
+ expect(new_resource.uri.to_s).to eq("#{base_url}/some/path?per_page=1")
56
+ expect(resource.uri.to_s).to eq("#{base_url}/some/path")
57
+ end
58
+ end
59
+
60
+ describe "when called without arguments" do
61
+ it "should return the current query string parameters as a hash" do
62
+ expect(resource.__send__(method)).to eq({})
63
+ new_resource = resource.__send__(method, :a => 1, :b => 2)
64
+ expect(new_resource.__send__(method)).to eq({"a"=>"1", "b"=>"2"})
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ describe "filter" do
71
+ describe "when given a hash" do
72
+ it "should add a _filter query string param" do
73
+ expect(resource.filter(:a => 1, :b => 2).uri).to eq(
74
+ "#{base_url}/some/path?_filter=%7B%22a%22%3A1%2C%22b%22%3A2%7D".to_uri
75
+ )
76
+ end
77
+ end
78
+
79
+ describe "when called multiple times" do
80
+ it "should append args to _filter param as JSON array" do
81
+ expect(resource.filter(:a => 1).filter(:b => 2).filter(:c => 3).uri).to eq(
82
+ "#{base_url}/some/path?_filter=%5B%7B%22a%22%3A1%7D%2C%7B%22b%22%3A2%7D%2C%7B%22c%22%3A3%7D%5D".to_uri
83
+ )
84
+ end
85
+ end
86
+
87
+ describe "when given a string" do
88
+ it "should add a _filter query string param" do
89
+ expect(resource.filter('{"a":1,"b":2}').uri).to eq(
90
+ "#{base_url}/some/path?_filter=%7B%22a%22%3A1%2C%22b%22%3A2%7D".to_uri
91
+ )
92
+ end
93
+ end
94
+
95
+ describe "when called multiple times with other methods" do
96
+ it "should append args to _filter param as JSON array" do
97
+ expect(resource.filter(:a => 1).embed(:other).only(:field).filter(:b => 2).uri).to eq(
98
+ "#{base_url}/some/path?_filter=%5B%7B%22a%22%3A1%7D%2C%7B%22b%22%3A2%7D%5D&_include%5B%5D=other&_only%5B%5D=field".to_uri
99
+ )
100
+ end
101
+ end
102
+ end
103
+
104
+ describe "sort" do
105
+ it "should set the sort parameter based on the given values" do
106
+ expect(resource.sort('f1', 'f2,desc').uri.to_s).to eq(
107
+ "#{base_url}/some/path?sort%5B%5D=f1&sort%5B%5D=f2%2Cdesc"
108
+ )
109
+ end
110
+
111
+ it "should replace any existing sort parameter" do
112
+ resource.sort('f1', 'f2,desc')
113
+ expect(resource.sort('f3,asc', 'f4').uri.to_s).to eq(
114
+ "#{base_url}/some/path?sort%5B%5D=f3%2Casc&sort%5B%5D=f4"
115
+ )
116
+ end
117
+ end
118
+
119
+ describe "only" do
120
+ it "should set the _only parameter based on the given values" do
121
+ expect(resource.only('f1', 'f2').uri.to_s).to eq(
122
+ "#{base_url}/some/path?_only%5B%5D=f1&_only%5B%5D=f2"
123
+ )
124
+ end
125
+
126
+ it "should replace the existing _only parameters" do
127
+ expect(resource.only('f1').only('f2', 'f3').uri.to_s).to eq(
128
+ "#{base_url}/some/path?_only%5B%5D=f2&_only%5B%5D=f3"
129
+ )
130
+ end
131
+ end
132
+
133
+ %w{each each_page}.each do |method|
134
+ describe method do
135
+ it "should call the client's #{method} method with the resource's URI" do
136
+ expect(client).to receive(method).with(resource.uri)
137
+ resource.__send__(method)
138
+ end
139
+ end
140
+ end
141
+
142
+ %w{get head delete}.each do |method|
143
+ describe method do
144
+ it "should call the client's #{method} method with the resource's URI and a header hash" do
145
+ expect(client).to receive(method).with(resource.uri, false)
146
+ resource.__send__(method)
147
+ end
148
+ end
149
+ end
150
+
151
+ %w{put post}.each do |method|
152
+ describe method do
153
+ it "should call the client's #{method} method with the resource's URI, the given body, and a headers hash" do
154
+ expect(client).to receive(method).with(resource.uri, "body", false)
155
+ resource.__send__(method, "body")
156
+ end
157
+ end
158
+ end
159
+
160
+ describe "count" do
161
+ let(:response_data) {
162
+ {
163
+ :status => 200,
164
+ :body => {total: 123}.to_json
165
+ }
166
+ }
167
+
168
+ it "should call the client's count method with the resource's URI" do
169
+ expect(client).to receive(:count).with(resource.uri)
170
+ resource.count
171
+ end
172
+
173
+ it "should set the per_page query string param to 1" do
174
+ request_stub = stub_request(:get, "#{base_url}/some/path?page=1&per_page=1").to_return(response_data)
175
+ resource.count
176
+ expect(request_stub).to have_been_requested
177
+ end
178
+
179
+ it "should return the resource count" do
180
+ request_stub = stub_request(:get, "#{base_url}/some/path?page=1&per_page=1").to_return(response_data)
181
+ expect(resource.count).to eq(123)
182
+ end
183
+
184
+ it "should not modify the original resource URI" do
185
+ request_stub = stub_request(:get, "#{base_url}/some/path?page=1&per_page=1").to_return(response_data)
186
+ resource.count
187
+ expect(resource.uri.to_s).to eq("#{base_url}/some/path")
188
+ end
189
+ end
190
+
191
+ describe "first" do
192
+ let(:response_data) {
193
+ {
194
+ :status => 200,
195
+ :body => {:results => [{:id => "Me first!"}, {:id => "Me second!"}]}.to_json
196
+ }
197
+ }
198
+
199
+ it "should set the per_page query string param to 1" do
200
+ request_stub = stub_request(:get, "#{base_url}/some/path?page=1&per_page=1").to_return(response_data)
201
+ resource.first
202
+ expect(request_stub).to have_been_requested
203
+ end
204
+
205
+ it "should return the first element of the :results array" do
206
+ request_stub = stub_request(:get, "#{base_url}/some/path?page=1&per_page=1").to_return(response_data)
207
+ expect(resource.first).to eq({"id" => "Me first!"})
208
+ end
209
+
210
+ it "should not modify the original resource URI" do
211
+ request_stub = stub_request(:get, "#{base_url}/some/path?page=1&per_page=1").to_return(response_data)
212
+ resource.first
213
+ expect(resource.uri.to_s).to eq("#{base_url}/some/path")
214
+ end
215
+ end
216
+
217
+ describe "embed" do
218
+ it "should support a single embed" do
219
+ expect(resource.embed(:thing1).uri.to_s).to eq(
220
+ "#{base_url}/some/path?_include%5B%5D=thing1"
221
+ )
222
+ end
223
+
224
+ it "should support multiple embeds" do
225
+ expect(resource.embed(:thing1, :thing2, :thing3).uri.to_s).to eq(
226
+ "#{base_url}/some/path?_include%5B%5D=thing1&_include%5B%5D=thing2&_include%5B%5D=thing3"
227
+ )
228
+ end
229
+
230
+ it "should merge multiple embed calls" do
231
+ expect(resource.embed(:thing1, :thing2).embed(:thing3, :thing4).uri.to_s).to eq(
232
+ "#{base_url}/some/path?_include%5B%5D=thing1&_include%5B%5D=thing2&_include%5B%5D=thing3&_include%5B%5D=thing4"
233
+ )
234
+ end
235
+
236
+ it "should merge a call to embed with a manually added _include query param" do
237
+ expect(resource.query('_include[]' => :thing1).embed(:thing2, :thing3).uri.to_s).to eq(
238
+ "#{base_url}/some/path?_include%5B%5D=thing1&_include%5B%5D=thing2&_include%5B%5D=thing3"
239
+ )
240
+ end
241
+ end
242
+
243
+ describe "while_results" do
244
+ it "should yield each result to the block as long as the response includes results" do
245
+ results = ["r1", "r2", "r3"]
246
+
247
+ request_stub = stub_request(:get, "#{base_url}/some/path").to_return do |req|
248
+ {:body => {:results => results}.to_json}
249
+ end
250
+
251
+ yielded_results = []
252
+
253
+ # timeout in case of endless loop
254
+ Timeout::timeout(10) do
255
+ resource.while_results do |result|
256
+ yielded_results.push results.shift
257
+ end
258
+ end
259
+
260
+ expect(yielded_results).to eq(["r1", "r2", "r3"])
261
+ end
262
+
263
+ it "should raise an exception if it receives an error response" do
264
+ request_stub = stub_request(:get, "#{base_url}/some/path").to_return do |req|
265
+ {:status => 400}
266
+ end
267
+
268
+ # timeout in case of endless loop
269
+ Timeout::timeout(10) do
270
+ expect do
271
+ resource.while_results do |result|
272
+ # nothing
273
+ end
274
+ end.to raise_error(HeartlandRetail::Client::RequestFailed)
275
+ end
276
+ end
277
+
278
+ describe "exists?" do
279
+ let(:response) { double(HeartlandRetail::Client::Response) }
280
+
281
+ it "should return true if the response indicates success" do
282
+ allow(response).to receive(:success?).and_return(true)
283
+ expect(client).to receive(:head).with(resource.uri, false).and_return(response)
284
+ expect(resource.exists?).to be === true
285
+ end
286
+
287
+ it "should return false if the response status is 404" do
288
+ allow(response).to receive(:status).and_return(404)
289
+ allow(response).to receive(:success?).and_return(false)
290
+ expect(client).to receive(:head).with(resource.uri, false).and_return(response)
291
+ expect(resource.exists?).to be === false
292
+ end
293
+
294
+ it "should raise a RequestFailed exception if the request fails but the status is not 404" do
295
+ allow(response).to receive(:status).and_return(400)
296
+ allow(response).to receive(:success?).and_return(false)
297
+ expect(client).to receive(:head).with(resource.uri, false).and_return(response)
298
+ expect { resource.exists? }.to raise_error { |e|
299
+ expect(e).to be_a HeartlandRetail::Client::RequestFailed
300
+ expect(e.response).to be === response
301
+ expect(e.message).to eq("Request during call to 'exists?' resulted in non-404 error.")
302
+ }
303
+ end
304
+ end
305
+
306
+ describe "empty?" do
307
+ it "should return true if the resource has a count of zero" do
308
+ allow(resource).to receive(:count).and_return 0
309
+ expect(resource.empty?).to be === true
310
+ end
311
+
312
+ it "should return false if the resource has a count greater than zero" do
313
+ allow(resource).to receive(:count).and_return 10
314
+ expect(resource.empty?).to be === false
315
+ end
316
+ end
317
+ end
318
+ end
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+
3
+ describe HeartlandRetail::Client::Response do
4
+ include_context "client"
5
+
6
+ let(:raw_body) { '{"key":"value"}' }
7
+ let(:raw_headers) { { 'X-Custom-Header' => 'Hi' } }
8
+ let(:status_code) { 200 }
9
+ let(:path) { '/path' }
10
+ let(:faraday_response) do
11
+ env = Faraday::Env.new(:get, raw_body, path, nil, nil, nil, nil,
12
+ nil, raw_body, raw_headers, status_code)
13
+
14
+ Faraday::Response.new(env)
15
+ end
16
+ let(:response) { HeartlandRetail::Client::Response.new(faraday_response, client) }
17
+
18
+ describe "body" do
19
+ describe "if raw body is valid JSON" do
20
+ it "should return a HeartlandRetail::Client::Body" do
21
+ expect(response.body).to be_a HeartlandRetail::Client::Body
22
+ end
23
+
24
+ it "should wrap the parsed response body" do
25
+ expect(response.body.to_hash).to eq({"key" => "value"})
26
+ end
27
+ end
28
+
29
+ describe "if raw body is not valid JSON" do
30
+ let(:raw_body) { 'I am not JSON!' }
31
+ it "should raise an informative error" do
32
+ expect { response.body }.to raise_error \
33
+ HeartlandRetail::Client::BodyError,
34
+ "Can't parse response body. (Hint: Try the raw_body method.)"
35
+ end
36
+ end
37
+
38
+ describe "if raw body is empty" do
39
+ let(:raw_body) { '' }
40
+ it "should raise an informative error" do
41
+ expect { response.body }.to raise_error \
42
+ HeartlandRetail::Client::BodyError,
43
+ "Response body is empty. (Hint: If you just created a new resource, try: response.resource.get)"
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "raw_body" do
49
+ it "should return the raw body JSON" do
50
+ expect(response.raw_body).to eq(raw_body)
51
+ end
52
+ end
53
+
54
+ describe "headers" do
55
+ it "should return the response headers as a hash" do
56
+ expect(response.headers).to eq({'X-Custom-Header' => 'Hi'})
57
+ end
58
+ end
59
+
60
+ describe "resource" do
61
+ describe "when Location header is returned" do
62
+ let(:raw_headers) { { Location: '/new/path' } }
63
+
64
+ it "should be a HeartlandRetail::Client::Resource" do
65
+ expect(response.resource).to be_a HeartlandRetail::Client::Resource
66
+ end
67
+
68
+ it "should have the Location header value as its URL" do
69
+ expect(response.resource.uri.to_s).to eq("#{base_url}/new/path")
70
+ end
71
+ end
72
+
73
+ describe "when Location header is not returned" do
74
+ let(:raw_headers) { Hash.new }
75
+
76
+ it "should be nil" do
77
+ expect(response.resource).to be_nil
78
+ end
79
+ end
80
+ end
81
+
82
+ describe "[]" do
83
+ it "should forward [] to body" do
84
+ expect(response.body).to receive(:[]).with("key").and_return("value")
85
+ expect(response["key"]).to eq("value")
86
+ end
87
+ end
88
+
89
+ describe "success?" do
90
+ %w{100 101 102 200 201 202 203 204 205 206 207 208 226 300 301 302 303 304 305 306 307 308}.each do |code|
91
+ it "should return true if the response status code is #{code}" do
92
+ allow(response).to receive(:status).and_return(code.to_i)
93
+ expect(response.success?).to be === true
94
+ end
95
+ end
96
+
97
+ %w{400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 420 422 423 424 425 426 428 429 431
98
+ 444 449 450 499 500 501 502 503 504 505 506 507 508 509 510 511 598 599}.each do |code|
99
+ it "should return false if the response status code is #{code}" do
100
+ allow(response).to receive(:status).and_return(code.to_i)
101
+ expect(response.success?).to be === false
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ describe HeartlandRetail::Client::URI do
4
+ let(:uri) { described_class.parse('/relative/path') }
5
+
6
+ describe "subpath" do
7
+ it "should return a new URI with the path relative to the receiver" do
8
+ expect(uri.subpath('other')).to eq(described_class.parse('/relative/path/other'))
9
+ expect(uri.subpath('/other')).to eq(described_class.parse('/relative/path/other'))
10
+ uri.subpath(described_class.parse('/other')) == described_class.parse('/relative/path/other')
11
+ end
12
+ end
13
+
14
+ describe "parse" do
15
+ describe "when called with a URI object" do
16
+ it "should return a cloned URI object" do
17
+ parsed_uri = described_class.parse(uri)
18
+ expect(uri.class).to eq(parsed_uri.class)
19
+ expect(uri.object_id).not_to eq(parsed_uri.object_id)
20
+ expect(parsed_uri.to_s).to eq(uri.to_s)
21
+ end
22
+ end
23
+
24
+ describe "when called with a URI string" do
25
+ it "should return a URI based on the given string" do
26
+ uri = described_class.parse('/some_path')
27
+ expect(uri).to be_a(described_class)
28
+ expect(uri.to_s).to eq('/some_path')
29
+ end
30
+ end
31
+ end
32
+
33
+ describe "dup" do
34
+ it "should return a duplicate URI object" do
35
+ dup_uri = uri.dup
36
+ expect(uri.class).to eq(dup_uri.class)
37
+ expect(uri.to_s).to eq(dup_uri.to_s)
38
+ expect(uri.object_id).not_to eq(dup_uri.object_id)
39
+ end
40
+
41
+ describe "when mutating the copy" do
42
+ it "should not affect the original" do
43
+ dup_uri = uri.dup
44
+ dup_uri.query_values = {per_page: 1}
45
+ expect(uri.to_s).to eq('/relative/path')
46
+ expect(dup_uri.to_s).to eq('/relative/path?per_page=1')
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "==" do
52
+ it "should consider two URIs parsed from the same string equal" do
53
+ expect(
54
+ described_class.parse('http://example.com/some_path?a=1&b=2') ==
55
+ described_class.parse('http://example.com/some_path?a=1&b=2')
56
+ ).to be(true)
57
+ end
58
+
59
+ it "should consider two URIs parsed from different strings not equal" do
60
+ expect(
61
+ described_class.parse('http://example.com/some_path?a=1&b=2') ==
62
+ described_class.parse('http://example.com/some_path?a=1&c=3')
63
+ ).to be(false)
64
+
65
+ expect(
66
+ described_class.parse('http://foo.example.com') ==
67
+ described_class.parse('http://bar.example.com')
68
+ ).to be(false)
69
+ end
70
+ end
71
+
72
+ describe "merge_query_values!" do
73
+ it "should merge the given values with the existing query_values" do
74
+ uri.query_values = {'a' => '1', 'b' => '2'}
75
+ uri.merge_query_values! 'c' => '3'
76
+ expect(uri.query_values).to eq(
77
+ {'a' => '1', 'b' => '2', 'c' => '3'}
78
+ )
79
+ end
80
+
81
+ it "should overwrite the previous values when a new value is given" do
82
+ uri.query_values = {'a' => '1', 'b' => '2'}
83
+ uri.merge_query_values! 'a' => '3', 'b' => '4'
84
+ expect(uri.query_values).to eq(
85
+ {'a' => '3', 'b' => '4'}
86
+ )
87
+ end
88
+
89
+ it "should overwrite the previous values if a new array is given" do
90
+ uri.query_values = {'a' => '1', 'b' => ['2', '3']}
91
+ uri.merge_query_values! 'b' => ['4', '5']
92
+ expect(uri.query_values).to eq(
93
+ {'a' => '1', 'b' => ['4', '5']}
94
+ )
95
+ end
96
+
97
+ it "should set the given values if there are no existing query_values" do
98
+ expect(uri.query_values).to be_nil
99
+ uri.merge_query_values! 'a' => ['10'], 'b' => '20', 'c' => '30'
100
+ expect(uri.query_values).to eq({'a' => ['10'], 'b' => '20', 'c' => '30'})
101
+ end
102
+ end
103
+
104
+ describe "query_values=" do
105
+ it "should set the string value for the specified key" do
106
+ uri.query_values = {'p1' => '1'}
107
+ expect(uri.query_values).to eq({'p1' => '1'})
108
+ end
109
+
110
+ it "should preserve empty bracket notation for array params" do
111
+ uri.query = 'sort[]=f1&sort[]=f2'
112
+ uri.query_values = uri.query_values
113
+ expect(uri.to_s).to eq('/relative/path?sort%5B%5D=f1&sort%5B%5D=f2')
114
+ end
115
+
116
+ it "should stringify symbol keys" do
117
+ uri.query_values = {:a => '1'}
118
+ expect(uri.query_values).to eq({'a' => '1'})
119
+ end
120
+
121
+ it "should stringify boolean param values" do
122
+ uri.query_values = {:p1 => true, :p2 => false}
123
+ expect(uri.to_s).to eq('/relative/path?p1=true&p2=false')
124
+ end
125
+
126
+ it "should support hash param values" do
127
+ uri.query_values = {:a => {:b => {:c => 123}}}
128
+ expect(uri.to_s).to eq(
129
+ '/relative/path?a=%7B%22b%22%3D%3E%7B%22c%22%3D%3E123%7D%7D'
130
+ )
131
+ end
132
+
133
+ it "should add [] to the key for array values" do
134
+ uri.query_values = {:a => ['1', '2', '3']}
135
+ expect(uri.query).to eq('a%5B%5D=1&a%5B%5D=2&a%5B%5D=3')
136
+ end
137
+
138
+ it "should remove duplicate values for the same key" do
139
+ uri.query_values = {:a => ['1', '1', '2']}
140
+ expect(uri.query_values).to eq({'a' => ['1', '2']})
141
+ end
142
+ end
143
+
144
+ describe "query_values" do
145
+ it "should return the current query values" do
146
+ uri.query = 'sort[]=f1&sort[]=f2&per_page=all'
147
+ uri.query_values = uri.query_values
148
+ expect(uri.query_values).to eq({'sort' => ['f1', 'f2'], 'per_page' => 'all'})
149
+ end
150
+
151
+ it "should remove [] from array keys" do
152
+ uri.query = 'sort[]=f1&sort[]=f2'
153
+ uri.query_values = uri.query_values
154
+ expect(uri.query_values).to eq({'sort' => ['f1', 'f2']})
155
+ end
156
+ end
157
+ end