locked-rb 0.0.1

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