bootic_client 0.0.31 → 0.0.32
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 +4 -4
- data/README.md +34 -0
- data/lib/bootic_client/entity.rb +6 -1
- data/lib/bootic_client/stubbing.rb +118 -0
- data/lib/bootic_client/version.rb +1 -1
- data/lib/bootic_client.rb +18 -0
- data/spec/entity_spec.rb +28 -0
- data/spec/stubbing_spec.rb +96 -0
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6eb630196e916b01799d6ff1079895e608073f9cf141c2fb66496c08494f2825
|
4
|
+
data.tar.gz: f84b45a3e467863a71f21e8560985302197ca3e7b1e1598945fb9502ce10cd51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8ce6a6af2642f5d73122e7c7097b57efaf0594509ca8119e0d4c62606b60b094aa4debeb070b1ea42a9d04007fc7e66f56c27ee517a0a2d854c114893b9fd11
|
7
|
+
data.tar.gz: f5979b3ca50b880006d29ab0555d2fb5ab063f8805e1415785d64cdeb729b7514d3ab833c2d429075206acf45cb8d3db4d92cbe8e5893bb151c6aacf4b0a7f1a
|
data/README.md
CHANGED
@@ -320,6 +320,40 @@ messaging_api_root = client.from_url("https://some.api.com")
|
|
320
320
|
messaging_api.do_something(foo: "bar") # etc
|
321
321
|
```
|
322
322
|
|
323
|
+
## Testing
|
324
|
+
|
325
|
+
# What
|
326
|
+
|
327
|
+
This library provides methods to simplify testing. For example, to stub a chain of links you can simply do:
|
328
|
+
|
329
|
+
```rb
|
330
|
+
BooticClient.stub_chain('root.shops.first').and_return_data({
|
331
|
+
'name' => 'Foo bar'
|
332
|
+
})
|
333
|
+
|
334
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
335
|
+
shop = client.root.shops.first
|
336
|
+
expect(shop).to be_a BooticClient::Entity
|
337
|
+
expect(shop.name).to eq 'Foo bar'
|
338
|
+
```
|
339
|
+
|
340
|
+
You can also stub links that requires arguments, like this:
|
341
|
+
|
342
|
+
```rb
|
343
|
+
BooticClient.stub_chain('root.shops', foo: 1).and_return_data({
|
344
|
+
'name' => 'Foo 1'
|
345
|
+
})
|
346
|
+
BooticClient.stub_chain('root.shops', foo: 1, bar: { hello: 123 }).and_return_data({
|
347
|
+
'name' => 'Foo 2'
|
348
|
+
})
|
349
|
+
|
350
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
351
|
+
expect(client.root.shops(foo: 1).name).to eq 'Foo 1'
|
352
|
+
|
353
|
+
# arg order doesn't matter
|
354
|
+
expect(client.root.shops(bar: { hello: 123 }, foo: 2).name).to eq 'Foo 2'
|
355
|
+
```
|
356
|
+
|
323
357
|
## Contributing
|
324
358
|
|
325
359
|
1. Fork it
|
data/lib/bootic_client/entity.rb
CHANGED
@@ -15,9 +15,14 @@ module BooticClient
|
|
15
15
|
|
16
16
|
Enumerator.new do |yielder|
|
17
17
|
loop do
|
18
|
-
page.each{|item| yielder.yield
|
18
|
+
page.each { |item| yielder.yield(item) }
|
19
19
|
raise StopIteration unless page.has_rel?(:next)
|
20
20
|
page = page.next
|
21
|
+
|
22
|
+
if page.has?(:errors) # && page.errors.first.messages.first['cannot be higher'] # reached last page
|
23
|
+
yielder.yield(nil, page.errors) # yield a nil value so caller can stop gracefully
|
24
|
+
raise StopIteration
|
25
|
+
end
|
21
26
|
end
|
22
27
|
end
|
23
28
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module BooticClient
|
2
|
+
module Stubbing
|
3
|
+
MissingStubError = Class.new(StandardError)
|
4
|
+
|
5
|
+
module Stubber
|
6
|
+
def from_hash(h)
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def stub_chain(method_path, opts = {})
|
11
|
+
meths = method_path.split('.')
|
12
|
+
c = 0
|
13
|
+
opts = stringify_keys(opts)
|
14
|
+
|
15
|
+
meths.reduce(self) do |stub, method_name|
|
16
|
+
c += 1
|
17
|
+
a = c == meths.size ? opts : {}
|
18
|
+
stub.stub(method_name, a)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def stub(method_name, opts = {})
|
23
|
+
key = stub_key(method_name, opts)
|
24
|
+
if st = stubs[key]
|
25
|
+
st.stub(method_name, opts)
|
26
|
+
st
|
27
|
+
else
|
28
|
+
stubs[key] = Stub.new(method_name, opts)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing(method_name, *args, &block)
|
33
|
+
opts = stringify_keys(args.first)
|
34
|
+
if stub = stubs[stub_key(method_name, opts)]
|
35
|
+
stub.returns? ? stub.returns : stub
|
36
|
+
else
|
37
|
+
raise MissingStubError, "No method stubbed for '#{method_name}' with options #{opts.inspect}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def respond_to_missing?(method_name, include_private = false)
|
42
|
+
stubs.keys.any?{|k|
|
43
|
+
k.to_s =~ /^#{method_name.to_s}/
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def stub_key(method_name, opts)
|
49
|
+
[method_name.to_s, options_key(opts || {})].join('_')
|
50
|
+
end
|
51
|
+
|
52
|
+
def options_key(opts)
|
53
|
+
# sort keys
|
54
|
+
keys = opts.keys.sort
|
55
|
+
hash = keys.each_with_object({}) do |key, h|
|
56
|
+
value = if opts[key].is_a?(Hash)
|
57
|
+
options_key(opts[key])
|
58
|
+
else
|
59
|
+
opts[key].to_s
|
60
|
+
end
|
61
|
+
|
62
|
+
h[key] = value
|
63
|
+
end
|
64
|
+
|
65
|
+
hash.inspect
|
66
|
+
end
|
67
|
+
|
68
|
+
def stringify_keys(hash)
|
69
|
+
return hash unless hash.is_a?(Hash)
|
70
|
+
|
71
|
+
hash.each_with_object({}) do |(k, v), h|
|
72
|
+
h[k.to_s] = stringify_keys(v)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class StubRoot
|
78
|
+
include Stubber
|
79
|
+
|
80
|
+
def initialize
|
81
|
+
@stubs = {}
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
attr_reader :stubs
|
86
|
+
end
|
87
|
+
|
88
|
+
class Stub
|
89
|
+
include Stubber
|
90
|
+
|
91
|
+
def initialize(method_name = '', opts = {})
|
92
|
+
@method_name, @opts = method_name, opts
|
93
|
+
@return_data = nil
|
94
|
+
@stubs = {}
|
95
|
+
end
|
96
|
+
|
97
|
+
def and_return_data(data)
|
98
|
+
@return_data = data
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
def returns?
|
103
|
+
!!@return_data
|
104
|
+
end
|
105
|
+
|
106
|
+
def returns
|
107
|
+
if @return_data.is_a?(Array)
|
108
|
+
@return_data.map{|d| BooticClient::Entity.new(d, nil)}
|
109
|
+
else
|
110
|
+
BooticClient::Entity.new(@return_data || {}, nil)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
attr_reader :stubs
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/lib/bootic_client.rb
CHANGED
@@ -12,6 +12,7 @@ module BooticClient
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def client(strategy_name, client_opts = {}, &on_new_token)
|
15
|
+
return @stubber if @stubber
|
15
16
|
opts = client_opts.dup
|
16
17
|
opts[:logging] = configuration.logging
|
17
18
|
opts[:logger] = configuration.logger if configuration.logging
|
@@ -21,6 +22,10 @@ module BooticClient
|
|
21
22
|
strategies.fetch(strategy_name.to_sym).new configuration, opts, &on_new_token
|
22
23
|
end
|
23
24
|
|
25
|
+
def auth_host
|
26
|
+
@auth_host || AUTH_HOST
|
27
|
+
end
|
28
|
+
|
24
29
|
def configure(&block)
|
25
30
|
yield configuration
|
26
31
|
end
|
@@ -28,5 +33,18 @@ module BooticClient
|
|
28
33
|
def configuration
|
29
34
|
@configuration ||= Configuration.new
|
30
35
|
end
|
36
|
+
|
37
|
+
def stub!
|
38
|
+
require "bootic_client/stubbing"
|
39
|
+
@stubber = Stubbing::StubRoot.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def stub_chain(method_chain, opts = {})
|
43
|
+
@stubber.stub_chain(method_chain, opts)
|
44
|
+
end
|
45
|
+
|
46
|
+
def unstub!
|
47
|
+
@stubber = nil
|
48
|
+
end
|
31
49
|
end
|
32
50
|
end
|
data/spec/entity_spec.rb
CHANGED
@@ -232,6 +232,34 @@ describe BooticClient::Entity do
|
|
232
232
|
titles = results.map(&:title)
|
233
233
|
expect(titles).to match_array(['iPhone 4', 'iPhone 5', 'Item 3', 'Item 4'])
|
234
234
|
end
|
235
|
+
|
236
|
+
it 'handles errors gracefully' do
|
237
|
+
error_data = {
|
238
|
+
"_embedded" => {
|
239
|
+
'errors' => [
|
240
|
+
{ field: 'foo', messages: ['Ugly error'] }
|
241
|
+
]
|
242
|
+
}
|
243
|
+
}
|
244
|
+
|
245
|
+
error_page = BooticClient::Entity.new(error_data, client)
|
246
|
+
expect(client).to receive(:request_and_wrap).with(:get, '/foo?page=2', {}).and_return(error_page)
|
247
|
+
expect(client).to_not receive(:request_and_wrap).with(:get, '/foo?page=3', {})
|
248
|
+
|
249
|
+
valid = []
|
250
|
+
errors = nil
|
251
|
+
entity.full_set.each do |item, errors|
|
252
|
+
if item
|
253
|
+
valid.push(item)
|
254
|
+
else
|
255
|
+
expect(valid.count).to eq(2)
|
256
|
+
expect(errors.first.to_hash).to eq({ field: 'foo', messages: ['Ugly error']})
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
titles = valid.map(&:title)
|
261
|
+
expect(titles).to match_array(['iPhone 4', 'iPhone 5'])
|
262
|
+
end
|
235
263
|
end
|
236
264
|
end
|
237
265
|
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "stubbing" do
|
4
|
+
before do
|
5
|
+
BooticClient.stub!
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
BooticClient.unstub!
|
10
|
+
end
|
11
|
+
|
12
|
+
it "stubs method chains and returns entities" do
|
13
|
+
BooticClient.stub_chain('root.shops.first').and_return_data({
|
14
|
+
'name' => 'Foo bar'
|
15
|
+
})
|
16
|
+
|
17
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
18
|
+
shop = client.root.shops.first
|
19
|
+
expect(shop).to be_a BooticClient::Entity
|
20
|
+
expect(shop.name).to eq 'Foo bar'
|
21
|
+
end
|
22
|
+
|
23
|
+
it "stubs method chains and returns arrays of entities" do
|
24
|
+
BooticClient.stub_chain('root.shops').and_return_data([
|
25
|
+
{'name' => 'Foo bar'},
|
26
|
+
{'name' => 'Bar foo'}
|
27
|
+
])
|
28
|
+
|
29
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
30
|
+
shops = client.root.shops
|
31
|
+
expect(shops.first).to be_a BooticClient::Entity
|
32
|
+
expect(shops.last).to be_a BooticClient::Entity
|
33
|
+
expect(shops.first.name).to eq 'Foo bar'
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'can be chained further' do
|
37
|
+
BooticClient.stub_chain('foo.bar')
|
38
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
39
|
+
|
40
|
+
stub = client.foo.bar
|
41
|
+
stub.stub_chain('another.stubz').and_return_data({
|
42
|
+
'id' => 123
|
43
|
+
})
|
44
|
+
|
45
|
+
expect(stub.another.stubz.id).to eq 123
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'stubs depending on arguments' do
|
49
|
+
BooticClient.stub_chain('root.shops', foo: 0).and_return_data({
|
50
|
+
'name' => 'Foo 0'
|
51
|
+
})
|
52
|
+
BooticClient.stub_chain('root.shops', foo: 1).and_return_data({
|
53
|
+
'name' => 'Foo 1'
|
54
|
+
})
|
55
|
+
BooticClient.stub_chain('root.shops', foo: 2, bar: {yup: 'yiss'}).and_return_data({
|
56
|
+
'name' => 'Foo 2'
|
57
|
+
})
|
58
|
+
|
59
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
60
|
+
|
61
|
+
expect(client.root.shops(foo: 0).name).to eq 'Foo 0'
|
62
|
+
expect(client.root.shops(foo: 1).name).to eq 'Foo 1'
|
63
|
+
expect(client.root.shops(foo: 2, bar: {yup: 'yiss'}).name).to eq 'Foo 2'
|
64
|
+
# arg order shouldn't matter
|
65
|
+
expect(client.root.shops(bar: {yup: 'yiss'}, foo: 2).name).to eq 'Foo 2'
|
66
|
+
|
67
|
+
expect {
|
68
|
+
client.root.shops(foo: 2, bar: {yup: 'nope'})
|
69
|
+
}.to raise_error BooticClient::Stubbing::MissingStubError
|
70
|
+
end
|
71
|
+
|
72
|
+
it "stubs multiple chains with arguments" do
|
73
|
+
BooticClient.stub_chain('one.two', arg: 1).stub_chain('three.four').and_return_data('name' => 'example 1')
|
74
|
+
BooticClient.stub_chain('one.two', arg: 2).stub_chain('three.four').and_return_data('name' => 'example 2')
|
75
|
+
|
76
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
77
|
+
|
78
|
+
expect(client.one.two(arg: 1).three.four.name).to eq 'example 1'
|
79
|
+
expect(client.one.two(arg: 2).three.four.name).to eq 'example 2'
|
80
|
+
end
|
81
|
+
|
82
|
+
it "treats symbol and string keys the same" do
|
83
|
+
BooticClient.stub_chain('one.two', arg: 1).and_return_data('name' => 'example 1')
|
84
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
85
|
+
|
86
|
+
expect(client.one.two("arg" => 1).name).to eq 'example 1'
|
87
|
+
end
|
88
|
+
|
89
|
+
it "raises known exception if no stub found" do
|
90
|
+
client = BooticClient.client(:authorized, access_token: 'abc')
|
91
|
+
|
92
|
+
expect{
|
93
|
+
client.nope
|
94
|
+
}.to raise_error BooticClient::Stubbing::MissingStubError
|
95
|
+
end
|
96
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bootic_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.32
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ismael Celis
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -181,6 +181,7 @@ files:
|
|
181
181
|
- lib/bootic_client/strategies/client_credentials.rb
|
182
182
|
- lib/bootic_client/strategies/oauth2_strategy.rb
|
183
183
|
- lib/bootic_client/strategies/strategy.rb
|
184
|
+
- lib/bootic_client/stubbing.rb
|
184
185
|
- lib/bootic_client/version.rb
|
185
186
|
- lib/bootic_client/whiny_uri.rb
|
186
187
|
- spec/authorized_strategy_spec.rb
|
@@ -198,12 +199,13 @@ files:
|
|
198
199
|
- spec/safe_cache_serializer_spec.rb
|
199
200
|
- spec/spec_helper.rb
|
200
201
|
- spec/strategy_spec.rb
|
202
|
+
- spec/stubbing_spec.rb
|
201
203
|
- spec/whiny_uri_spec.rb
|
202
204
|
homepage: https://developers.bootic.net
|
203
205
|
licenses:
|
204
206
|
- MIT
|
205
207
|
metadata: {}
|
206
|
-
post_install_message:
|
208
|
+
post_install_message:
|
207
209
|
rdoc_options: []
|
208
210
|
require_paths:
|
209
211
|
- lib
|
@@ -218,8 +220,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
218
220
|
- !ruby/object:Gem::Version
|
219
221
|
version: '0'
|
220
222
|
requirements: []
|
221
|
-
rubygems_version: 3.
|
222
|
-
signing_key:
|
223
|
+
rubygems_version: 3.4.6
|
224
|
+
signing_key:
|
223
225
|
specification_version: 4
|
224
226
|
summary: Official Ruby client for the Bootic API
|
225
227
|
test_files:
|
@@ -238,4 +240,5 @@ test_files:
|
|
238
240
|
- spec/safe_cache_serializer_spec.rb
|
239
241
|
- spec/spec_helper.rb
|
240
242
|
- spec/strategy_spec.rb
|
243
|
+
- spec/stubbing_spec.rb
|
241
244
|
- spec/whiny_uri_spec.rb
|