bootic_client 0.0.31 → 0.0.32

Sign up to get free protection for your applications and to get access to all the features.
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