locked-rb 0.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.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +127 -0
  3. data/lib/locked-rb.rb +3 -0
  4. data/lib/locked.rb +60 -0
  5. data/lib/locked/api.rb +40 -0
  6. data/lib/locked/api/request.rb +37 -0
  7. data/lib/locked/api/request/build.rb +29 -0
  8. data/lib/locked/api/response.rb +40 -0
  9. data/lib/locked/client.rb +66 -0
  10. data/lib/locked/command.rb +5 -0
  11. data/lib/locked/commands/authenticate.rb +23 -0
  12. data/lib/locked/commands/identify.rb +23 -0
  13. data/lib/locked/commands/review.rb +14 -0
  14. data/lib/locked/configuration.rb +75 -0
  15. data/lib/locked/context/default.rb +40 -0
  16. data/lib/locked/context/merger.rb +14 -0
  17. data/lib/locked/context/sanitizer.rb +23 -0
  18. data/lib/locked/errors.rb +41 -0
  19. data/lib/locked/extractors/client_id.rb +17 -0
  20. data/lib/locked/extractors/headers.rb +24 -0
  21. data/lib/locked/extractors/ip.rb +18 -0
  22. data/lib/locked/failover_auth_response.rb +23 -0
  23. data/lib/locked/header_formatter.rb +9 -0
  24. data/lib/locked/review.rb +11 -0
  25. data/lib/locked/secure_mode.rb +11 -0
  26. data/lib/locked/support/hanami.rb +19 -0
  27. data/lib/locked/support/padrino.rb +19 -0
  28. data/lib/locked/support/rails.rb +13 -0
  29. data/lib/locked/support/sinatra.rb +19 -0
  30. data/lib/locked/utils.rb +55 -0
  31. data/lib/locked/utils/cloner.rb +11 -0
  32. data/lib/locked/utils/merger.rb +23 -0
  33. data/lib/locked/utils/timestamp.rb +12 -0
  34. data/lib/locked/validators/not_supported.rb +16 -0
  35. data/lib/locked/validators/present.rb +16 -0
  36. data/lib/locked/version.rb +5 -0
  37. data/spec/lib/Locked/api/request/build_spec.rb +42 -0
  38. data/spec/lib/Locked/api/request_spec.rb +59 -0
  39. data/spec/lib/Locked/api/response_spec.rb +58 -0
  40. data/spec/lib/Locked/api_spec.rb +37 -0
  41. data/spec/lib/Locked/client_spec.rb +226 -0
  42. data/spec/lib/Locked/command_spec.rb +9 -0
  43. data/spec/lib/Locked/commands/authenticate_spec.rb +95 -0
  44. data/spec/lib/Locked/commands/identify_spec.rb +87 -0
  45. data/spec/lib/Locked/commands/review_spec.rb +24 -0
  46. data/spec/lib/Locked/configuration_spec.rb +146 -0
  47. data/spec/lib/Locked/context/default_spec.rb +35 -0
  48. data/spec/lib/Locked/context/merger_spec.rb +23 -0
  49. data/spec/lib/Locked/context/sanitizer_spec.rb +27 -0
  50. data/spec/lib/Locked/extractors/client_id_spec.rb +62 -0
  51. data/spec/lib/Locked/extractors/headers_spec.rb +26 -0
  52. data/spec/lib/Locked/extractors/ip_spec.rb +27 -0
  53. data/spec/lib/Locked/header_formatter_spec.rb +25 -0
  54. data/spec/lib/Locked/review_spec.rb +19 -0
  55. data/spec/lib/Locked/secure_mode_spec.rb +9 -0
  56. data/spec/lib/Locked/utils/cloner_spec.rb +18 -0
  57. data/spec/lib/Locked/utils/merger_spec.rb +13 -0
  58. data/spec/lib/Locked/utils/timestamp_spec.rb +17 -0
  59. data/spec/lib/Locked/utils_spec.rb +156 -0
  60. data/spec/lib/Locked/validators/not_supported_spec.rb +26 -0
  61. data/spec/lib/Locked/validators/present_spec.rb +33 -0
  62. data/spec/lib/Locked/version_spec.rb +5 -0
  63. data/spec/lib/locked_spec.rb +66 -0
  64. data/spec/spec_helper.rb +22 -0
  65. metadata +133 -0
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Locked
4
+ module Utils
5
+ class << self
6
+ # Returns a new hash with all keys converted to symbols, as long as
7
+ # they respond to +to_sym+. This includes the keys from the root hash
8
+ # and from all nested hashes and arrays.
9
+ #
10
+ # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
11
+ #
12
+ # Locked::Hash.deep_symbolize_keys(hash)
13
+ # # => {:person=>{:name=>"Rob", :age=>"28"}}
14
+ def deep_symbolize_keys(object, &block)
15
+ case object
16
+ when Hash
17
+ object.each_with_object({}) do |(key, value), result|
18
+ result[key.to_sym] = deep_symbolize_keys(value, &block)
19
+ end
20
+ when Array
21
+ object.map { |e| deep_symbolize_keys(e, &block) }
22
+ else
23
+ object
24
+ end
25
+ end
26
+
27
+ def deep_symbolize_keys!(object, &block)
28
+ case object
29
+ when Hash
30
+ object.keys.each do |key|
31
+ value = object.delete(key)
32
+ object[key.to_sym] = deep_symbolize_keys!(value, &block)
33
+ end
34
+ object
35
+ when Array
36
+ object.map! { |e| deep_symbolize_keys!(e, &block) }
37
+ else
38
+ object
39
+ end
40
+ end
41
+
42
+ def replace_invalid_characters(arg)
43
+ if arg.is_a?(::String)
44
+ arg.encode('UTF-8', invalid: :replace, undef: :replace)
45
+ elsif arg.is_a?(::Hash)
46
+ arg.each_with_object({}) { |(k, v), h| h[k] = replace_invalid_characters(v) }
47
+ elsif arg.is_a?(::Array)
48
+ arg.map(&method(:replace_invalid_characters))
49
+ else
50
+ arg
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Locked
4
+ module Utils
5
+ class Cloner
6
+ def self.call(object)
7
+ Marshal.load(Marshal.dump(object))
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Locked
4
+ module Utils
5
+ class Merger
6
+ def self.call(base, extra)
7
+ base_s = Locked::Utils.deep_symbolize_keys(base)
8
+ extra_s = Locked::Utils.deep_symbolize_keys(extra)
9
+
10
+ extra_s.each do |name, value|
11
+ if value.nil?
12
+ base_s.delete(name)
13
+ elsif value.is_a?(Hash) && base_s[name].is_a?(Hash)
14
+ base_s[name] = call(base_s[name], value)
15
+ else
16
+ base_s[name] = value
17
+ end
18
+ end
19
+ base_s
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Locked
4
+ module Utils
5
+ # generates proper timestamp
6
+ class Timestamp
7
+ def self.call
8
+ Time.now.utc.iso8601(3)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Locked
4
+ module Validators
5
+ class NotSupported
6
+ class << self
7
+ def call(options, keys)
8
+ keys.each do |key|
9
+ next unless options.key?(key)
10
+ raise Locked::InvalidParametersError, "#{key} is/are not supported"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Locked
4
+ module Validators
5
+ class Present
6
+ class << self
7
+ def call(options, keys)
8
+ keys.each do |key|
9
+ next unless options[key].to_s.empty?
10
+ raise Locked::InvalidParametersError, "#{key} is missing or empty"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Locked
4
+ VERSION = '0.0.1'
5
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Locked::API::Request::Build do
4
+ subject(:call) { described_class.call(command, headers, api_key) }
5
+
6
+ let(:headers) { { 'SAMPLE-HEADER' => '1' } }
7
+ let(:api_key) { 'key' }
8
+
9
+ describe 'call' do
10
+ # context 'when get' do
11
+ # let(:command) { Locked::Commands::Review.build(review_id) }
12
+ # let(:review_id) { SecureRandom.uuid }
13
+ #
14
+ # it { expect(call.body).to be_nil }
15
+ # it { expect(call.method).to eql('GET') }
16
+ # it { expect(call.path).to eql("/api/v1/client#{command.path}") }
17
+ # it { expect(call.to_hash).to have_key('authorization') }
18
+ # it { expect(call.to_hash).to have_key('sample-header') }
19
+ # it { expect(call.to_hash['sample-header']).to eql(['1']) }
20
+ # end
21
+
22
+ context 'when post' do
23
+ let(:time) { Time.now.utc.iso8601(3) }
24
+ let(:command) { Locked::Commands::Authenticate.new({}).build(event: '$login.success', name: "test") }
25
+ let(:expected_body) do
26
+ {
27
+ event: '$login.success',
28
+ name: "test",
29
+ # context: {},
30
+ }
31
+ end
32
+
33
+ before { allow(Locked::Utils::Timestamp).to receive(:call).and_return(time) }
34
+
35
+ it { expect(call.body).to be_eql(expected_body.to_json) }
36
+ it { expect(call.method).to eql('POST') }
37
+ it { expect(call.path).to eql("/api/v1/client/#{command.path}") }
38
+ it { expect(call.to_hash).to have_key('sample-header') }
39
+ it { expect(call.to_hash['sample-header']).to eql(['1']) }
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Locked::API::Request do
4
+ describe '#call' do
5
+ subject(:call) { described_class.call(command, api_key, headers) }
6
+
7
+ let(:http) { instance_double('Net::HTTP') }
8
+ let(:request_build) { instance_double('Locked::API::Request::Build') }
9
+ let(:command) { Locked::Commands::Authenticate.new({}).build(event: '$login.success') }
10
+ let(:headers) { {} }
11
+ let(:api_key) { 'key' }
12
+ let(:expected_headers) { { 'Content-Type' => 'application/json' } }
13
+
14
+ before do
15
+ allow(described_class).to receive(:http).and_return(http)
16
+ allow(http).to receive(:request).with(request_build)
17
+ allow(Locked::API::Request::Build).to receive(:call)
18
+ .with(command, expected_headers, api_key)
19
+ .and_return(request_build)
20
+ call
21
+ end
22
+
23
+ it do
24
+ expect(Locked::API::Request::Build).to have_received(:call)
25
+ .with(command, expected_headers, api_key)
26
+ end
27
+ it { expect(http).to have_received(:request).with(request_build) }
28
+ end
29
+
30
+ describe '#http' do
31
+ subject(:http) { described_class.http }
32
+
33
+ context 'when ssl false' do
34
+ before do
35
+ Locked.config.host = 'localhost'
36
+ Locked.config.port = 3002
37
+ end
38
+
39
+ after do
40
+ Locked.config.host = Locked::Configuration::HOST
41
+ Locked.config.port = Locked::Configuration::PORT
42
+ end
43
+
44
+ it { expect(http).to be_instance_of(Net::HTTP) }
45
+ it { expect(http.address).to eq(Locked.config.host) }
46
+ it { expect(http.port).to eq(Locked.config.port) }
47
+ it { expect(http.use_ssl?).to be false }
48
+ it { expect(http.verify_mode).to be_nil }
49
+ end
50
+
51
+ context 'when ssl true' do
52
+ it { expect(http).to be_instance_of(Net::HTTP) }
53
+ it { expect(http.address).to eq(Locked.config.host) }
54
+ it { expect(http.port).to eq(Locked.config.port) }
55
+ it { expect(http.use_ssl?).to be true }
56
+ it { expect(http.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER) }
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Locked::API::Response do
4
+ describe '#call' do
5
+ subject(:call) { described_class.call(response) }
6
+
7
+ context 'when success' do
8
+ let(:response) { OpenStruct.new(body: '{"user":1}', code: 200) }
9
+
10
+ it { expect(call).to eql(user: 1) }
11
+ end
12
+
13
+ context 'when response empty' do
14
+ let(:response) { OpenStruct.new(body: '', code: 200) }
15
+
16
+ it { expect(call).to eql({}) }
17
+ end
18
+
19
+ context 'when response nil' do
20
+ let(:response) { OpenStruct.new(code: 200) }
21
+
22
+ it { expect(call).to eql({}) }
23
+ end
24
+
25
+ context 'when json is malformed' do
26
+ let(:response) { OpenStruct.new(body: '{a', code: 200) }
27
+
28
+ it { expect { call }.to raise_error(Locked::ApiError) }
29
+ end
30
+ end
31
+
32
+ describe '#verify!' do
33
+ subject(:verify!) { described_class.verify!(response) }
34
+
35
+ context 'without error when response is 2xx' do
36
+ let(:response) { OpenStruct.new(code: 200) }
37
+
38
+ it { expect { verify! }.not_to raise_error }
39
+ end
40
+
41
+ shared_examples 'response_failed' do |code, error|
42
+ let(:response) { OpenStruct.new(code: code) }
43
+
44
+ it "fail when response is #{code}" do
45
+ expect { verify! }.to raise_error(error)
46
+ end
47
+ end
48
+
49
+ it_behaves_like 'response_failed', '400', Locked::BadRequestError
50
+ it_behaves_like 'response_failed', '401', Locked::UnauthorizedError
51
+ it_behaves_like 'response_failed', '403', Locked::ForbiddenError
52
+ it_behaves_like 'response_failed', '404', Locked::NotFoundError
53
+ it_behaves_like 'response_failed', '419', Locked::UserUnauthorizedError
54
+ it_behaves_like 'response_failed', '422', Locked::InvalidParametersError
55
+ it_behaves_like 'response_failed', '499', Locked::ApiError
56
+ it_behaves_like 'response_failed', '500', Locked::InternalServerError
57
+ end
58
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Locked::API do
4
+ subject(:request) { described_class.request(command) }
5
+
6
+ let(:command) { Locked::Commands::Authenticate.new({}).build(event: '$login.success') }
7
+
8
+ context 'when request timeouts' do
9
+ before { stub_request(:any, /locked.jp/).to_timeout }
10
+
11
+ it do
12
+ expect do
13
+ request
14
+ end.to raise_error(Locked::RequestError)
15
+ end
16
+ end
17
+
18
+ context 'when non-OK response code' do
19
+ before { stub_request(:any, /locked.jp/).to_return(status: 400) }
20
+
21
+ it do
22
+ expect do
23
+ request
24
+ end.to raise_error(Locked::BadRequestError)
25
+ end
26
+ end
27
+
28
+ context 'when no api_key' do
29
+ before { allow(Locked.config).to receive(:api_key).and_return('') }
30
+
31
+ it do
32
+ expect do
33
+ request
34
+ end.to raise_error(Locked::ConfigurationError)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Locked::Client do
4
+ let(:ip) { '1.2.3.4' }
5
+ let(:cookie_id) { 'abcd' }
6
+ let(:ua) { 'Chrome' }
7
+ let(:env) do
8
+ Rack::MockRequest.env_for(
9
+ '/',
10
+ 'HTTP_USER_AGENT' => ua,
11
+ 'HTTP_X_FORWARDED_FOR' => ip,
12
+ 'HTTP_COOKIE' => "__cid=#{cookie_id};other=efgh",
13
+ 'X-LOCKED-API-KEY' => 'key'
14
+ )
15
+ end
16
+ let(:request) { Rack::Request.new(env) }
17
+ let(:client) { described_class.from_request(request) }
18
+ let(:request_to_context) { described_class.to_context(request) }
19
+ let(:client_with_user_timestamp) do
20
+ described_class.new(request_to_context, timestamp: time_user)
21
+ end
22
+ let(:client_with_no_timestamp) { described_class.new(request_to_context) }
23
+
24
+ let(:headers) { { 'X-Forwarded-For' => ip.to_s, 'User-Agent' => ua } }
25
+ let(:context) do
26
+ {
27
+ client_id: 'abcd',
28
+ active: true,
29
+ origin: 'web',
30
+ user_agent: ua,
31
+ headers: { 'X-Forwarded-For': ip.to_s, 'User-Agent': ua },
32
+ ip: ip,
33
+ library: { name: 'locked-rb', version: '0.0.1' }
34
+ }
35
+ end
36
+
37
+ let(:time_now) { Time.now }
38
+ let(:time_auto) { time_now.utc.iso8601(3) }
39
+ let(:time_user) { (Time.now - 10_000).utc.iso8601(3) }
40
+ let(:response_body) { {}.to_json }
41
+
42
+ before do
43
+ Timecop.freeze(time_now)
44
+ stub_const('Locked::VERSION', '0.0.1')
45
+ stub_request(:any, /locked.jp/).to_return(status: 200, body: response_body, headers: {})
46
+ end
47
+
48
+ after { Timecop.return }
49
+
50
+ describe 'parses the request' do
51
+ before do
52
+ allow(Locked::API).to receive(:request).and_call_original
53
+ end
54
+
55
+ it do
56
+ client.authenticate(event: '$login.success', user_id: '1234')
57
+ expect(Locked::API).to have_received(:request)
58
+ end
59
+ end
60
+
61
+ describe 'to_context' do
62
+ it do
63
+ expect(described_class.to_context(request)).to eql(context)
64
+ end
65
+ end
66
+
67
+ describe 'to_options' do
68
+ let(:options) { { user_id: '1234', user_traits: { name: 'Jo' } } }
69
+ let(:result) { { user_id: '1234', user_traits: { name: 'Jo' }, timestamp: time_auto } }
70
+
71
+ it do
72
+ expect(described_class.to_options(options)).to eql(result)
73
+ end
74
+ end
75
+
76
+ # describe 'identify' do
77
+ # let(:request_body) do
78
+ # { user_id: '1234', timestamp: time_auto,
79
+ # sent_at: time_auto, context: context, user_traits: { name: 'Jo' } }
80
+ # end
81
+ #
82
+ # before { client.identify(options) }
83
+ #
84
+ # context 'when used with symbol keys' do
85
+ # let(:options) { { user_id: '1234', user_traits: { name: 'Jo' } } }
86
+ #
87
+ # it do
88
+ # assert_requested :post, 'https://locked.jp/api/v1/client/identify', times: 1 do |req|
89
+ # JSON.parse(req.body) == JSON.parse(request_body.to_json)
90
+ # end
91
+ # end
92
+ #
93
+ # context 'when passed timestamp in options and no defined timestamp' do
94
+ # let(:client) { client_with_no_timestamp }
95
+ # let(:options) { { user_id: '1234', user_traits: { name: 'Jo' }, timestamp: time_user } }
96
+ # let(:request_body) do
97
+ # { user_id: '1234', user_traits: { name: 'Jo' }, context: context,
98
+ # timestamp: time_user, sent_at: time_auto }
99
+ # end
100
+ #
101
+ # it do
102
+ # assert_requested :post, 'https://locked.jp/api/v1/client/identify', times: 1 do |req|
103
+ # JSON.parse(req.body) == JSON.parse(request_body.to_json)
104
+ # end
105
+ # end
106
+ # end
107
+ #
108
+ # context 'with client initialized with timestamp' do
109
+ # let(:client) { client_with_user_timestamp }
110
+ # let(:request_body) do
111
+ # { user_id: '1234', timestamp: time_user, sent_at: time_auto,
112
+ # context: context, user_traits: { name: 'Jo' } }
113
+ # end
114
+ #
115
+ # it do
116
+ # assert_requested :post, 'https://locked.jp/api/v1/client/identify', times: 1 do |req|
117
+ # JSON.parse(req.body) == JSON.parse(request_body.to_json)
118
+ # end
119
+ # end
120
+ # end
121
+ # end
122
+ #
123
+ # context 'when used with string keys' do
124
+ # let(:options) { { 'user_id' => '1234', 'user_traits' => { 'name' => 'Jo' } } }
125
+ #
126
+ # it do
127
+ # assert_requested :post, 'https://locked.jp/api/v1/client/identify', times: 1 do |req|
128
+ # JSON.parse(req.body) == JSON.parse(request_body.to_json)
129
+ # end
130
+ # end
131
+ # end
132
+ # end
133
+
134
+ describe 'authenticate' do
135
+ let(:options) { { event: '$login.success', user_id: '1234' } }
136
+ let(:request_response) { client.authenticate(options) }
137
+ let(:request_body) do
138
+ { event: '$login.success', user_id: '1234', timestamp: time_auto }
139
+ end
140
+
141
+ context 'when used with symbol keys' do
142
+ before { request_response }
143
+
144
+ it do
145
+ assert_requested :post, 'https://locked.jp/api/v1/client/authenticate', times: 1 do |req|
146
+ JSON.parse(req.body) == JSON.parse(request_body.to_json)
147
+ end
148
+ end
149
+
150
+ context 'when passed timestamp in options and no defined timestamp' do
151
+ let(:client) { client_with_no_timestamp }
152
+ let(:options) { { event: '$login.success', user_id: '1234', timestamp: time_user } }
153
+ let(:request_body) do
154
+ { event: '$login.success', user_id: '1234', timestamp: time_user }
155
+ end
156
+
157
+ it do
158
+ assert_requested :post, 'https://locked.jp/api/v1/client/authenticate', times: 1 do |req|
159
+ JSON.parse(req.body) == JSON.parse(request_body.to_json)
160
+ end
161
+ end
162
+ end
163
+
164
+ context 'with client initialized with timestamp' do
165
+ let(:client) { client_with_user_timestamp }
166
+ let(:request_body) do
167
+ { event: '$login.success', user_id: '1234', timestamp: time_user }
168
+ end
169
+
170
+ it do
171
+ assert_requested :post, 'https://locked.jp/api/v1/client/authenticate', times: 1 do |req|
172
+ JSON.parse(req.body) == JSON.parse(request_body.to_json)
173
+ end
174
+ end
175
+ end
176
+ end
177
+
178
+ context 'when used with string keys' do
179
+ let(:options) { { 'event' => '$login.success', 'user_id' => '1234' } }
180
+
181
+ before { request_response }
182
+
183
+ it do
184
+ assert_requested :post, 'https://locked.jp/api/v1/client/authenticate', times: 1 do |req|
185
+ JSON.parse(req.body) == JSON.parse(request_body.to_json)
186
+ end
187
+ end
188
+ end
189
+
190
+ context 'when request with fail' do
191
+ before { allow(Locked::API).to receive(:request).and_raise(Locked::RequestError.new(Timeout::Error)) }
192
+
193
+ context 'with request error and throw strategy' do
194
+ before { allow(Locked.config).to receive(:failover_strategy).and_return(:throw) }
195
+
196
+ it { expect { request_response }.to raise_error(Locked::RequestError) }
197
+ end
198
+
199
+ context 'with request error and not throw on eg deny strategy' do
200
+ it { assert_not_requested :post, 'https://locked.jp/api/v1/client/authenticate' }
201
+ it { expect(request_response[:data][:action]).to be_eql('deny') }
202
+ it { expect(request_response[:data][:user_id]).to be_eql('1234') }
203
+ it { expect(request_response[:failover]).to be true }
204
+ it { expect(request_response[:failover_reason]).to be_eql('Locked::RequestError') }
205
+ end
206
+ end
207
+
208
+ context 'when request is internal server error' do
209
+ before { allow(Locked::API).to receive(:request).and_raise(Locked::InternalServerError) }
210
+
211
+ describe 'throw strategy' do
212
+ before { allow(Locked.config).to receive(:failover_strategy).and_return(:throw) }
213
+
214
+ it { expect { request_response }.to raise_error(Locked::InternalServerError) }
215
+ end
216
+
217
+ context 'not throw on eg deny strategy' do
218
+ it { assert_not_requested :post, 'https://locked.jp/api/v1/client/authenticate' }
219
+ it { expect(request_response[:data][:action]).to be_eql('deny') }
220
+ it { expect(request_response[:data][:user_id]).to be_eql('1234') }
221
+ it { expect(request_response[:failover]).to be true }
222
+ it { expect(request_response[:failover_reason]).to be_eql('Locked::InternalServerError') }
223
+ end
224
+ end
225
+ end
226
+ end