razorrisk-cassini-common 0.26.24

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 (45) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +22 -0
  3. data/LICENSE +5 -0
  4. data/README.md +2 -0
  5. data/Rakefile +102 -0
  6. data/lib/razor_risk/cassini/applications/microservice.rb +318 -0
  7. data/lib/razor_risk/cassini/applications/rest_framework/route_verb_dispatcher.rb +120 -0
  8. data/lib/razor_risk/cassini/applications/rest_framework/verb_handler.rb +117 -0
  9. data/lib/razor_risk/cassini/applications/route_verb_adaptors/utilities/collection_get_helper.rb +86 -0
  10. data/lib/razor_risk/cassini/applications/securable_microservice.rb +164 -0
  11. data/lib/razor_risk/cassini/applications/secured_microservice.rb +63 -0
  12. data/lib/razor_risk/cassini/applications/unsecured_microservice.rb +77 -0
  13. data/lib/razor_risk/cassini/authorisation/header_helpers.rb +271 -0
  14. data/lib/razor_risk/cassini/authorisation/security_model_helpers.rb +93 -0
  15. data/lib/razor_risk/cassini/authorisation.rb +27 -0
  16. data/lib/razor_risk/cassini/cli.rb +19 -0
  17. data/lib/razor_risk/cassini/common/version.rb +44 -0
  18. data/lib/razor_risk/cassini/common.rb +32 -0
  19. data/lib/razor_risk/cassini/constants.rb +68 -0
  20. data/lib/razor_risk/cassini/diagnostics/util_functions.rb +248 -0
  21. data/lib/razor_risk/cassini/diagnostics/zeroth_include.rb +35 -0
  22. data/lib/razor_risk/cassini/extensions/libclimate/common_options.rb +267 -0
  23. data/lib/razor_risk/cassini/extensions/libclimate.rb +26 -0
  24. data/lib/razor_risk/cassini/header_functions.rb +59 -0
  25. data/lib/razor_risk/cassini/main.rb +238 -0
  26. data/lib/razor_risk/cassini/mixin/razor_response_validator.rb +176 -0
  27. data/lib/razor_risk/cassini/testing/suppress_pantheios_logging.rb +31 -0
  28. data/lib/razor_risk/cassini/util/conversion_util.rb +176 -0
  29. data/lib/razor_risk/cassini/util/program_execution_util.rb +379 -0
  30. data/lib/razor_risk/cassini/util/secrets_util.rb +229 -0
  31. data/lib/razor_risk/cassini/util/version_util.rb +88 -0
  32. data/lib/razor_risk/sinatra/helpers/check_auth_helper.rb +209 -0
  33. data/lib/razor_risk/sinatra/helpers/validate_accept_helper.rb +69 -0
  34. data/lib/razor_risk/sinatra/helpers/validate_content_type_helper.rb +74 -0
  35. data/lib/razor_risk/sinatra/helpers/validate_query_parameters_helper.rb +198 -0
  36. data/test/scratch/cassini/util/convert_XML.rb +54 -0
  37. data/test/unit/applications/route_verb_adaptors/utilities/tc_collection_get_helper.rb +236 -0
  38. data/test/unit/applications/tc_verb_handler.rb +130 -0
  39. data/test/unit/mixin/tc_razor_response_validator.rb +328 -0
  40. data/test/unit/sinatra/helpers/tc_validate_query_parameters_helper.rb +134 -0
  41. data/test/unit/tc_authorisation_util.rb +265 -0
  42. data/test/unit/tc_load_secrets.rb +95 -0
  43. data/test/unit/util/tc_conversion_util.rb +393 -0
  44. data/test/unit/util/tc_program_execution_util.rb +462 -0
  45. metadata +380 -0
@@ -0,0 +1,236 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *(['..'] * 5), 'lib')
4
+
5
+ require 'simplecov'
6
+
7
+ require 'razor_risk/cassini/testing/suppress_pantheios_logging' unless $DEBUG
8
+
9
+ require 'razor_risk/cassini/applications/route_verb_adaptors/utilities/collection_get_helper'
10
+
11
+ require 'xqsr3/extensions/test/unit'
12
+ require 'test/unit'
13
+
14
+ class Test_CollectionGetHelper_validate_exclusive_search_parameters < Test::Unit::TestCase
15
+
16
+ include ::RazorRisk::Cassini::Applications::RouteVerbAdaptors::Utilities::CollectionGetHelper
17
+
18
+ def halt code, headers, body_lines
19
+
20
+ if code && headers.nil?
21
+
22
+ headers = {}
23
+ end
24
+
25
+ if code && body_lines.nil?
26
+
27
+ body_lines = []
28
+ end
29
+
30
+ if ::String === body_lines
31
+
32
+ body_lines = [ body_lines ]
33
+ end
34
+
35
+
36
+ @code = code
37
+ @headers = headers
38
+ @body_lines = body_lines
39
+
40
+ nil
41
+ end
42
+
43
+ attr_reader :code
44
+ attr_reader :headers
45
+ attr_reader :body_lines
46
+
47
+ def test_empty_params
48
+
49
+ halt nil, nil, nil
50
+
51
+ r = validate_exclusive_search_parameters({})
52
+
53
+ assert r
54
+
55
+
56
+ halt nil, nil, nil
57
+
58
+ r = validate_exclusive_search_parameters({}, 'filter-name')
59
+
60
+ assert r
61
+ assert_nil code
62
+ assert_nil headers
63
+ assert_nil body_lines
64
+
65
+
66
+ halt nil, nil, nil
67
+
68
+ r = validate_exclusive_search_parameters({}, 'field-search', 'filter-name', 'quick-search')
69
+
70
+ assert r
71
+ assert_nil code
72
+ assert_nil headers
73
+ assert_nil body_lines
74
+ end
75
+
76
+ def test_non_conflicts
77
+
78
+ param_names = [
79
+
80
+ 'field-search',
81
+ 'filter-name',
82
+ 'quick-search',
83
+ ]
84
+
85
+ halt nil, nil, nil
86
+
87
+ r = validate_exclusive_search_parameters({}, *param_names)
88
+
89
+ assert r
90
+ assert_nil code
91
+ assert_nil headers
92
+ assert_nil body_lines
93
+
94
+
95
+ halt nil, nil, nil
96
+
97
+ r = validate_exclusive_search_parameters({ 'field-search' => 'abc' }, *param_names)
98
+
99
+ assert r
100
+ assert_nil code
101
+ assert_nil headers
102
+ assert_nil body_lines
103
+
104
+
105
+ halt nil, nil, nil
106
+
107
+ r = validate_exclusive_search_parameters({ 'filter-name' => 'abc' }, *param_names)
108
+
109
+ assert r
110
+ assert_nil code
111
+ assert_nil headers
112
+ assert_nil body_lines
113
+
114
+
115
+ halt nil, nil, nil
116
+
117
+ r = validate_exclusive_search_parameters({ 'quick-search' => 'abc' }, *param_names)
118
+
119
+ assert r
120
+ assert_nil code
121
+ assert_nil headers
122
+ assert_nil body_lines
123
+
124
+
125
+ halt nil, nil, nil
126
+
127
+ r = validate_exclusive_search_parameters({ 'other-param' => 'xyz', 'field-search' => 'abc' }, *param_names)
128
+
129
+ assert r
130
+ assert_nil code
131
+ assert_nil headers
132
+ assert_nil body_lines
133
+
134
+
135
+ halt nil, nil, nil
136
+
137
+ r = validate_exclusive_search_parameters({ 'other-param' => 'xyz', 'filter-name' => 'abc' }, *param_names)
138
+
139
+ assert r
140
+ assert_nil code
141
+ assert_nil headers
142
+ assert_nil body_lines
143
+
144
+
145
+ halt nil, nil, nil
146
+
147
+ r = validate_exclusive_search_parameters({ 'other-param' => 'xyz', 'quick-search' => 'abc' }, *param_names)
148
+
149
+ assert r
150
+ assert_nil code
151
+ assert_nil headers
152
+ assert_nil body_lines
153
+ end
154
+
155
+ def test_conflicts
156
+
157
+ param_names = [
158
+
159
+ 'quick-search',
160
+ 'field-search',
161
+ 'filter-name',
162
+ ]
163
+
164
+
165
+ halt nil, nil, nil
166
+
167
+ validate_exclusive_search_parameters({ 'field-search' => 'abc', 'filter-name' => 'xyz' }, *param_names)
168
+
169
+ assert_equal 422, code
170
+ assert_not_nil headers
171
+ assert_empty headers
172
+ assert_match /invalid search specifier: cannot specify 'field-search' and 'filter-name' together/, body_lines[0]
173
+
174
+
175
+ halt nil, nil, nil
176
+
177
+ validate_exclusive_search_parameters({ 'filter-name' => 'abc', 'quick-search' => 'xyz' }, *param_names)
178
+
179
+ assert_equal 422, code
180
+ assert_not_nil headers
181
+ assert_empty headers
182
+ assert_match /invalid search specifier: cannot specify 'filter-name' and 'quick-search' together/, body_lines[0]
183
+
184
+
185
+ halt nil, nil, nil
186
+
187
+ validate_exclusive_search_parameters({ 'quick-search' => 'abc', 'field-search' => 'xyz' }, *param_names)
188
+
189
+ assert_equal 422, code
190
+ assert_not_nil headers
191
+ assert_empty headers
192
+ assert_match /invalid search specifier: cannot specify 'field-search' and 'quick-search' together/, body_lines[0]
193
+ end
194
+
195
+ def test_conflicts_with_custom_message_scope_and_halt_code
196
+
197
+ param_names = [
198
+
199
+ 'quick-search',
200
+ 'field-search',
201
+ 'filter-name',
202
+ ]
203
+
204
+
205
+ halt nil, nil, nil
206
+
207
+ validate_exclusive_search_parameters({ 'field-search' => 'abc', 'filter-name' => 'xyz' }, *param_names, halt_code: 555, message_scope: 'incompatible search specifiers')
208
+
209
+ assert_equal 555, code
210
+ assert_not_nil headers
211
+ assert_empty headers
212
+ assert_match /incompatible search specifiers: cannot specify 'field-search' and 'filter-name' together/, body_lines[0]
213
+
214
+
215
+ halt nil, nil, nil
216
+
217
+ validate_exclusive_search_parameters({ 'filter-name' => 'abc', 'quick-search' => 'xyz' }, *param_names, halt_code: 555, message_scope: '')
218
+
219
+ assert_equal 555, code
220
+ assert_not_nil headers
221
+ assert_empty headers
222
+ assert_match /cannot specify 'filter-name' and 'quick-search' together/, body_lines[0]
223
+
224
+
225
+ halt nil, nil, nil
226
+
227
+ validate_exclusive_search_parameters({ 'quick-search' => 'abc', 'field-search' => 'xyz' }, *param_names, halt_code: 555, message_scope: nil)
228
+
229
+ assert_equal 555, code
230
+ assert_not_nil headers
231
+ assert_empty headers
232
+ assert_match /invalid search specifier: cannot specify 'field-search' and 'quick-search' together/, body_lines[0]
233
+ end
234
+ end
235
+
236
+
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # ######################################################################## #
4
+ #
5
+ # Copyright (c) 2019 Razor Risk Technologies Pty Limited. All rights reserved.
6
+ #
7
+ # ######################################################################## #
8
+
9
+ $:.unshift File.join(File.dirname(__FILE__), *(['..'] * 4), 'lib')
10
+
11
+ require 'simplecov'
12
+
13
+ unless $DEBUG
14
+
15
+ require 'pantheios/globals'
16
+ require 'pantheios/services/null_log_service'
17
+
18
+ ::Pantheios::Globals.INITIAL_SERVICE_CLASSES = [ ::Pantheios::Services::NullLogService ]
19
+ end
20
+
21
+ # ##########################################################
22
+ # requires
23
+
24
+ require 'razor_risk/cassini/applications/rest_framework/verb_handler'
25
+
26
+ require 'xqsr3/extensions/test/unit'
27
+ require 'test/unit'
28
+
29
+ # ##########################################################
30
+ # tests
31
+
32
+ class Test_VerbHandler < Test::Unit::TestCase
33
+
34
+ include ::RazorRisk::Cassini::Applications::RESTFramework
35
+
36
+ App = Struct.new(:credentials)
37
+ Settings = Struct.new(:authentication_scheme, :auth_test_mode)
38
+
39
+ def test_get_required_credentials_with_basic
40
+
41
+ username = 'name'
42
+ password = 'pass'
43
+ domain = 'dom'
44
+ app = App.new([
45
+ username,
46
+ password,
47
+ domain,
48
+ ])
49
+ settings = Settings.new(:basic)
50
+ vh = VerbHandler.new(app, settings)
51
+
52
+ cr = vh.get_required_credentials
53
+
54
+ assert_kind_of Hash, cr
55
+ assert_equal username, cr[:username]
56
+ assert_equal password, cr[:password]
57
+ assert_equal domain, cr[:domain]
58
+ assert_nil cr[:impersonatee]
59
+ end
60
+
61
+ def test_get_required_credentials_with_basic_in_auth_test_mode
62
+
63
+ username = 'name'
64
+ password = 'pass'
65
+ domain = 'dom'
66
+ app = App.new([
67
+ username,
68
+ password,
69
+ domain,
70
+ ])
71
+ settings = Settings.new(:basic, true)
72
+ vh = VerbHandler.new(app, settings)
73
+
74
+ cr = vh.get_required_credentials
75
+
76
+ assert_kind_of Hash, cr
77
+ assert_nil cr[:username]
78
+ assert_nil cr[:password]
79
+ assert_nil cr[:domain]
80
+ assert_equal username, cr[:impersonatee]
81
+ end
82
+
83
+ def test_get_required_credentials_with_jwt
84
+
85
+ user_id = 'name'
86
+ user_name = 'Name'
87
+ session_id = 'session'
88
+ app = App.new([
89
+ session_id,
90
+ user_id,
91
+ user_name,
92
+ ])
93
+ settings = Settings.new(:jwt)
94
+ vh = VerbHandler.new(app, settings)
95
+
96
+ cr = vh.get_required_credentials
97
+
98
+ assert_kind_of Hash, cr
99
+ assert_equal session_id, cr[:session_id]
100
+ assert_equal user_id, cr[:user_id]
101
+ assert_equal user_name, cr[:user_name]
102
+ assert_nil cr[:impersonatee]
103
+ end
104
+
105
+ def test_get_required_credentials_with_jwt_in_auth_test_mode
106
+
107
+ user_id = 'name'
108
+ user_name = 'Name'
109
+ session_id = 'session'
110
+ app = App.new([
111
+ session_id,
112
+ user_id,
113
+ user_name,
114
+ ])
115
+ settings = Settings.new(:jwt)
116
+ vh = VerbHandler.new(app, settings)
117
+
118
+ cr = vh.get_required_credentials
119
+
120
+ assert_kind_of Hash, cr
121
+ assert_equal session_id, cr[:session_id]
122
+ assert_equal user_id, cr[:user_id]
123
+ assert_equal user_name, cr[:user_name]
124
+ assert_nil cr[:impersonatee]
125
+ end
126
+ end
127
+
128
+ # ############################## end of file ############################# #
129
+
130
+
@@ -0,0 +1,328 @@
1
+ # encoding: utf-8
2
+
3
+ # ##########################################################################
4
+ #
5
+ # Copyright (c) 2019 Razor Risk Technologies Pty Limited. All rights reserved.
6
+ #
7
+ # ##########################################################################
8
+
9
+ # ##########################################################
10
+ # load path
11
+
12
+ $:.unshift File.join(File.dirname(__FILE__), *(['..'] * 3), 'lib')
13
+
14
+
15
+ # ##########################################################
16
+ # diagnostics
17
+
18
+ require 'razor_risk/cassini/testing/suppress_pantheios_logging' unless $DEBUG
19
+
20
+
21
+ # ##########################################################
22
+ # code coverage
23
+
24
+ require 'simplecov'
25
+
26
+
27
+ # ##########################################################
28
+ # requires
29
+
30
+ require 'razor_risk/cassini/mixin/razor_response_validator'
31
+
32
+ require 'xqsr3/extensions/test/unit'
33
+
34
+ require 'test/unit'
35
+
36
+
37
+ # ##########################################################
38
+ # tests
39
+
40
+ class Test_RazorResponseValidator < Test::Unit::TestCase
41
+
42
+ include ::RazorRisk::Cassini::Mixin
43
+
44
+ Reason = Struct.new(:code, :message, :details)
45
+ Reasons = Struct.new(:reasons) do
46
+ def lookup? code
47
+ reasons.select { |r| r.code == code }.first
48
+ end
49
+ def map &block
50
+ reasons.map(&block)
51
+ end
52
+ def empty?
53
+ reasons.empty?
54
+ end
55
+ end
56
+ FailureQualifier = Struct.new(:reasons)
57
+ QualifiedResult = Struct.new(:succeeded?, :failure_qualifier)
58
+
59
+ def test_is_a_module
60
+
61
+ assert_kind_of Module, RazorResponseValidator
62
+ end
63
+
64
+ def test_has_required_methods
65
+
66
+ assert_respond_to(
67
+ RazorResponseValidator,
68
+ :validate_qualified_razor_response
69
+ )
70
+ end
71
+
72
+ def test_can_be_called_on_module
73
+
74
+ assert_nothing_raised do
75
+ RazorResponseValidator.validate_qualified_razor_response nil
76
+ end
77
+ end
78
+
79
+ def test_can_be_called_when_included
80
+
81
+ assert_nothing_raised do
82
+
83
+ app = Struct.new(:dummy) do
84
+ include RazorResponseValidator
85
+ end.new
86
+
87
+ app.validate_qualified_razor_response nil
88
+ end
89
+ end
90
+
91
+ def test_adds_custom_error_handler
92
+
93
+ app_class = Struct.new(:dummy) do
94
+
95
+ @error_handler_called = false
96
+
97
+ def self.error_handler_called
98
+ @error_handler_called
99
+ end
100
+
101
+ def self.error *args
102
+ @error_handler_called = true
103
+ end
104
+ end
105
+
106
+ assert_false app_class.error_handler_called
107
+ app_class.include RazorResponseValidator
108
+ assert_true app_class.error_handler_called
109
+ end
110
+
111
+ def test_validates_RZ0002
112
+
113
+ qr = QualifiedResult.new(
114
+ false,
115
+ FailureQualifier.new(
116
+ Reasons.new([
117
+ Reason.new('RZ0002', 'test message', 'test details'),
118
+ Reason.new('RZ0008', 'anther test message', 'another test details'),
119
+ ])
120
+ )
121
+ )
122
+
123
+ exp = RazorResponseValidator::RazorErrorException.new(
124
+ 500,
125
+ 'test message: test details',
126
+ )
127
+
128
+ begin
129
+ RazorResponseValidator.validate_qualified_razor_response qr
130
+ rescue => x
131
+ assert_equal exp.message, x.message
132
+ assert_equal exp.http_status, x.http_status
133
+ end
134
+ end
135
+
136
+ def test_validates_RZ0007
137
+
138
+ qr = QualifiedResult.new(
139
+ false,
140
+ FailureQualifier.new(
141
+ Reasons.new([
142
+ Reason.new('RZ0007', 'test message', 'test details')
143
+ ])
144
+ )
145
+ )
146
+
147
+ exp = RazorResponseValidator::RazorErrorException.new(
148
+ 401,
149
+ 'Invalid credentials',
150
+ )
151
+
152
+ begin
153
+ RazorResponseValidator.validate_qualified_razor_response qr
154
+ rescue => x
155
+ assert_equal exp.message, x.message
156
+ assert_equal exp.http_status, x.http_status
157
+ end
158
+ end
159
+
160
+ def test_validates_RZ0008
161
+
162
+ qr = QualifiedResult.new(
163
+ false,
164
+ FailureQualifier.new(
165
+ Reasons.new([
166
+ Reason.new('RZ0008', 'anther test message', 'another test details'),
167
+ ])
168
+ )
169
+ )
170
+
171
+ exp = RazorResponseValidator::RazorErrorException.new(
172
+ 500,
173
+ 'Oops! Something went wrong!',
174
+ )
175
+
176
+ begin
177
+ RazorResponseValidator.validate_qualified_razor_response qr
178
+ rescue => x
179
+ assert_equal exp.message, x.message
180
+ assert_equal exp.http_status, x.http_status
181
+ end
182
+ end
183
+
184
+ def test_validates_RZ0009
185
+
186
+ qr = QualifiedResult.new(
187
+ false,
188
+ FailureQualifier.new(
189
+ Reasons.new([
190
+ Reason.new('RZ0009', 'test message', 'test details')
191
+ ])
192
+ )
193
+ )
194
+
195
+ exp = RazorResponseValidator::RazorErrorException.new(
196
+ 401,
197
+ 'Invalid credentials',
198
+ )
199
+
200
+ begin
201
+ RazorResponseValidator.validate_qualified_razor_response qr
202
+ rescue => x
203
+ assert_equal exp.message, x.message
204
+ assert_equal exp.http_status, x.http_status
205
+ end
206
+ end
207
+
208
+ def test_validates_RZ0010
209
+
210
+ qr = QualifiedResult.new(
211
+ false,
212
+ FailureQualifier.new(
213
+ Reasons.new([
214
+ Reason.new('RZ0010', 'test message', 'test details'),
215
+ ])
216
+ )
217
+ )
218
+
219
+ exp = RazorResponseValidator::RazorErrorException.new(
220
+ 400,
221
+ 'test message: test details',
222
+ )
223
+
224
+ begin
225
+ RazorResponseValidator.validate_qualified_razor_response qr
226
+ rescue => x
227
+ assert_equal exp.message, x.message
228
+ assert_equal exp.http_status, x.http_status
229
+ end
230
+ end
231
+
232
+ def test_validates_RZ0011
233
+
234
+ qr = QualifiedResult.new(
235
+ false,
236
+ FailureQualifier.new(
237
+ Reasons.new([
238
+ Reason.new('RZ0011', 'test message', 'test details')
239
+ ])
240
+ )
241
+ )
242
+
243
+ exp = RazorResponseValidator::RazorErrorException.new(
244
+ 404,
245
+ 'test message: test details',
246
+ )
247
+
248
+ begin
249
+ RazorResponseValidator.validate_qualified_razor_response qr
250
+ rescue => x
251
+ assert_equal exp.message, x.message
252
+ assert_equal exp.http_status, x.http_status
253
+ end
254
+ end
255
+
256
+ def test_validates_unsupported_error
257
+
258
+ qr = QualifiedResult.new(
259
+ false,
260
+ FailureQualifier.new(
261
+ Reasons.new([
262
+ Reason.new('RZ0015', 'anther test message', 'another test details'),
263
+ ])
264
+ )
265
+ )
266
+
267
+ exp = RazorResponseValidator::RazorErrorException.new(
268
+ 500,
269
+ 'Oops! Something went wrong!',
270
+ )
271
+
272
+ begin
273
+ RazorResponseValidator.validate_qualified_razor_response qr
274
+ rescue => x
275
+ assert_equal exp.message, x.message
276
+ assert_equal exp.http_status, x.http_status
277
+ end
278
+ end
279
+
280
+ def test_handles_erros_in_correct_priority
281
+
282
+ reasons = [
283
+ Reason.new('RZ0007', 'RZ0007', 'RZ0007'),
284
+ Reason.new('RZ0009', 'RZ0009', 'RZ0009'),
285
+ Reason.new('RZ0002', 'RZ0002', 'RZ0002'),
286
+ Reason.new('RZ0011', 'RZ0011', 'RZ0011'),
287
+ Reason.new('RZ0010', 'RZ0010', 'RZ0010'),
288
+ Reason.new('RZ0008', 'RZ0008', 'RZ0008'),
289
+ Reason.new('RZ0015', 'RZ0015', 'RZ0015'),
290
+ ]
291
+
292
+ exp = [
293
+ [ 'RZ0007', 401, 'Invalid credentials' ],
294
+ [ 'RZ0009', 401, 'Invalid credentials' ],
295
+ [ 'RZ0002', 500, 'RZ0002: RZ0002' ],
296
+ [ 'RZ0011', 404, 'RZ0011: RZ0011' ],
297
+ [ 'RZ0010', 400, 'RZ0010: RZ0010' ],
298
+ [ 'RZ0008', 500, 'Oops! Something went wrong!' ],
299
+ [ 'RZ0015', 500, 'Oops! Something went wrong!' ],
300
+ ]
301
+
302
+ exp.each do |error, code, message|
303
+ qr = QualifiedResult.new(
304
+ false,
305
+ FailureQualifier.new(Reasons.new(reasons))
306
+ )
307
+ begin
308
+ RazorResponseValidator.validate_qualified_razor_response qr
309
+ rescue => x
310
+ assert_equal(
311
+ code, x.http_status,
312
+ "Expected #{error} to be handled"
313
+ )
314
+ assert_equal(
315
+ message, x.message,
316
+ "Expected #{error} to be handled"
317
+ )
318
+ end
319
+
320
+ reasons.shift
321
+ end
322
+ end
323
+ end
324
+
325
+
326
+ # ############################## end of file ############################# #
327
+
328
+