castle-rb 6.0.1 → 7.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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -8
  3. data/lib/castle.rb +7 -11
  4. data/lib/castle/api.rb +7 -12
  5. data/lib/castle/api/approve_device.rb +1 -6
  6. data/lib/castle/api/authenticate.rb +10 -7
  7. data/lib/castle/api/end_impersonation.rb +3 -8
  8. data/lib/castle/api/filter.rb +37 -0
  9. data/lib/castle/api/get_device.rb +1 -6
  10. data/lib/castle/api/get_devices_for_user.rb +1 -6
  11. data/lib/castle/api/log.rb +37 -0
  12. data/lib/castle/api/report_device.rb +1 -6
  13. data/lib/castle/api/risk.rb +37 -0
  14. data/lib/castle/api/start_impersonation.rb +3 -8
  15. data/lib/castle/api/track.rb +1 -6
  16. data/lib/castle/client.rb +36 -16
  17. data/lib/castle/commands/approve_device.rb +1 -5
  18. data/lib/castle/commands/end_impersonation.rb +1 -1
  19. data/lib/castle/commands/filter.rb +23 -0
  20. data/lib/castle/commands/get_device.rb +1 -5
  21. data/lib/castle/commands/get_devices_for_user.rb +1 -5
  22. data/lib/castle/commands/{identify.rb → log.rb} +4 -3
  23. data/lib/castle/commands/report_device.rb +1 -5
  24. data/lib/castle/commands/risk.rb +23 -0
  25. data/lib/castle/commands/start_impersonation.rb +1 -1
  26. data/lib/castle/configuration.rb +18 -8
  27. data/lib/castle/core/get_connection.rb +3 -1
  28. data/lib/castle/core/process_response.rb +5 -2
  29. data/lib/castle/core/process_webhook.rb +10 -5
  30. data/lib/castle/core/send_request.rb +8 -16
  31. data/lib/castle/errors.rb +37 -13
  32. data/lib/castle/failover/prepare_response.rb +2 -7
  33. data/lib/castle/failover/strategy.rb +3 -0
  34. data/lib/castle/headers/extract.rb +4 -4
  35. data/lib/castle/headers/filter.rb +9 -6
  36. data/lib/castle/ips/extract.rb +4 -2
  37. data/lib/castle/logger.rb +3 -3
  38. data/lib/castle/payload/prepare.rb +3 -4
  39. data/lib/castle/secure_mode.rb +3 -2
  40. data/lib/castle/support/hanami.rb +2 -6
  41. data/lib/castle/support/rails.rb +1 -3
  42. data/lib/castle/utils/clean_invalid_chars.rb +1 -3
  43. data/lib/castle/verdict.rb +2 -0
  44. data/lib/castle/version.rb +1 -1
  45. data/lib/castle/webhooks/verify.rb +9 -7
  46. data/spec/integration/rails/rails_spec.rb +9 -7
  47. data/spec/integration/rails/support/home_controller.rb +26 -24
  48. data/spec/lib/castle/api/approve_device_spec.rb +3 -3
  49. data/spec/lib/castle/api/authenticate_spec.rb +20 -24
  50. data/spec/lib/castle/api/end_impersonation_spec.rb +11 -5
  51. data/spec/lib/castle/api/filter_spec.rb +5 -0
  52. data/spec/lib/castle/api/get_device_spec.rb +3 -3
  53. data/spec/lib/castle/api/get_devices_for_user_spec.rb +3 -3
  54. data/spec/lib/castle/api/log_spec.rb +5 -0
  55. data/spec/lib/castle/api/report_device_spec.rb +3 -3
  56. data/spec/lib/castle/api/risk_spec.rb +5 -0
  57. data/spec/lib/castle/api/start_impersonation_spec.rb +11 -5
  58. data/spec/lib/castle/api/track_spec.rb +11 -7
  59. data/spec/lib/castle/api_spec.rb +4 -20
  60. data/spec/lib/castle/client_id/extract_spec.rb +4 -13
  61. data/spec/lib/castle/client_spec.rb +81 -84
  62. data/spec/lib/castle/commands/authenticate_spec.rb +8 -15
  63. data/spec/lib/castle/commands/end_impersonation_spec.rb +6 -9
  64. data/spec/lib/castle/commands/{identify_spec.rb → filter_spec.rb} +41 -19
  65. data/spec/lib/castle/commands/log_spec.rb +100 -0
  66. data/spec/lib/castle/commands/risk_spec.rb +100 -0
  67. data/spec/lib/castle/commands/start_impersonation_spec.rb +6 -9
  68. data/spec/lib/castle/commands/track_spec.rb +9 -18
  69. data/spec/lib/castle/configuration_spec.rb +2 -6
  70. data/spec/lib/castle/context/get_default_spec.rb +8 -8
  71. data/spec/lib/castle/context/prepare_spec.rb +6 -7
  72. data/spec/lib/castle/core/get_connection_spec.rb +6 -22
  73. data/spec/lib/castle/core/process_response_spec.rb +1 -8
  74. data/spec/lib/castle/core/send_request_spec.rb +4 -29
  75. data/spec/lib/castle/headers/extract_spec.rb +1 -3
  76. data/spec/lib/castle/headers/filter_spec.rb +12 -11
  77. data/spec/lib/castle/ips/extract_spec.rb +4 -13
  78. data/spec/lib/castle/logger_spec.rb +2 -6
  79. data/spec/lib/castle/payload/prepare_spec.rb +5 -4
  80. data/spec/lib/castle/session_spec.rb +13 -36
  81. data/spec/lib/castle/singleton_configuration_spec.rb +2 -6
  82. data/spec/lib/castle/utils/clean_invalid_chars_spec.rb +2 -2
  83. data/spec/lib/castle/utils/merge_spec.rb +3 -1
  84. data/spec/lib/castle/validators/present_spec.rb +5 -6
  85. data/spec/lib/castle/webhooks/verify_spec.rb +8 -24
  86. data/spec/lib/castle_spec.rb +4 -10
  87. data/spec/spec_helper.rb +1 -3
  88. data/spec/support/shared_examples/action_request.rb +152 -0
  89. data/spec/support/shared_examples/configuration.rb +14 -42
  90. metadata +23 -18
  91. data/lib/castle/api/identify.rb +0 -26
  92. data/lib/castle/api/review.rb +0 -24
  93. data/lib/castle/commands/review.rb +0 -17
  94. data/lib/castle/events.rb +0 -49
  95. data/spec/lib/castle/api/identify_spec.rb +0 -68
  96. data/spec/lib/castle/api/review_spec.rb +0 -19
  97. data/spec/lib/castle/commands/review_spec.rb +0 -24
  98. data/spec/lib/castle/events_spec.rb +0 -5
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Castle::API::Risk do
4
+ pending
5
+ end
@@ -22,9 +22,9 @@ describe Castle::API::StartImpersonation do
22
22
  before do
23
23
  Timecop.freeze(time_now)
24
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: {})
25
+ stub_request(:any, /api.castle.io/)
26
+ .with(basic_auth: ['', 'secret'])
27
+ .to_return(status: 200, body: response_body, headers: {})
28
28
  end
29
29
 
30
30
  after { Timecop.return }
@@ -32,8 +32,14 @@ describe Castle::API::StartImpersonation do
32
32
  describe 'call' do
33
33
  let(:impersonator) { 'test@castle.io' }
34
34
  let(:request_body) do
35
- { user_id: '1234', sent_at: time_auto,
36
- properties: { impersonator: impersonator }, context: context }
35
+ {
36
+ user_id: '1234',
37
+ sent_at: time_auto,
38
+ properties: {
39
+ impersonator: impersonator
40
+ },
41
+ context: context
42
+ }
37
43
  end
38
44
  let(:response_body) { { success: true }.to_json }
39
45
  let(:options) do
@@ -24,17 +24,16 @@ describe Castle::API::Track do
24
24
  before do
25
25
  Timecop.freeze(time_now)
26
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: {})
27
+ stub_request(:any, /api.castle.io/)
28
+ .with(basic_auth: ['', 'secret'])
29
+ .to_return(status: 200, body: response_body, headers: {})
30
30
  end
31
31
 
32
32
  after { Timecop.return }
33
33
 
34
34
  describe 'track' do
35
35
  let(:request_body) do
36
- { event: '$login.succeeded', context: context, user_id: '1234',
37
- sent_at: time_auto }
36
+ { event: '$login.succeeded', context: context, user_id: '1234', sent_at: time_auto }
38
37
  end
39
38
 
40
39
  before { call }
@@ -53,8 +52,13 @@ describe Castle::API::Track do
53
52
  { event: '$login.succeeded', user_id: '1234', timestamp: time_user, context: context }
54
53
  end
55
54
  let(:request_body) do
56
- { event: '$login.succeeded', user_id: '1234', context: context,
57
- timestamp: time_user, sent_at: time_auto }
55
+ {
56
+ event: '$login.succeeded',
57
+ user_id: '1234',
58
+ context: context,
59
+ timestamp: time_user,
60
+ sent_at: time_auto
61
+ }
58
62
  end
59
63
 
60
64
  it do
@@ -8,31 +8,19 @@ describe Castle::API do
8
8
  context 'when request timeouts' do
9
9
  before { stub_request(:any, /api.castle.io/).to_timeout }
10
10
 
11
- it do
12
- expect do
13
- call
14
- end.to raise_error(Castle::RequestError)
15
- end
11
+ it { expect { call }.to raise_error(Castle::RequestError) }
16
12
  end
17
13
 
18
14
  context 'when non-OK response code' do
19
15
  before { stub_request(:any, /api.castle.io/).to_return(status: 400) }
20
16
 
21
- it do
22
- expect do
23
- call
24
- end.to raise_error(Castle::BadRequestError)
25
- end
17
+ it { expect { call }.to raise_error(Castle::BadRequestError) }
26
18
  end
27
19
 
28
20
  context 'when no api_secret' do
29
21
  before { allow(Castle.config).to receive(:api_secret).and_return('') }
30
22
 
31
- it do
32
- expect do
33
- call
34
- end.to raise_error(Castle::ConfigurationError)
35
- end
23
+ it { expect { call }.to raise_error(Castle::ConfigurationError) }
36
24
  end
37
25
 
38
26
  context 'when custom config' do
@@ -43,10 +31,6 @@ describe Castle::API do
43
31
  stub_request(:any, /api.castle.io/)
44
32
  end
45
33
 
46
- it do
47
- expect do
48
- call
49
- end.not_to raise_error
50
- end
34
+ it { expect { call }.not_to raise_error }
51
35
  end
52
36
  end
@@ -8,9 +8,7 @@ describe Castle::ClientId::Extract do
8
8
  let(:client_id_header) { 'abcde' }
9
9
  let(:cookies) { request.cookies }
10
10
  let(:request) { Rack::Request.new(env) }
11
- let(:env) do
12
- Rack::MockRequest.env_for('/', headers)
13
- end
11
+ let(:env) { Rack::MockRequest.env_for('/', headers) }
14
12
 
15
13
  context 'with client_id' do
16
14
  let(:headers) do
@@ -20,17 +18,12 @@ describe Castle::ClientId::Extract do
20
18
  }
21
19
  end
22
20
 
23
- it do
24
- expect(extractor.call).to eql(client_id_cookie)
25
- end
21
+ it { expect(extractor.call).to eql(client_id_cookie) }
26
22
  end
27
23
 
28
24
  context 'with X-Castle-Client-Id header' do
29
25
  let(:headers) do
30
- {
31
- 'HTTP_X_FORWARDED_FOR' => '1.2.3.4',
32
- 'HTTP_X_CASTLE_CLIENT_ID' => client_id_header
33
- }
26
+ { 'HTTP_X_FORWARDED_FOR' => '1.2.3.4', 'HTTP_X_CASTLE_CLIENT_ID' => client_id_header }
34
27
  end
35
28
 
36
29
  it 'appends the client_id' do
@@ -42,9 +35,7 @@ describe Castle::ClientId::Extract do
42
35
  let(:cookies) { nil }
43
36
  let(:headers) { {} }
44
37
 
45
- it do
46
- expect(extractor.call).to eql('')
47
- end
38
+ it { expect(extractor.call).to eql('') }
48
39
  end
49
40
 
50
41
  context 'with X-Castle-Client-Id header and cookies client' do
@@ -21,9 +21,7 @@ describe Castle::Client do
21
21
  let(:client_with_no_timestamp) { described_class.new(context: request_to_context) }
22
22
 
23
23
  let(:headers) do
24
- {
25
- 'Content-Length': '0', 'User-Agent': ua, 'X-Forwarded-For': ip.to_s, 'Cookie': true
26
- }
24
+ { 'Content-Length': '0', 'User-Agent': ua, 'X-Forwarded-For': ip.to_s, 'Cookie': true }
27
25
  end
28
26
  let(:context) do
29
27
  {
@@ -32,7 +30,10 @@ describe Castle::Client do
32
30
  user_agent: ua,
33
31
  headers: headers,
34
32
  ip: ip,
35
- library: { name: 'castle-rb', version: '2.2.0' }
33
+ library: {
34
+ name: 'castle-rb',
35
+ version: '2.2.0'
36
+ }
36
37
  }
37
38
  end
38
39
 
@@ -44,17 +45,15 @@ describe Castle::Client do
44
45
  before do
45
46
  Timecop.freeze(time_now)
46
47
  stub_const('Castle::VERSION', '2.2.0')
47
- stub_request(:any, /api.castle.io/).with(
48
- basic_auth: ['', 'secret']
49
- ).to_return(status: 200, body: response_body, headers: {})
48
+ stub_request(:any, /api.castle.io/)
49
+ .with(basic_auth: ['', 'secret'])
50
+ .to_return(status: 200, body: response_body, headers: {})
50
51
  end
51
52
 
52
53
  after { Timecop.return }
53
54
 
54
55
  describe 'parses the request' do
55
- before do
56
- allow(Castle::API).to receive(:send_request).and_call_original
57
- end
56
+ before { allow(Castle::API).to receive(:send_request).and_call_original }
58
57
 
59
58
  it do
60
59
  client.authenticate(event: '$login.succeeded', user_id: '1234')
@@ -65,8 +64,15 @@ describe Castle::Client do
65
64
  describe 'end impersonation' do
66
65
  let(:impersonator) { 'test@castle.io' }
67
66
  let(:request_body) do
68
- { user_id: '1234', timestamp: time_auto, sent_at: time_auto,
69
- properties: { impersonator: impersonator }, context: context }
67
+ {
68
+ user_id: '1234',
69
+ timestamp: time_auto,
70
+ sent_at: time_auto,
71
+ properties: {
72
+ impersonator: impersonator
73
+ },
74
+ context: context
75
+ }
70
76
  end
71
77
  let(:response_body) { { success: true }.to_json }
72
78
  let(:options) { { user_id: '1234', properties: { impersonator: impersonator } } }
@@ -93,8 +99,15 @@ describe Castle::Client do
93
99
  describe 'start impersonation' do
94
100
  let(:impersonator) { 'test@castle.io' }
95
101
  let(:request_body) do
96
- { user_id: '1234', timestamp: time_auto, sent_at: time_auto,
97
- properties: { impersonator: impersonator }, context: context }
102
+ {
103
+ user_id: '1234',
104
+ timestamp: time_auto,
105
+ sent_at: time_auto,
106
+ properties: {
107
+ impersonator: impersonator
108
+ },
109
+ context: context
110
+ }
98
111
  end
99
112
  let(:response_body) { { success: true }.to_json }
100
113
  let(:options) { { user_id: '1234', properties: { impersonator: impersonator } } }
@@ -118,70 +131,17 @@ describe Castle::Client do
118
131
  end
119
132
  end
120
133
 
121
- describe 'identify' do
122
- let(:request_body) do
123
- { user_id: '1234', timestamp: time_auto,
124
- sent_at: time_auto, context: context, user_traits: { name: 'Jo' } }
125
- end
126
-
127
- before { client.identify(options) }
128
-
129
- context 'when used with symbol keys' do
130
- let(:options) { { user_id: '1234', user_traits: { name: 'Jo' } } }
131
-
132
- it do
133
- assert_requested :post, 'https://api.castle.io/v1/identify', times: 1 do |req|
134
- JSON.parse(req.body) == JSON.parse(request_body.to_json)
135
- end
136
- end
137
-
138
- context 'when passed timestamp in options and no defined timestamp' do
139
- let(:client) { client_with_no_timestamp }
140
- let(:options) { { user_id: '1234', user_traits: { name: 'Jo' }, timestamp: time_user } }
141
- let(:request_body) do
142
- { user_id: '1234', user_traits: { name: 'Jo' }, context: context,
143
- timestamp: time_user, sent_at: time_auto }
144
- end
145
-
146
- it do
147
- assert_requested :post, 'https://api.castle.io/v1/identify', times: 1 do |req|
148
- JSON.parse(req.body) == JSON.parse(request_body.to_json)
149
- end
150
- end
151
- end
152
-
153
- context 'with client initialized with timestamp' do
154
- let(:client) { client_with_user_timestamp }
155
- let(:request_body) do
156
- { user_id: '1234', timestamp: time_user, sent_at: time_auto,
157
- context: context, user_traits: { name: 'Jo' } }
158
- end
159
-
160
- it do
161
- assert_requested :post, 'https://api.castle.io/v1/identify', times: 1 do |req|
162
- JSON.parse(req.body) == JSON.parse(request_body.to_json)
163
- end
164
- end
165
- end
166
- end
167
-
168
- context 'when used with string keys' do
169
- let(:options) { { 'user_id' => '1234', 'user_traits' => { 'name' => 'Jo' } } }
170
-
171
- it do
172
- assert_requested :post, 'https://api.castle.io/v1/identify', times: 1 do |req|
173
- JSON.parse(req.body) == JSON.parse(request_body.to_json)
174
- end
175
- end
176
- end
177
- end
178
-
179
134
  describe 'authenticate' do
180
135
  let(:options) { { event: '$login.succeeded', user_id: '1234' } }
181
136
  let(:request_response) { client.authenticate(options) }
182
137
  let(:request_body) do
183
- { event: '$login.succeeded', user_id: '1234', context: context,
184
- timestamp: time_auto, sent_at: time_auto }
138
+ {
139
+ event: '$login.succeeded',
140
+ user_id: '1234',
141
+ context: context,
142
+ timestamp: time_auto,
143
+ sent_at: time_auto
144
+ }
185
145
  end
186
146
 
187
147
  context 'when used with symbol keys' do
@@ -197,8 +157,13 @@ describe Castle::Client do
197
157
  let(:client) { client_with_no_timestamp }
198
158
  let(:options) { { event: '$login.succeeded', user_id: '1234', timestamp: time_user } }
199
159
  let(:request_body) do
200
- { event: '$login.succeeded', user_id: '1234', context: context,
201
- timestamp: time_user, sent_at: time_auto }
160
+ {
161
+ event: '$login.succeeded',
162
+ user_id: '1234',
163
+ context: context,
164
+ timestamp: time_user,
165
+ sent_at: time_auto
166
+ }
202
167
  end
203
168
 
204
169
  it do
@@ -211,8 +176,13 @@ describe Castle::Client do
211
176
  context 'with client initialized with timestamp' do
212
177
  let(:client) { client_with_user_timestamp }
213
178
  let(:request_body) do
214
- { event: '$login.succeeded', user_id: '1234', context: context,
215
- timestamp: time_user, sent_at: time_auto }
179
+ {
180
+ event: '$login.succeeded',
181
+ user_id: '1234',
182
+ context: context,
183
+ timestamp: time_user,
184
+ sent_at: time_auto
185
+ }
216
186
  end
217
187
 
218
188
  it do
@@ -306,8 +276,13 @@ describe Castle::Client do
306
276
 
307
277
  describe 'track' do
308
278
  let(:request_body) do
309
- { event: '$login.succeeded', context: context, user_id: '1234',
310
- timestamp: time_auto, sent_at: time_auto }
279
+ {
280
+ event: '$login.succeeded',
281
+ context: context,
282
+ user_id: '1234',
283
+ timestamp: time_auto,
284
+ sent_at: time_auto
285
+ }
311
286
  end
312
287
 
313
288
  before { client.track(options) }
@@ -325,8 +300,13 @@ describe Castle::Client do
325
300
  let(:client) { client_with_no_timestamp }
326
301
  let(:options) { { event: '$login.succeeded', user_id: '1234', timestamp: time_user } }
327
302
  let(:request_body) do
328
- { event: '$login.succeeded', user_id: '1234', context: context,
329
- timestamp: time_user, sent_at: time_auto }
303
+ {
304
+ event: '$login.succeeded',
305
+ user_id: '1234',
306
+ context: context,
307
+ timestamp: time_user,
308
+ sent_at: time_auto
309
+ }
330
310
  end
331
311
 
332
312
  it do
@@ -339,8 +319,13 @@ describe Castle::Client do
339
319
  context 'with client initialized with timestamp' do
340
320
  let(:client) { client_with_user_timestamp }
341
321
  let(:request_body) do
342
- { event: '$login.succeeded', context: context, user_id: '1234',
343
- timestamp: time_user, sent_at: time_auto }
322
+ {
323
+ event: '$login.succeeded',
324
+ context: context,
325
+ user_id: '1234',
326
+ timestamp: time_user,
327
+ sent_at: time_auto
328
+ }
344
329
  end
345
330
 
346
331
  it do
@@ -375,4 +360,16 @@ describe Castle::Client do
375
360
  it { expect(client).to be_tracked }
376
361
  end
377
362
  end
363
+
364
+ describe 'filter' do
365
+ it_behaves_like 'action request', :filter
366
+ end
367
+
368
+ describe 'risk' do
369
+ it_behaves_like 'action request', :risk
370
+ end
371
+
372
+ describe 'log' do
373
+ it_behaves_like 'action request', :log
374
+ end
378
375
  end
@@ -20,9 +20,7 @@ describe Castle::Commands::Authenticate do
20
20
 
21
21
  context 'with properties' do
22
22
  let(:payload) { default_payload.merge(properties: { test: '1' }) }
23
- let(:command_data) do
24
- default_payload.merge(properties: { test: '1' }, context: context)
25
- end
23
+ let(:command_data) { default_payload.merge(properties: { test: '1' }, context: context) }
26
24
 
27
25
  it { expect(command.method).to be_eql(:post) }
28
26
  it { expect(command.path).to be_eql('authenticate') }
@@ -31,9 +29,7 @@ describe Castle::Commands::Authenticate do
31
29
 
32
30
  context 'with user_traits' do
33
31
  let(:payload) { default_payload.merge(user_traits: { test: '1' }) }
34
- let(:command_data) do
35
- default_payload.merge(user_traits: { test: '1' }, context: context)
36
- end
32
+ let(:command_data) { default_payload.merge(user_traits: { test: '1' }, context: context) }
37
33
 
38
34
  it { expect(command.method).to be_eql(:post) }
39
35
  it { expect(command.path).to be_eql('authenticate') }
@@ -42,9 +38,7 @@ describe Castle::Commands::Authenticate do
42
38
 
43
39
  context 'when active true' do
44
40
  let(:payload) { default_payload.merge(context: context.merge(active: true)) }
45
- let(:command_data) do
46
- default_payload.merge(context: context.merge(active: true))
47
- end
41
+ let(:command_data) { default_payload.merge(context: context.merge(active: true)) }
48
42
 
49
43
  it { expect(command.method).to be_eql(:post) }
50
44
  it { expect(command.path).to be_eql('authenticate') }
@@ -53,9 +47,7 @@ describe Castle::Commands::Authenticate do
53
47
 
54
48
  context 'when active false' do
55
49
  let(:payload) { default_payload.merge(context: context.merge(active: false)) }
56
- let(:command_data) do
57
- default_payload.merge(context: context.merge(active: false))
58
- end
50
+ let(:command_data) { default_payload.merge(context: context.merge(active: false)) }
59
51
 
60
52
  it { expect(command.method).to be_eql(:post) }
61
53
  it { expect(command.path).to be_eql('authenticate') }
@@ -79,9 +71,10 @@ describe Castle::Commands::Authenticate do
79
71
  let(:payload) { {} }
80
72
 
81
73
  it do
82
- expect do
83
- validate!
84
- end.to raise_error(Castle::InvalidParametersError, 'event is missing or empty')
74
+ expect { validate! }.to raise_error(
75
+ Castle::InvalidParametersError,
76
+ 'event is missing or empty'
77
+ )
85
78
  end
86
79
  end
87
80