castle-rb 4.1.0 → 6.0.0

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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +158 -43
  3. data/lib/castle.rb +46 -21
  4. data/lib/castle/api.rb +24 -12
  5. data/lib/castle/api/approve_device.rb +25 -0
  6. data/lib/castle/api/authenticate.rb +34 -0
  7. data/lib/castle/api/end_impersonation.rb +29 -0
  8. data/lib/castle/api/get_device.rb +25 -0
  9. data/lib/castle/api/get_devices_for_user.rb +25 -0
  10. data/lib/castle/api/identify.rb +26 -0
  11. data/lib/castle/api/report_device.rb +25 -0
  12. data/lib/castle/api/review.rb +24 -0
  13. data/lib/castle/api/start_impersonation.rb +29 -0
  14. data/lib/castle/api/track.rb +26 -0
  15. data/lib/castle/client.rb +52 -45
  16. data/lib/castle/{extractors/client_id.rb → client_id/extract.rb} +2 -2
  17. data/lib/castle/commands/approve_device.rb +21 -0
  18. data/lib/castle/commands/authenticate.rb +13 -13
  19. data/lib/castle/commands/end_impersonation.rb +25 -0
  20. data/lib/castle/commands/get_device.rb +21 -0
  21. data/lib/castle/commands/get_devices_for_user.rb +21 -0
  22. data/lib/castle/commands/identify.rb +12 -13
  23. data/lib/castle/commands/report_device.rb +21 -0
  24. data/lib/castle/commands/review.rb +6 -3
  25. data/lib/castle/commands/start_impersonation.rb +25 -0
  26. data/lib/castle/commands/track.rb +12 -13
  27. data/lib/castle/configuration.rb +45 -28
  28. data/lib/castle/context/{default.rb → get_default.rb} +5 -6
  29. data/lib/castle/context/{merger.rb → merge.rb} +3 -3
  30. data/lib/castle/context/prepare.rb +18 -0
  31. data/lib/castle/context/{sanitizer.rb → sanitize.rb} +1 -1
  32. data/lib/castle/core/get_connection.rb +25 -0
  33. data/lib/castle/{api/response.rb → core/process_response.rb} +4 -2
  34. data/lib/castle/core/process_webhook.rb +20 -0
  35. data/lib/castle/core/send_request.rb +50 -0
  36. data/lib/castle/errors.rb +2 -0
  37. data/lib/castle/events.rb +1 -1
  38. data/lib/castle/failover/prepare_response.rb +23 -0
  39. data/lib/castle/failover/strategy.rb +20 -0
  40. data/lib/castle/headers/extract.rb +47 -0
  41. data/lib/castle/headers/filter.rb +37 -0
  42. data/lib/castle/headers/format.rb +24 -0
  43. data/lib/castle/ip/extract.rb +83 -0
  44. data/lib/castle/logger.rb +19 -0
  45. data/lib/castle/payload/prepare.rb +27 -0
  46. data/lib/castle/secure_mode.rb +6 -2
  47. data/lib/castle/session.rb +18 -0
  48. data/lib/castle/singleton_configuration.rb +9 -0
  49. data/lib/castle/utils/clean_invalid_chars.rb +24 -0
  50. data/lib/castle/utils/clone.rb +15 -0
  51. data/lib/castle/utils/deep_symbolize_keys.rb +45 -0
  52. data/lib/castle/utils/get_timestamp.rb +15 -0
  53. data/lib/castle/utils/{merger.rb → merge.rb} +3 -3
  54. data/lib/castle/utils/secure_compare.rb +22 -0
  55. data/lib/castle/validators/not_supported.rb +1 -0
  56. data/lib/castle/validators/present.rb +1 -0
  57. data/lib/castle/verdict.rb +13 -0
  58. data/lib/castle/version.rb +1 -1
  59. data/lib/castle/webhooks/verify.rb +43 -0
  60. data/spec/integration/rails/rails_spec.rb +33 -7
  61. data/spec/integration/rails/support/application.rb +3 -1
  62. data/spec/integration/rails/support/home_controller.rb +47 -5
  63. data/spec/lib/castle/api/approve_device_spec.rb +21 -0
  64. data/spec/lib/castle/api/authenticate_spec.rb +140 -0
  65. data/spec/lib/castle/api/end_impersonation_spec.rb +59 -0
  66. data/spec/lib/castle/api/get_device_spec.rb +19 -0
  67. data/spec/lib/castle/api/get_devices_for_user_spec.rb +19 -0
  68. data/spec/lib/castle/api/identify_spec.rb +68 -0
  69. data/spec/lib/castle/api/report_device_spec.rb +21 -0
  70. data/spec/lib/castle/{review_spec.rb → api/review_spec.rb} +3 -3
  71. data/spec/lib/castle/api/start_impersonation_spec.rb +59 -0
  72. data/spec/lib/castle/api/track_spec.rb +68 -0
  73. data/spec/lib/castle/api_spec.rb +16 -1
  74. data/spec/lib/castle/{extractors/client_id_spec.rb → client_id/extract_spec.rb} +2 -2
  75. data/spec/lib/castle/client_spec.rb +41 -23
  76. data/spec/lib/castle/commands/approve_device_spec.rb +24 -0
  77. data/spec/lib/castle/commands/authenticate_spec.rb +7 -16
  78. data/spec/lib/castle/commands/end_impersonation_spec.rb +82 -0
  79. data/spec/lib/castle/commands/get_device_spec.rb +24 -0
  80. data/spec/lib/castle/commands/get_devices_for_user_spec.rb +24 -0
  81. data/spec/lib/castle/commands/identify_spec.rb +5 -16
  82. data/spec/lib/castle/commands/report_device_spec.rb +24 -0
  83. data/spec/lib/castle/commands/review_spec.rb +1 -1
  84. data/spec/lib/castle/commands/{impersonate_spec.rb → start_impersonation_spec.rb} +9 -34
  85. data/spec/lib/castle/commands/track_spec.rb +5 -16
  86. data/spec/lib/castle/configuration_spec.rb +9 -138
  87. data/spec/lib/castle/context/{default_spec.rb → get_default_spec.rb} +1 -2
  88. data/spec/lib/castle/context/{merger_spec.rb → merge_spec.rb} +1 -1
  89. data/spec/lib/castle/context/prepare_spec.rb +44 -0
  90. data/spec/lib/castle/context/{sanitizer_spec.rb → sanitize_spec.rb} +1 -1
  91. data/spec/lib/castle/core/get_connection_spec.rb +59 -0
  92. data/spec/lib/castle/{api/response_spec.rb → core/process_response_spec.rb} +56 -1
  93. data/spec/lib/castle/core/process_webhook_spec.rb +46 -0
  94. data/spec/lib/castle/core/send_request_spec.rb +102 -0
  95. data/spec/lib/castle/failover/strategy_spec.rb +12 -0
  96. data/spec/lib/castle/{extractors/headers_spec.rb → headers/extract_spec.rb} +18 -18
  97. data/spec/lib/castle/{headers_filter_spec.rb → headers/filter_spec.rb} +6 -5
  98. data/spec/lib/castle/headers/format_spec.rb +25 -0
  99. data/spec/lib/castle/{extractors/ip_spec.rb → ip/extract_spec.rb} +35 -7
  100. data/spec/lib/castle/logger_spec.rb +42 -0
  101. data/spec/lib/castle/payload/prepare_spec.rb +54 -0
  102. data/spec/lib/castle/session_spec.rb +88 -0
  103. data/spec/lib/castle/singleton_configuration_spec.rb +18 -0
  104. data/spec/lib/castle/utils/clean_invalid_chars_spec.rb +69 -0
  105. data/spec/lib/castle/utils/{cloner_spec.rb → clone_spec.rb} +3 -3
  106. data/spec/lib/castle/utils/deep_symbolize_keys_spec.rb +50 -0
  107. data/spec/lib/castle/utils/{timestamp_spec.rb → get_timestamp_spec.rb} +1 -1
  108. data/spec/lib/castle/utils/{merger_spec.rb → merge_spec.rb} +3 -3
  109. data/spec/lib/castle/verdict_spec.rb +9 -0
  110. data/spec/lib/castle/webhooks/verify_spec.rb +69 -0
  111. data/spec/spec_helper.rb +2 -0
  112. data/spec/support/shared_examples/configuration.rb +129 -0
  113. metadata +133 -56
  114. data/lib/castle/api/request.rb +0 -42
  115. data/lib/castle/api/session.rb +0 -39
  116. data/lib/castle/commands/impersonate.rb +0 -26
  117. data/lib/castle/extractors/headers.rb +0 -45
  118. data/lib/castle/extractors/ip.rb +0 -68
  119. data/lib/castle/failover_auth_response.rb +0 -21
  120. data/lib/castle/headers_filter.rb +0 -35
  121. data/lib/castle/headers_formatter.rb +0 -22
  122. data/lib/castle/review.rb +0 -11
  123. data/lib/castle/utils.rb +0 -55
  124. data/lib/castle/utils/cloner.rb +0 -11
  125. data/lib/castle/utils/timestamp.rb +0 -12
  126. data/spec/lib/castle/api/request_spec.rb +0 -72
  127. data/spec/lib/castle/headers_formatter_spec.rb +0 -25
  128. data/spec/lib/castle/utils_spec.rb +0 -156
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::API::EndImpersonation do
4
+ subject(:call) { described_class.call(options) }
5
+
6
+ let(:ip) { '1.2.3.4' }
7
+ let(:cookie_id) { 'abcd' }
8
+ let(:ua) { 'Chrome' }
9
+ let(:env) do
10
+ Rack::MockRequest.env_for(
11
+ '/',
12
+ 'HTTP_USER_AGENT' => ua,
13
+ 'HTTP_X_FORWARDED_FOR' => ip,
14
+ 'HTTP_COOKIE' => "__cid=#{cookie_id};other=efgh"
15
+ )
16
+ end
17
+ let(:request) { Rack::Request.new(env) }
18
+ let(:context) { Castle::Context::Prepare.call(request) }
19
+ let(:time_now) { Time.now }
20
+ let(:time_auto) { time_now.utc.iso8601(3) }
21
+
22
+ before do
23
+ Timecop.freeze(time_now)
24
+ stub_const('Castle::VERSION', '2.2.0')
25
+ stub_request(:any, /api.castle.io/).with(
26
+ basic_auth: ['', 'secret']
27
+ ).to_return(status: 200, body: response_body, headers: {})
28
+ end
29
+
30
+ after { Timecop.return }
31
+
32
+ describe 'call' do
33
+ let(:impersonator) { 'test@castle.io' }
34
+ let(:request_body) do
35
+ { user_id: '1234', sent_at: time_auto,
36
+ properties: { impersonator: impersonator }, context: context }
37
+ end
38
+ let(:response_body) { { success: true }.to_json }
39
+ let(:options) do
40
+ { user_id: '1234', properties: { impersonator: impersonator }, context: context }
41
+ end
42
+
43
+ context 'when used with symbol keys' do
44
+ before { call }
45
+
46
+ it do
47
+ assert_requested :delete, 'https://api.castle.io/v1/impersonate', times: 1 do |req|
48
+ JSON.parse(req.body) == JSON.parse(request_body.to_json)
49
+ end
50
+ end
51
+ end
52
+
53
+ context 'when request is not successful' do
54
+ let(:response_body) { {}.to_json }
55
+
56
+ it { expect { call }.to raise_error(Castle::ImpersonationFailed) }
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::API::GetDevice do
4
+ before do
5
+ stub_request(:any, /api.castle.io/).with(
6
+ basic_auth: ['', 'secret']
7
+ ).to_return(status: 200, body: '{}', headers: {})
8
+ end
9
+
10
+ describe '.call' do
11
+ subject(:retrieve) { described_class.call(device_token: device_token) }
12
+
13
+ let(:device_token) { '1234' }
14
+
15
+ before { retrieve }
16
+
17
+ it { assert_requested :get, "https://api.castle.io/v1/devices/#{device_token}", times: 1 }
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::API::GetDevicesForUser do
4
+ before do
5
+ stub_request(:any, /api.castle.io/).with(
6
+ basic_auth: ['', 'secret']
7
+ ).to_return(status: 200, body: '{}', headers: {})
8
+ end
9
+
10
+ describe '.call' do
11
+ subject(:retrieve) { described_class.call(user_id: user_id) }
12
+
13
+ let(:user_id) { '1234' }
14
+
15
+ before { retrieve }
16
+
17
+ it { assert_requested :get, "https://api.castle.io/v1/users/#{user_id}/devices", times: 1 }
18
+ end
19
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::API::Identify do
4
+ subject(:call) { described_class.call(options) }
5
+
6
+ let(:ip) { '1.2.3.4' }
7
+ let(:cookie_id) { 'abcd' }
8
+ let(:ua) { 'Chrome' }
9
+ let(:env) do
10
+ Rack::MockRequest.env_for(
11
+ '/',
12
+ 'HTTP_USER_AGENT' => ua,
13
+ 'HTTP_X_FORWARDED_FOR' => ip,
14
+ 'HTTP_COOKIE' => "__cid=#{cookie_id};other=efgh"
15
+ )
16
+ end
17
+ let(:request) { Rack::Request.new(env) }
18
+ let(:context) { Castle::Context::Prepare.call(request) }
19
+ let(:time_now) { Time.now }
20
+ let(:time_auto) { time_now.utc.iso8601(3) }
21
+ let(:time_user) { (Time.now - 10_000).utc.iso8601(3) }
22
+ let(:response_body) { {}.to_json }
23
+
24
+ before do
25
+ Timecop.freeze(time_now)
26
+ stub_const('Castle::VERSION', '2.2.0')
27
+ stub_request(:any, /api.castle.io/).with(
28
+ basic_auth: ['', 'secret']
29
+ ).to_return(status: 200, body: response_body, headers: {})
30
+ end
31
+
32
+ after { Timecop.return }
33
+
34
+ describe '.call' do
35
+ let(:request_body) do
36
+ { event: '$login.succeeded', context: context, user_id: '1234',
37
+ sent_at: time_auto }
38
+ end
39
+
40
+ before { call }
41
+
42
+ context 'when used with symbol keys' do
43
+ let(:options) { { event: '$login.succeeded', user_id: '1234', context: context } }
44
+
45
+ it do
46
+ assert_requested :post, 'https://api.castle.io/v1/identify', times: 1 do |req|
47
+ JSON.parse(req.body) == JSON.parse(request_body.to_json)
48
+ end
49
+ end
50
+
51
+ context 'when passed timestamp in options and no defined timestamp' do
52
+ let(:options) do
53
+ { event: '$login.succeeded', user_id: '1234', timestamp: time_user, context: context }
54
+ end
55
+ let(:request_body) do
56
+ { event: '$login.succeeded', user_id: '1234', context: context,
57
+ timestamp: time_user, sent_at: time_auto }
58
+ end
59
+
60
+ it do
61
+ assert_requested :post, 'https://api.castle.io/v1/identify', times: 1 do |req|
62
+ JSON.parse(req.body) == JSON.parse(request_body.to_json)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::API::ReportDevice do
4
+ before do
5
+ stub_request(:any, /api.castle.io/).with(
6
+ basic_auth: ['', 'secret']
7
+ ).to_return(status: 200, body: '{}', headers: {})
8
+ end
9
+
10
+ describe '.call' do
11
+ subject(:retrieve) { described_class.call(device_token: device_token) }
12
+
13
+ let(:device_token) { '1234' }
14
+
15
+ before { retrieve }
16
+
17
+ it do
18
+ assert_requested :put, "https://api.castle.io/v1/devices/#{device_token}/report", times: 1
19
+ end
20
+ end
21
+ end
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- describe Castle::Review do
3
+ describe Castle::API::Review do
4
4
  before do
5
5
  stub_request(:any, /api.castle.io/).with(
6
6
  basic_auth: ['', 'secret']
7
7
  ).to_return(status: 200, body: '{}', headers: {})
8
8
  end
9
9
 
10
- describe '#retrieve' do
11
- subject(:retrieve) { described_class.retrieve(review_id) }
10
+ describe '.call' do
11
+ subject(:retrieve) { described_class.call(review_id: review_id) }
12
12
 
13
13
  let(:review_id) { '1234' }
14
14
 
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::API::StartImpersonation do
4
+ subject(:call) { described_class.call(options) }
5
+
6
+ let(:ip) { '1.2.3.4' }
7
+ let(:cookie_id) { 'abcd' }
8
+ let(:ua) { 'Chrome' }
9
+ let(:env) do
10
+ Rack::MockRequest.env_for(
11
+ '/',
12
+ 'HTTP_USER_AGENT' => ua,
13
+ 'HTTP_X_FORWARDED_FOR' => ip,
14
+ 'HTTP_COOKIE' => "__cid=#{cookie_id};other=efgh"
15
+ )
16
+ end
17
+ let(:request) { Rack::Request.new(env) }
18
+ let(:context) { Castle::Context::Prepare.call(request) }
19
+ let(:time_now) { Time.now }
20
+ let(:time_auto) { time_now.utc.iso8601(3) }
21
+
22
+ before do
23
+ Timecop.freeze(time_now)
24
+ stub_const('Castle::VERSION', '2.2.0')
25
+ stub_request(:any, /api.castle.io/).with(
26
+ basic_auth: ['', 'secret']
27
+ ).to_return(status: 200, body: response_body, headers: {})
28
+ end
29
+
30
+ after { Timecop.return }
31
+
32
+ describe 'call' do
33
+ let(:impersonator) { 'test@castle.io' }
34
+ let(:request_body) do
35
+ { user_id: '1234', sent_at: time_auto,
36
+ properties: { impersonator: impersonator }, context: context }
37
+ end
38
+ let(:response_body) { { success: true }.to_json }
39
+ let(:options) do
40
+ { user_id: '1234', properties: { impersonator: impersonator }, context: context }
41
+ end
42
+
43
+ context 'when used with symbol keys' do
44
+ before { call }
45
+
46
+ it do
47
+ assert_requested :post, 'https://api.castle.io/v1/impersonate', times: 1 do |req|
48
+ JSON.parse(req.body) == JSON.parse(request_body.to_json)
49
+ end
50
+ end
51
+ end
52
+
53
+ context 'when request is not successful' do
54
+ let(:response_body) { {}.to_json }
55
+
56
+ it { expect { call }.to raise_error(Castle::ImpersonationFailed) }
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::API::Track do
4
+ subject(:call) { described_class.call(options) }
5
+
6
+ let(:ip) { '1.2.3.4' }
7
+ let(:cookie_id) { 'abcd' }
8
+ let(:ua) { 'Chrome' }
9
+ let(:env) do
10
+ Rack::MockRequest.env_for(
11
+ '/',
12
+ 'HTTP_USER_AGENT' => ua,
13
+ 'HTTP_X_FORWARDED_FOR' => ip,
14
+ 'HTTP_COOKIE' => "__cid=#{cookie_id};other=efgh"
15
+ )
16
+ end
17
+ let(:request) { Rack::Request.new(env) }
18
+ let(:context) { Castle::Context::Prepare.call(request) }
19
+ let(:time_now) { Time.now }
20
+ let(:time_auto) { time_now.utc.iso8601(3) }
21
+ let(:time_user) { (Time.now - 10_000).utc.iso8601(3) }
22
+ let(:response_body) { {}.to_json }
23
+
24
+ before do
25
+ Timecop.freeze(time_now)
26
+ stub_const('Castle::VERSION', '2.2.0')
27
+ stub_request(:any, /api.castle.io/).with(
28
+ basic_auth: ['', 'secret']
29
+ ).to_return(status: 200, body: response_body, headers: {})
30
+ end
31
+
32
+ after { Timecop.return }
33
+
34
+ describe 'track' do
35
+ let(:request_body) do
36
+ { event: '$login.succeeded', context: context, user_id: '1234',
37
+ sent_at: time_auto }
38
+ end
39
+
40
+ before { call }
41
+
42
+ context 'when used with symbol keys' do
43
+ let(:options) { { event: '$login.succeeded', user_id: '1234', context: context } }
44
+
45
+ it do
46
+ assert_requested :post, 'https://api.castle.io/v1/track', times: 1 do |req|
47
+ JSON.parse(req.body) == JSON.parse(request_body.to_json)
48
+ end
49
+ end
50
+
51
+ context 'when passed timestamp in options and no defined timestamp' do
52
+ let(:options) do
53
+ { event: '$login.succeeded', user_id: '1234', timestamp: time_user, context: context }
54
+ end
55
+ let(:request_body) do
56
+ { event: '$login.succeeded', user_id: '1234', context: context,
57
+ timestamp: time_user, sent_at: time_auto }
58
+ end
59
+
60
+ it do
61
+ assert_requested :post, 'https://api.castle.io/v1/track', times: 1 do |req|
62
+ JSON.parse(req.body) == JSON.parse(request_body.to_json)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -3,7 +3,7 @@
3
3
  describe Castle::API do
4
4
  subject(:call) { described_class.call(command) }
5
5
 
6
- let(:command) { Castle::Commands::Track.new({}).build(event: '$login.succeeded') }
6
+ let(:command) { Castle::Commands::Track.build(event: '$login.succeeded') }
7
7
 
8
8
  context 'when request timeouts' do
9
9
  before { stub_request(:any, /api.castle.io/).to_timeout }
@@ -34,4 +34,19 @@ describe Castle::API do
34
34
  end.to raise_error(Castle::ConfigurationError)
35
35
  end
36
36
  end
37
+
38
+ context 'when custom config' do
39
+ let(:config) { Castle::Configuration.new }
40
+
41
+ before do
42
+ config.api_secret = 'test'
43
+ stub_request(:any, /api.castle.io/)
44
+ end
45
+
46
+ it do
47
+ expect do
48
+ call
49
+ end.not_to raise_error
50
+ end
51
+ end
37
52
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- describe Castle::Extractors::ClientId do
3
+ describe Castle::ClientId::Extract do
4
4
  subject(:extractor) { described_class.new(formatted_headers, cookies) }
5
5
 
6
- let(:formatted_headers) { Castle::HeadersFilter.new(request).call }
6
+ let(:formatted_headers) { Castle::Headers::Filter.new(request).call }
7
7
  let(:client_id_cookie) { 'abcd' }
8
8
  let(:client_id_header) { 'abcde' }
9
9
  let(:cookies) { request.cookies }
@@ -14,11 +14,11 @@ describe Castle::Client do
14
14
  end
15
15
  let(:request) { Rack::Request.new(env) }
16
16
  let(:client) { described_class.from_request(request) }
17
- let(:request_to_context) { described_class.to_context(request) }
17
+ let(:request_to_context) { Castle::Context::Prepare.call(request) }
18
18
  let(:client_with_user_timestamp) do
19
- described_class.new(request_to_context, timestamp: time_user)
19
+ described_class.new(context: request_to_context, timestamp: time_user)
20
20
  end
21
- let(:client_with_no_timestamp) { described_class.new(request_to_context) }
21
+ let(:client_with_no_timestamp) { described_class.new(context: request_to_context) }
22
22
 
23
23
  let(:headers) do
24
24
  {
@@ -29,7 +29,6 @@ describe Castle::Client do
29
29
  {
30
30
  client_id: 'abcd',
31
31
  active: true,
32
- origin: 'web',
33
32
  user_agent: ua,
34
33
  headers: headers,
35
34
  ip: ip,
@@ -54,41 +53,54 @@ describe Castle::Client do
54
53
 
55
54
  describe 'parses the request' do
56
55
  before do
57
- allow(Castle::API).to receive(:request).and_call_original
56
+ allow(Castle::API).to receive(:send_request).and_call_original
58
57
  end
59
58
 
60
59
  it do
61
60
  client.authenticate(event: '$login.succeeded', user_id: '1234')
62
- expect(Castle::API).to have_received(:request)
61
+ expect(Castle::API).to have_received(:send_request)
63
62
  end
64
63
  end
65
64
 
66
- describe 'to_context' do
67
- it do
68
- expect(described_class.to_context(request)).to eql(context)
65
+ describe 'end impersonation' do
66
+ let(:impersonator) { 'test@castle.io' }
67
+ let(:request_body) do
68
+ { user_id: '1234', timestamp: time_auto, sent_at: time_auto,
69
+ properties: { impersonator: impersonator }, context: context }
69
70
  end
70
- end
71
+ let(:response_body) { { success: true }.to_json }
72
+ let(:options) { { user_id: '1234', properties: { impersonator: impersonator } } }
71
73
 
72
- describe 'to_options' do
73
- let(:options) { { user_id: '1234', user_traits: { name: 'Jo' } } }
74
- let(:result) { { user_id: '1234', user_traits: { name: 'Jo' }, timestamp: time_auto } }
74
+ context 'when used with symbol keys' do
75
+ before { client.end_impersonation(options) }
75
76
 
76
- it do
77
- expect(described_class.to_options(options)).to eql(result)
77
+ it do
78
+ assert_requested :delete, 'https://api.castle.io/v1/impersonate', times: 1 do |req|
79
+ JSON.parse(req.body) == JSON.parse(request_body.to_json)
80
+ end
81
+ end
82
+ end
83
+
84
+ context 'when request is not successful' do
85
+ let(:response_body) { {}.to_json }
86
+
87
+ it do
88
+ expect { client.end_impersonation(options) }.to raise_error(Castle::ImpersonationFailed)
89
+ end
78
90
  end
79
91
  end
80
92
 
81
- describe 'impersonate' do
93
+ describe 'start impersonation' do
82
94
  let(:impersonator) { 'test@castle.io' }
83
95
  let(:request_body) do
84
96
  { user_id: '1234', timestamp: time_auto, sent_at: time_auto,
85
- impersonator: impersonator, context: context }
97
+ properties: { impersonator: impersonator }, context: context }
86
98
  end
87
99
  let(:response_body) { { success: true }.to_json }
88
- let(:options) { { user_id: '1234', impersonator: impersonator } }
100
+ let(:options) { { user_id: '1234', properties: { impersonator: impersonator } } }
89
101
 
90
102
  context 'when used with symbol keys' do
91
- before { client.impersonate(options) }
103
+ before { client.start_impersonation(options) }
92
104
 
93
105
  it do
94
106
  assert_requested :post, 'https://api.castle.io/v1/impersonate', times: 1 do |req|
@@ -100,7 +112,9 @@ describe Castle::Client do
100
112
  context 'when request is not successful' do
101
113
  let(:response_body) { {}.to_json }
102
114
 
103
- it { expect { client.impersonate(options) }.to raise_error(Castle::ImpersonationFailed) }
115
+ it do
116
+ expect { client.start_impersonation(options) }.to raise_error(Castle::ImpersonationFailed)
117
+ end
104
118
  end
105
119
  end
106
120
 
@@ -241,7 +255,7 @@ describe Castle::Client do
241
255
  end
242
256
 
243
257
  it { assert_not_requested :post, 'https://api.castle.io/v1/authenticate' }
244
- it { expect(request_response[:action]).to be_eql('allow') }
258
+ it { expect(request_response[:action]).to be_eql(Castle::Verdict::ALLOW) }
245
259
  it { expect(request_response[:user_id]).to be_eql('1234') }
246
260
  it { expect(request_response[:failover]).to be true }
247
261
  it { expect(request_response[:failover_reason]).to be_eql('Castle is set to do not track.') }
@@ -249,7 +263,9 @@ describe Castle::Client do
249
263
 
250
264
  context 'when request with fail' do
251
265
  before do
252
- allow(Castle::API).to receive(:request).and_raise(Castle::RequestError.new(Timeout::Error))
266
+ allow(Castle::API).to receive(:send_request).and_raise(
267
+ Castle::RequestError.new(Timeout::Error)
268
+ )
253
269
  end
254
270
 
255
271
  context 'with request error and throw strategy' do
@@ -268,7 +284,9 @@ describe Castle::Client do
268
284
  end
269
285
 
270
286
  context 'when request is internal server error' do
271
- before { allow(Castle::API).to receive(:request).and_raise(Castle::InternalServerError) }
287
+ before do
288
+ allow(Castle::API).to receive(:send_request).and_raise(Castle::InternalServerError)
289
+ end
272
290
 
273
291
  describe 'throw strategy' do
274
292
  before { allow(Castle.config).to receive(:failover_strategy).and_return(:throw) }