castle-rb 5.0.0 → 7.1.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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +113 -39
  3. data/lib/castle.rb +49 -29
  4. data/lib/castle/api.rb +20 -16
  5. data/lib/castle/api/approve_device.rb +20 -0
  6. data/lib/castle/api/authenticate.rb +37 -0
  7. data/lib/castle/api/end_impersonation.rb +24 -0
  8. data/lib/castle/api/filter.rb +37 -0
  9. data/lib/castle/api/get_device.rb +20 -0
  10. data/lib/castle/api/get_devices_for_user.rb +20 -0
  11. data/lib/castle/api/log.rb +37 -0
  12. data/lib/castle/api/report_device.rb +20 -0
  13. data/lib/castle/api/risk.rb +37 -0
  14. data/lib/castle/api/start_impersonation.rb +24 -0
  15. data/lib/castle/api/track.rb +21 -0
  16. data/lib/castle/client.rb +74 -68
  17. data/lib/castle/{extractors/client_id.rb → client_id/extract.rb} +2 -2
  18. data/lib/castle/commands/approve_device.rb +17 -0
  19. data/lib/castle/commands/authenticate.rb +13 -13
  20. data/lib/castle/commands/end_impersonation.rb +25 -0
  21. data/lib/castle/commands/filter.rb +22 -0
  22. data/lib/castle/commands/get_device.rb +17 -0
  23. data/lib/castle/commands/get_devices_for_user.rb +17 -0
  24. data/lib/castle/commands/log.rb +22 -0
  25. data/lib/castle/commands/report_device.rb +17 -0
  26. data/lib/castle/commands/risk.rb +22 -0
  27. data/lib/castle/commands/start_impersonation.rb +25 -0
  28. data/lib/castle/commands/track.rb +12 -13
  29. data/lib/castle/configuration.rb +31 -23
  30. data/lib/castle/context/{default.rb → get_default.rb} +5 -6
  31. data/lib/castle/context/{merger.rb → merge.rb} +3 -3
  32. data/lib/castle/context/prepare.rb +18 -0
  33. data/lib/castle/context/{sanitizer.rb → sanitize.rb} +1 -1
  34. data/lib/castle/core/get_connection.rb +27 -0
  35. data/lib/castle/{api/response.rb → core/process_response.rb} +8 -3
  36. data/lib/castle/core/process_webhook.rb +25 -0
  37. data/lib/castle/core/send_request.rb +42 -0
  38. data/lib/castle/errors.rb +38 -12
  39. data/lib/castle/failover/prepare_response.rb +28 -0
  40. data/lib/castle/failover/strategy.rb +23 -0
  41. data/lib/castle/{extractors/headers.rb → headers/extract.rb} +8 -6
  42. data/lib/castle/headers/filter.rb +40 -0
  43. data/lib/castle/headers/format.rb +24 -0
  44. data/lib/castle/{extractors/ip.rb → ips/extract.rb} +11 -7
  45. data/lib/castle/logger.rb +19 -0
  46. data/lib/castle/payload/prepare.rb +26 -0
  47. data/lib/castle/secure_mode.rb +7 -2
  48. data/lib/castle/session.rb +18 -0
  49. data/lib/castle/singleton_configuration.rb +9 -0
  50. data/lib/castle/support/hanami.rb +2 -6
  51. data/lib/castle/support/rails.rb +1 -3
  52. data/lib/castle/utils/clean_invalid_chars.rb +22 -0
  53. data/lib/castle/utils/clone.rb +15 -0
  54. data/lib/castle/utils/deep_symbolize_keys.rb +45 -0
  55. data/lib/castle/utils/get_timestamp.rb +15 -0
  56. data/lib/castle/utils/{merger.rb → merge.rb} +3 -3
  57. data/lib/castle/utils/secure_compare.rb +22 -0
  58. data/lib/castle/validators/not_supported.rb +1 -0
  59. data/lib/castle/validators/present.rb +1 -0
  60. data/lib/castle/verdict.rb +15 -0
  61. data/lib/castle/version.rb +1 -1
  62. data/lib/castle/webhooks/verify.rb +45 -0
  63. data/spec/integration/rails/rails_spec.rb +42 -14
  64. data/spec/integration/rails/support/application.rb +3 -1
  65. data/spec/integration/rails/support/home_controller.rb +50 -6
  66. data/spec/lib/castle/api/approve_device_spec.rb +21 -0
  67. data/spec/lib/castle/api/authenticate_spec.rb +136 -0
  68. data/spec/lib/castle/api/end_impersonation_spec.rb +65 -0
  69. data/spec/lib/castle/api/filter_spec.rb +5 -0
  70. data/spec/lib/castle/api/get_device_spec.rb +19 -0
  71. data/spec/lib/castle/api/get_devices_for_user_spec.rb +19 -0
  72. data/spec/lib/castle/api/log_spec.rb +5 -0
  73. data/spec/lib/castle/api/report_device_spec.rb +21 -0
  74. data/spec/lib/castle/api/risk_spec.rb +5 -0
  75. data/spec/lib/castle/api/start_impersonation_spec.rb +65 -0
  76. data/spec/lib/castle/api/track_spec.rb +72 -0
  77. data/spec/lib/castle/api_spec.rb +14 -15
  78. data/spec/lib/castle/{extractors/client_id_spec.rb → client_id/extract_spec.rb} +6 -15
  79. data/spec/lib/castle/client_spec.rb +110 -92
  80. data/spec/lib/castle/commands/approve_device_spec.rb +24 -0
  81. data/spec/lib/castle/commands/authenticate_spec.rb +15 -31
  82. data/spec/lib/castle/commands/end_impersonation_spec.rb +79 -0
  83. data/spec/lib/castle/commands/filter_spec.rb +72 -0
  84. data/spec/lib/castle/commands/get_device_spec.rb +24 -0
  85. data/spec/lib/castle/commands/{review_spec.rb → get_devices_for_user_spec.rb} +7 -7
  86. data/spec/lib/castle/commands/log_spec.rb +73 -0
  87. data/spec/lib/castle/commands/report_device_spec.rb +24 -0
  88. data/spec/lib/castle/commands/risk_spec.rb +73 -0
  89. data/spec/lib/castle/commands/{impersonate_spec.rb → start_impersonation_spec.rb} +13 -41
  90. data/spec/lib/castle/commands/track_spec.rb +14 -34
  91. data/spec/lib/castle/configuration_spec.rb +8 -141
  92. data/spec/lib/castle/context/{default_spec.rb → get_default_spec.rb} +9 -10
  93. data/spec/lib/castle/context/{merger_spec.rb → merge_spec.rb} +1 -1
  94. data/spec/lib/castle/context/prepare_spec.rb +43 -0
  95. data/spec/lib/castle/context/{sanitizer_spec.rb → sanitize_spec.rb} +1 -1
  96. data/spec/lib/castle/core/get_connection_spec.rb +43 -0
  97. data/spec/lib/castle/{api/response_spec.rb → core/process_response_spec.rb} +49 -1
  98. data/spec/lib/castle/core/process_webhook_spec.rb +46 -0
  99. data/spec/lib/castle/{api/request_spec.rb → core/send_request_spec.rb} +16 -37
  100. data/spec/lib/castle/failover/strategy_spec.rb +12 -0
  101. data/spec/lib/castle/{extractors/headers_spec.rb → headers/extract_spec.rb} +7 -9
  102. data/spec/lib/castle/headers/filter_spec.rb +39 -0
  103. data/spec/lib/castle/headers/format_spec.rb +25 -0
  104. data/spec/lib/castle/{extractors/ip_spec.rb → ips/extract_spec.rb} +5 -14
  105. data/spec/lib/castle/logger_spec.rb +38 -0
  106. data/spec/lib/castle/payload/prepare_spec.rb +55 -0
  107. data/spec/lib/castle/session_spec.rb +65 -0
  108. data/spec/lib/castle/singleton_configuration_spec.rb +14 -0
  109. data/spec/lib/castle/utils/clean_invalid_chars_spec.rb +69 -0
  110. data/spec/lib/castle/utils/{cloner_spec.rb → clone_spec.rb} +3 -3
  111. data/spec/lib/castle/utils/deep_symbolize_keys_spec.rb +50 -0
  112. data/spec/lib/castle/utils/{timestamp_spec.rb → get_timestamp_spec.rb} +1 -1
  113. data/spec/lib/castle/utils/merge_spec.rb +15 -0
  114. data/spec/lib/castle/validators/present_spec.rb +5 -6
  115. data/spec/lib/castle/verdict_spec.rb +9 -0
  116. data/spec/lib/castle/webhooks/verify_spec.rb +53 -0
  117. data/spec/lib/castle_spec.rb +4 -10
  118. data/spec/spec_helper.rb +3 -3
  119. data/spec/support/shared_examples/action_request.rb +155 -0
  120. data/spec/support/shared_examples/configuration.rb +101 -0
  121. metadata +144 -67
  122. data/lib/castle/api/connection.rb +0 -24
  123. data/lib/castle/api/request.rb +0 -42
  124. data/lib/castle/api/session.rb +0 -20
  125. data/lib/castle/commands/identify.rb +0 -23
  126. data/lib/castle/commands/impersonate.rb +0 -26
  127. data/lib/castle/commands/review.rb +0 -14
  128. data/lib/castle/events.rb +0 -49
  129. data/lib/castle/failover_auth_response.rb +0 -21
  130. data/lib/castle/headers_filter.rb +0 -35
  131. data/lib/castle/headers_formatter.rb +0 -22
  132. data/lib/castle/review.rb +0 -11
  133. data/lib/castle/utils.rb +0 -55
  134. data/lib/castle/utils/cloner.rb +0 -11
  135. data/lib/castle/utils/timestamp.rb +0 -12
  136. data/spec/lib/castle/api/connection_spec.rb +0 -59
  137. data/spec/lib/castle/api/session_spec.rb +0 -86
  138. data/spec/lib/castle/commands/identify_spec.rb +0 -88
  139. data/spec/lib/castle/events_spec.rb +0 -5
  140. data/spec/lib/castle/headers_filter_spec.rb +0 -38
  141. data/spec/lib/castle/headers_formatter_spec.rb +0 -25
  142. data/spec/lib/castle/review_spec.rb +0 -19
  143. data/spec/lib/castle/utils/merger_spec.rb +0 -13
  144. data/spec/lib/castle/utils_spec.rb +0 -156
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # tmp logger for testing
4
+ class TmpLogger
5
+ # @param _message [String]
6
+ def info(_message); end
7
+ end
8
+
9
+ describe Castle::Logger do
10
+ subject(:log) { described_class.call(message, data) }
11
+
12
+ let(:message) { 'https://localhost/test:' }
13
+ let(:integration_logger) { TmpLogger.new }
14
+ let(:data) { { a: 1 }.to_json }
15
+ let(:logger_message) { "[CASTLE] #{message} #{data}" }
16
+
17
+ before { allow(integration_logger).to receive(:info).and_call_original }
18
+
19
+ describe '.call' do
20
+ context 'without logger' do
21
+ before do
22
+ Castle.config.logger = nil
23
+ log
24
+ end
25
+
26
+ it { expect(integration_logger).not_to have_received(:info) }
27
+ end
28
+
29
+ context 'with logger' do
30
+ before do
31
+ Castle.config.logger = integration_logger
32
+ log
33
+ end
34
+
35
+ it { expect(integration_logger).to have_received(:info).with(logger_message) }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::Payload::Prepare 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
+ )
14
+ end
15
+ let(:request) { Rack::Request.new(env) }
16
+
17
+ let(:headers) do
18
+ { 'Content-Length': '0', 'User-Agent': ua, 'X-Forwarded-For': ip.to_s, 'Cookie': true }
19
+ end
20
+ let(:context) do
21
+ {
22
+ client_id: 'abcd',
23
+ active: true,
24
+ user_agent: ua,
25
+ headers: headers,
26
+ ip: ip,
27
+ library: {
28
+ name: 'castle-rb',
29
+ version: '2.2.0'
30
+ }
31
+ }
32
+ end
33
+
34
+ let(:time_now) { Time.now }
35
+ let(:time_formatted) { time_now.utc.iso8601(3) }
36
+ let(:payload_options) { { user_id: '1234', user_traits: { name: 'Jo' } } }
37
+ let(:result) do
38
+ { user_id: '1234', user_traits: { name: 'Jo' }, timestamp: time_formatted, context: context }
39
+ end
40
+
41
+ before do
42
+ Timecop.freeze(time_now)
43
+ stub_const('Castle::VERSION', '2.2.0')
44
+ end
45
+
46
+ after { Timecop.return }
47
+
48
+ describe '#call' do
49
+ subject(:generated) { described_class.call(payload_options, request) }
50
+
51
+ context 'when active true' do
52
+ it { is_expected.to eql(result) }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::Session do
4
+ describe '.call' do
5
+ context 'when ssl false' do
6
+ let(:localhost) { 'localhost' }
7
+ let(:port) { 3002 }
8
+
9
+ before do
10
+ Castle.config.base_url = 'http://localhost:3002'
11
+ stub_request(:get, 'localhost:3002/test').to_return(status: 200, body: '{}', headers: {})
12
+ end
13
+
14
+ context 'with block' do
15
+ let(:api_url) { '/test' }
16
+ let(:request) { Net::HTTP::Get.new(api_url) }
17
+
18
+ before do
19
+ allow(Net::HTTP).to receive(:new).with(localhost, port).and_call_original
20
+
21
+ described_class.call { |http| http.request(request) }
22
+ end
23
+
24
+ it { expect(Net::HTTP).to have_received(:new).with(localhost, port) }
25
+
26
+ it { expect(a_request(:get, 'localhost:3002/test')).to have_been_made.once }
27
+ end
28
+
29
+ context 'without block' do
30
+ before { described_class.call }
31
+
32
+ it { expect(a_request(:get, 'localhost:3002/test')).not_to have_been_made }
33
+ end
34
+ end
35
+
36
+ context 'when ssl true' do
37
+ let(:localhost) { 'localhost' }
38
+ let(:port) { 443 }
39
+
40
+ before do
41
+ Castle.config.base_url = 'https://localhost'
42
+ stub_request(:get, 'https://localhost/test').to_return(
43
+ status: 200,
44
+ body: '{}',
45
+ headers: {}
46
+ )
47
+ end
48
+
49
+ context 'with block' do
50
+ let(:api_url) { '/test' }
51
+ let(:request) { Net::HTTP::Get.new(api_url) }
52
+
53
+ before do
54
+ allow(Net::HTTP).to receive(:new).with(localhost, port).and_call_original
55
+
56
+ allow(Net::HTTP).to receive(:start)
57
+
58
+ described_class.call { |http| http.request(request) }
59
+ end
60
+
61
+ it { expect(Net::HTTP).to have_received(:new).with(localhost, port) }
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::SingletonConfiguration do
4
+ subject(:config) { described_class.instance }
5
+
6
+ it_behaves_like 'configuration_host'
7
+ it_behaves_like 'configuration_request_timeout'
8
+ it_behaves_like 'configuration_allowlisted'
9
+ it_behaves_like 'configuration_denylisted'
10
+ it_behaves_like 'configuration_failover_strategy'
11
+ it_behaves_like 'configuration_api_secret'
12
+
13
+ it { expect(config.api_secret).to be_eql('secret') }
14
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::Utils::CleanInvalidChars do
4
+ describe '::call' do
5
+ subject { described_class.call(input) }
6
+
7
+ context 'when input is a string' do
8
+ let(:input) { '1234' }
9
+
10
+ it { is_expected.to eq input }
11
+ end
12
+
13
+ context 'when input is an array' do
14
+ let(:input) { [1, 2, 3, '4'] }
15
+
16
+ it { is_expected.to eq input }
17
+ end
18
+
19
+ context 'when input is a hash' do
20
+ let(:input) { { user_id: 1 } }
21
+
22
+ it { is_expected.to eq input }
23
+ end
24
+
25
+ context 'when input is nil' do
26
+ let(:input) { nil }
27
+
28
+ it { is_expected.to eq input }
29
+ end
30
+
31
+ context 'when input is a nested hash' do
32
+ let(:input) { { user: { id: 1 } } }
33
+
34
+ it { is_expected.to eq input }
35
+ end
36
+
37
+ context 'with invalid UTF-8 characters' do
38
+ context 'when input is a hash' do
39
+ let(:input) { { user_id: "inv\xC4lid" } }
40
+
41
+ it { is_expected.to eq(user_id: 'inv�lid') }
42
+ end
43
+
44
+ context 'when input is a nested hash' do
45
+ let(:input) { { user: { id: "inv\xC4lid" } } }
46
+
47
+ it { is_expected.to eq(user: { id: 'inv�lid' }) }
48
+ end
49
+
50
+ context 'when input is an array of hashes' do
51
+ let(:input) { [{ user: "inv\xC4lid" }] * 2 }
52
+
53
+ it { is_expected.to eq([{ user: 'inv�lid' }, { user: 'inv�lid' }]) }
54
+ end
55
+
56
+ context 'when input is an array' do
57
+ let(:input) { ["inv\xC4lid"] * 2 }
58
+
59
+ it { is_expected.to eq(%w[inv�lid inv�lid]) }
60
+ end
61
+
62
+ context 'when input is a hash with array in key' do
63
+ let(:input) { { items: ["inv\xC4lid"] * 2 } }
64
+
65
+ it { is_expected.to eq(items: %w[inv�lid inv�lid]) }
66
+ end
67
+ end
68
+ end
69
+ end
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- describe Castle::Utils::Cloner do
4
- subject(:cloner) { described_class }
3
+ describe Castle::Utils::Clone do
4
+ subject(:clone) { described_class }
5
5
 
6
6
  describe 'call' do
7
7
  let(:nested) { { c: '3' } }
8
8
  let(:first) { { test: { test1: { c: '4' }, test2: nested, a: '1', b: '2' } } }
9
9
  let(:result) { { test: { test1: { c: '4' }, test2: { c: '3' }, a: '1', b: '2' } } }
10
- let(:cloned) { cloner.call(first) }
10
+ let(:cloned) { clone.call(first) }
11
11
 
12
12
  before { cloned }
13
13
 
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::Utils::DeepSymbolizeKeys do
4
+ let(:nested_strings) { { 'a' => { 'b' => { 'c' => 3 } } } }
5
+ let(:nested_symbols) { { a: { b: { c: 3 } } } }
6
+ let(:nested_mixed) { { 'a' => { b: { 'c' => 3 } } } }
7
+ let(:string_array_of_hashes) { { 'a' => [{ 'b' => 2 }, { 'c' => 3 }, 4] } }
8
+ let(:symbol_array_of_hashes) { { a: [{ b: 2 }, { c: 3 }, 4] } }
9
+ let(:mixed_array_of_hashes) { { a: [{ b: 2 }, { 'c' => 3 }, 4] } }
10
+
11
+ describe '::call' do
12
+ subject { described_class.call(hash) }
13
+
14
+ context 'when nested_symbols' do
15
+ let(:hash) { nested_symbols }
16
+
17
+ it { is_expected.to eq(nested_symbols) }
18
+ end
19
+
20
+ context 'when nested_strings' do
21
+ let(:hash) { nested_strings }
22
+
23
+ it { is_expected.to eq(nested_symbols) }
24
+ end
25
+
26
+ context 'when nested_mixed' do
27
+ let(:hash) { nested_mixed }
28
+
29
+ it { is_expected.to eq(nested_symbols) }
30
+ end
31
+
32
+ context 'when string_array_of_hashes' do
33
+ let(:hash) { string_array_of_hashes }
34
+
35
+ it { is_expected.to eq(symbol_array_of_hashes) }
36
+ end
37
+
38
+ context 'when symbol_array_of_hashes' do
39
+ let(:hash) { symbol_array_of_hashes }
40
+
41
+ it { is_expected.to eq(symbol_array_of_hashes) }
42
+ end
43
+
44
+ context 'when mixed_array_of_hashes' do
45
+ let(:hash) { mixed_array_of_hashes }
46
+
47
+ it { is_expected.to eq(symbol_array_of_hashes) }
48
+ end
49
+ end
50
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- describe Castle::Utils::Timestamp do
3
+ describe Castle::Utils::GetTimestamp do
4
4
  subject(:timestamp) { described_class.call }
5
5
 
6
6
  let(:time_string) { '2018-01-10T14:14:24.407Z' }
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::Utils::Merge do
4
+ subject(:merge) { described_class }
5
+
6
+ describe 'call' do
7
+ let(:first) { { test: { test1: { c: '4' }, test2: { c: '3' }, a: '1', b: '2' } } }
8
+ let(:second) do
9
+ { test2: '2', test: { 'test1' => { d: '5' }, :test2 => '6', :a => nil, :b => '3' } }
10
+ end
11
+ let(:result) { { test2: '2', test: { test1: { c: '4', d: '5' }, test2: '6', b: '3' } } }
12
+
13
+ it { expect(merge.call(first, second)).to be_eql(result) }
14
+ end
15
+ end
@@ -8,9 +8,7 @@ describe Castle::Validators::Present do
8
8
  let(:keys) { %i[second third] }
9
9
 
10
10
  it do
11
- expect do
12
- call
13
- end.to raise_error(Castle::InvalidParametersError, 'third is missing or empty')
11
+ expect { call }.to raise_error(Castle::InvalidParametersError, 'third is missing or empty')
14
12
  end
15
13
  end
16
14
 
@@ -18,9 +16,10 @@ describe Castle::Validators::Present do
18
16
  let(:keys) { %i[second invalid] }
19
17
 
20
18
  it do
21
- expect do
22
- call
23
- end.to raise_error(Castle::InvalidParametersError, 'invalid is missing or empty')
19
+ expect { call }.to raise_error(
20
+ Castle::InvalidParametersError,
21
+ 'invalid is missing or empty'
22
+ )
24
23
  end
25
24
  end
26
25
 
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::Verdict do
4
+ subject(:verdict) { described_class }
5
+
6
+ it { expect(verdict::ALLOW).to be_eql('allow') }
7
+ it { expect(verdict::DENY).to be_eql('deny') }
8
+ it { expect(verdict::CHALLENGE).to be_eql('challenge') }
9
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::Webhooks::Verify do
4
+ describe '#call' do
5
+ subject(:call) { described_class.call(webhook) }
6
+
7
+ let(:env) { Rack::MockRequest.env_for('/', 'HTTP_X_CASTLE_SIGNATURE' => signature) }
8
+
9
+ let(:webhook) { Rack::Request.new(env) }
10
+ let(:user_id) { '12345' }
11
+ let(:webhook_body) do
12
+ {
13
+ api_version: 'v1',
14
+ app_id: '12345',
15
+ type: '$incident.confirmed',
16
+ created_at: '2020-12-18T12:55:21.779Z',
17
+ data: {
18
+ id: 'test',
19
+ device_token: 'token',
20
+ user_id: user_id,
21
+ trigger: '$login.succeeded',
22
+ context: {},
23
+ location: {},
24
+ user_agent: {}
25
+ },
26
+ user_traits: {},
27
+ properties: {},
28
+ policy: {}
29
+ }.to_json
30
+ end
31
+
32
+ context 'when success' do
33
+ let(:signature) { '3ptx3rUOBnGEqPjMrbcJn2UUfzwTKP54dFyP5uyPY+Y=' }
34
+
35
+ before { allow(Castle::Core::ProcessWebhook).to receive(:call).and_return(webhook_body) }
36
+
37
+ it { expect { call }.not_to raise_error }
38
+ end
39
+
40
+ context 'when signature is malformed' do
41
+ let(:signature) { '123' }
42
+
43
+ before { allow(Castle::Core::ProcessWebhook).to receive(:call).and_return(webhook_body) }
44
+
45
+ it do
46
+ expect { call }.to raise_error(
47
+ Castle::WebhookVerificationError,
48
+ 'Signature not matching the expected signature'
49
+ )
50
+ end
51
+ end
52
+ end
53
+ end
@@ -42,11 +42,7 @@ describe Castle do
42
42
  end
43
43
 
44
44
  context 'with block and options' do
45
- before do
46
- castle.configure(request_timeout: timeout) do |config|
47
- config.api_secret = value
48
- end
49
- end
45
+ before { castle.configure(request_timeout: timeout) { |config| config.api_secret = value } }
50
46
 
51
47
  it_behaves_like 'config_setup'
52
48
  end
@@ -56,11 +52,9 @@ describe Castle do
56
52
  let(:value) { 'new_secret' }
57
53
 
58
54
  it do
59
- expect do
60
- castle.configure do |config|
61
- config.wrong_config = value
62
- end
63
- end.to raise_error(Castle::ConfigurationError)
55
+ expect { castle.configure { |config| config.wrong_config = value } }.to raise_error(
56
+ Castle::ConfigurationError
57
+ )
64
58
  end
65
59
  end
66
60
  end