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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8906bbfeddb298199ecb675eea93c3780da4a370633bd73cb633bc10f893ae5
4
- data.tar.gz: d0de74be08f4fffb7f68e02354dead206c67ee1db0662506668fe18ee0ebc4d3
3
+ metadata.gz: 6eb630196e916b01799d6ff1079895e608073f9cf141c2fb66496c08494f2825
4
+ data.tar.gz: f84b45a3e467863a71f21e8560985302197ca3e7b1e1598945fb9502ce10cd51
5
5
  SHA512:
6
- metadata.gz: caa8411fdb9ab778b1e30c0c9c2e39ee0847a7d8a652658314d058d273b52c653ca85fc444aed5894d4406455965a32202c52365ac7a32ba085d54280360e4df
7
- data.tar.gz: 1f4989cf0177a73128c9861cedc41a047a986c1ec70394311168b354e312da768d2f2530364c63748e0ed431e9f9b3642321c73e34266c3150500cdf5ae8c53a
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
@@ -15,9 +15,14 @@ module BooticClient
15
15
 
16
16
  Enumerator.new do |yielder|
17
17
  loop do
18
- page.each{|item| yielder.yield item }
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BooticClient
4
- VERSION = "0.0.31".freeze
4
+ VERSION = "0.0.32".freeze
5
5
  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.31
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: 2022-03-22 00:00:00.000000000 Z
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.2.32
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