inferno_core 1.1.0 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 11e324b2bb99be1f0100e0b37829fa92668feab98fe2424f301a73ca70a82542
4
- data.tar.gz: 074fce8e9975dad0acebad6276c029016ffb219dcedc4e056970f8cadc7e63f6
3
+ metadata.gz: cc9addf71b6ea91eb5aa426eabe24df9a2cbdb3199271e3f60d8e66f1cacd8cc
4
+ data.tar.gz: 16fd0eaf84e1c2a391ba90efcedbeae1a24f82ad45dcc9b7a8b5e2365ed3d53d
5
5
  SHA512:
6
- metadata.gz: 303453ddfa0ecaedf8f68359f2cc90041372f74736917e3313bcde9c1f9c90edae4b271cbd422f3e875e373e32177a9ab814d12e65fa5214c13a4262b71e5d27
7
- data.tar.gz: 06eac23eb184c16cce19b5a6dceac53d73f87bd77baea45c8f138a384414cf683aff7ea9a5233cc254a8effdce3144b5b9bcee18468110977a6cbe43be445c93
6
+ metadata.gz: 0eed6f34c2f0cfd4a78f12d7dc4ce853480520d740e4af6da052ec4b4eb2d3551ff4208e99516990438a957dcccf8a0ad38f01aaae34224b5286086fb88d4095
7
+ data.tar.gz: 0fc841b4d2ad71f098061277d9041caa6633e36679276ca6db2db8553c22a383d63c950196aacb661b9175b398aa8e80da5f5e8cd9b90d377019afb8b14d04b9
@@ -21,6 +21,7 @@ module Inferno
21
21
  field :input_instructions
22
22
  field :user_runnable?, name: :user_runnable
23
23
  field :optional?, name: :optional
24
+ field :simulation_verification?, name: :is_simulation_verification
24
25
  field :verifies_requirements, if: :field_present?, extractor: RequirementsFilteringExtractor
25
26
  end
26
27
  end
@@ -1,4 +1,5 @@
1
1
  require_relative '../exceptions'
2
+ require_relative 'messages'
2
3
 
3
4
  module Inferno
4
5
  module DSL
@@ -7,6 +8,7 @@ module Inferno
7
8
  # immediately stop execution and receive a `fail` result. Additional
8
9
  # assertions added to this module will be available in all tests.
9
10
  module Assertions
11
+ include Messages
10
12
  # Make an assertion
11
13
  #
12
14
  # @param test a value whose truthiness will determine whether the
@@ -59,6 +61,7 @@ module Inferno
59
61
  # @private
60
62
  def invalid_resource_message(resource, profile_url)
61
63
  return "Resource does not conform to the profile: #{profile_url}" if profile_url.present?
64
+ return 'No resource to validate.' unless resource.present?
62
65
 
63
66
  "Resource does not conform to the base #{resource&.resourceType} profile."
64
67
  end
@@ -70,12 +73,31 @@ module Inferno
70
73
  # may include a version separated by a vertical bar (|),
71
74
  # and defaults to validating against the base FHIR resource type
72
75
  # @param validator [Symbol] the name of the validator to use
76
+ # @param message_prefix [String] Prefix to add to the start of logged messages
73
77
  # @return [void]
74
- def assert_valid_resource(resource: self.resource, profile_url: nil, validator: :default)
75
- assert resource_is_valid?(resource:, profile_url:, validator:),
78
+ def assert_valid_resource(resource: self.resource, profile_url: nil, validator: :default, message_prefix: '')
79
+ assert resource_is_valid?(resource:, profile_url:, validator:, message_prefix:),
76
80
  invalid_resource_message(resource, profile_url)
77
81
  end
78
82
 
83
+ # @private
84
+ def invalid_object_message(model_url)
85
+ "Object does not conform to the logical model: #{model_url}"
86
+ end
87
+
88
+ # Validate an object against a logical model
89
+ #
90
+ # @param object [Hash]
91
+ # @param model_url [String] canonical url of the model to validate against,
92
+ # may include a version separated by a vertical bar (|),
93
+ # @param validator [Symbol] the name of the validator to use
94
+ # @param message_prefix [String] Prefix to add to the start of logged messages
95
+ # @return [void]
96
+ def assert_conformance_to_logical_model(object, model_url, validator: :default, message_prefix: '')
97
+ assert conforms_to_logical_model?(object, model_url, validator:, message_prefix:),
98
+ invalid_object_message(model_url)
99
+ end
100
+
79
101
  # Validate each entry of a Bundle
80
102
  #
81
103
  # @param bundle [FHIR::Bundle]
@@ -87,6 +109,7 @@ module Inferno
87
109
  # types as keys and profile urls (or nil) as values, only those resource
88
110
  # types will be validated against the provided profile url or the base
89
111
  # resource if nil.
112
+ # @param message_prefix [String] Prefix to add to the start of logged messages
90
113
  # @example
91
114
  # # Only validate Patient bundle entries
92
115
  # assert_valid_bundle_entries(resource_types: 'Patient')
@@ -104,7 +127,7 @@ module Inferno
104
127
  # }
105
128
  # )
106
129
  # @return [void]
107
- def assert_valid_bundle_entries(bundle: resource, resource_types: {})
130
+ def assert_valid_bundle_entries(bundle: resource, resource_types: {}, message_prefix: '')
108
131
  assert_resource_type('Bundle', resource: bundle)
109
132
 
110
133
  types_to_check = normalize_types_to_check(resource_types)
@@ -115,7 +138,7 @@ module Inferno
115
138
  .map(&:resource)
116
139
  .select { |resource| types_to_check.empty? || types_to_check.include?(resource.resourceType) }
117
140
  .reject do |resource|
118
- validation_params = { resource: }
141
+ validation_params = { resource:, message_prefix: }
119
142
  profile = types_to_check[resource.resourceType]
120
143
  validation_params[:profile_url] = profile if profile
121
144
 
@@ -161,9 +184,25 @@ module Inferno
161
184
  # @param message [String] extra failure message
162
185
  # @return [void]
163
186
  def assert_valid_json(maybe_json_string, message = '')
164
- assert JSON.parse(maybe_json_string)
187
+ parsed_json_if_valid(maybe_json_string, message, continue: false)
188
+ end
189
+
190
+ # Return parsed json Hash if valid, or indicate an error with an error message or a failed assert
191
+ #
192
+ # @param maybe_json_string [String]
193
+ # @param message [String] extra failure message
194
+ # @param continue [Boolean] if true will log an error message and continue,
195
+ # otherwise will raise an assert exception
196
+ # @return [void]
197
+ def parsed_json_if_valid(maybe_json_string, message = '', continue: true)
198
+ JSON.parse(maybe_json_string)
165
199
  rescue JSON::ParserError
166
- assert false, "Invalid JSON. #{message}"
200
+ if continue
201
+ add_message(:error, "Invalid JSON. #{message}")
202
+ nil
203
+ else
204
+ assert false, "Invalid JSON. #{message}"
205
+ end
167
206
  end
168
207
 
169
208
  # Check for a valid http/https uri
@@ -224,6 +263,16 @@ module Inferno
224
263
  "Could not find #{missing_elements.join(', ')} in the #{resources.length} " \
225
264
  'provided resource(s)'
226
265
  end
266
+
267
+ # Check that there are no messages associated with the current runnable with a type of 'error'
268
+ #
269
+ # @param message [String] failure message
270
+ # @param message_list [Array] (optional) list of messages to check for errors,
271
+ # if different from the runnable's messages
272
+ # @return [void]
273
+ def assert_no_error_messages(message = '', message_list: messages)
274
+ assert !error_messages?(message_list:), message.present? ? message : 'Errors found - see Messages for details.'
275
+ end
227
276
  end
228
277
  end
229
278
  end
@@ -1,6 +1,7 @@
1
1
  require_relative 'fhir_client_builder'
2
2
  require_relative 'request_storage'
3
3
  require_relative 'tcp_exception_handler'
4
+ require_relative 'messages'
4
5
 
5
6
  module Inferno
6
7
  module DSL
@@ -37,6 +38,8 @@ module Inferno
37
38
  # @see Inferno::DSL::FHIRClientBuilder Documentation for the client
38
39
  # configuration DSL
39
40
  module FHIRClient
41
+ include Messages
42
+
40
43
  # @private
41
44
  def self.included(klass)
42
45
  klass.extend ClassMethods
@@ -1,5 +1,6 @@
1
1
  require_relative '../ext/fhir_models'
2
2
  require_relative '../feature'
3
+ require_relative '../exceptions'
3
4
  module Inferno
4
5
  module DSL
5
6
  # This module contains the methods needed to configure a validator to
@@ -159,6 +160,37 @@ module Inferno
159
160
  @exclude_message
160
161
  end
161
162
 
163
+ # Validate a FHIR resource and determine if it's valid.
164
+ # Adds validation messages to the runnable if add_messages_to_runnable is true.
165
+ def conforms_to_logical_model?(object, model_url, runnable, add_messages_to_runnable: true,
166
+ message_prefix: '', validator_response_details: nil)
167
+
168
+ unless model_url.present?
169
+ raise Inferno::Exceptions::TestSuiteImplementationException.new(
170
+ 'Logical Model Validation',
171
+ 'The profile of the logical model must be provided.'
172
+ )
173
+ end
174
+
175
+ unless object.present?
176
+ if add_messages_to_runnable
177
+ runnable.add_message(:error,
178
+ "#{message_prefix}No object to check for conformance.")
179
+ end
180
+ return false
181
+ end
182
+
183
+ unless object.is_a?(Hash)
184
+ raise Inferno::Exceptions::TestSuiteImplementationException.new(
185
+ 'Logical Model Validation',
186
+ "Expected a Hash, got a #{object.class}."
187
+ )
188
+ end
189
+
190
+ conformant?(object, model_url, runnable, add_messages_to_runnable:, message_prefix:,
191
+ validator_response_details:)
192
+ end
193
+
162
194
  # Validate a FHIR resource and determine if it's valid.
163
195
  # Adds validation messages to the runnable if add_messages_to_runnable is true.
164
196
  #
@@ -167,29 +199,51 @@ module Inferno
167
199
  # @param profile_url [String] the profile URL to validate against
168
200
  # @param runnable [Object] the runnable context (test/group/suite)
169
201
  # @param add_messages_to_runnable [Boolean] whether to add messages to the runnable
202
+ # @param message_prefix [String] Prefix to add to the start of logged messages
170
203
  # @param validator_response_details [Array, nil] if not nil, the service will populate this array with
171
204
  # the detailed response message from the validator service. Can be used by test kits to perform custom
172
205
  # handling of error messages.
173
206
  # @return [Boolean] true if the resource is valid
174
207
  def resource_is_valid?(resource, profile_url, runnable, add_messages_to_runnable: true,
175
- validator_response_details: nil)
208
+ message_prefix: '', validator_response_details: nil)
209
+
210
+ unless resource.present?
211
+ runnable.add_message(:error, "#{message_prefix}No resource to validate.") if add_messages_to_runnable
212
+ return false
213
+ end
214
+
215
+ unless resource.is_a?(FHIR::Model)
216
+ raise Inferno::Exceptions::TestSuiteImplementationException.new(
217
+ 'FHIR Resource Validation',
218
+ "Expected a FHIR::Model, got a #{resource.class}."
219
+ )
220
+
221
+ end
176
222
  profile_url ||= FHIR::Definitions.resource_definition(resource.resourceType).url
177
223
 
224
+ conformant?(resource, profile_url, runnable, add_messages_to_runnable:, message_prefix:,
225
+ validator_response_details:)
226
+ end
227
+
228
+ # @private
229
+ def conformant?(target, profile_url, runnable, add_messages_to_runnable: true,
230
+ message_prefix: '', validator_response_details: nil)
231
+
178
232
  # 1. Get raw content from validator
179
- response = get_raw_validator_content(resource, profile_url, runnable)
233
+ response = get_raw_validator_content(target, profile_url, runnable)
180
234
 
181
235
  # 2. Convert to validation issues
182
- issues = get_issues_from_validator_response(response, resource)
236
+ issues = get_issues_from_validator_response(response, target)
183
237
 
184
238
  # 3. Add additional validation messages
185
- issues = join_additional_validation_messages(issues, resource, profile_url)
239
+ issues = join_additional_validation_messages(issues, target, profile_url)
186
240
 
187
241
  # 4. Mark resources as filtered
188
242
  mark_issues_for_filtering(issues)
189
243
 
190
244
  # 5. Add error messages to runnable
191
245
  filtered_issues = issues.reject(&:filtered)
192
- add_validation_messages_to_runnable(runnable, filtered_issues) if add_messages_to_runnable
246
+ add_validation_messages_to_runnable(runnable, filtered_issues, message_prefix:) if add_messages_to_runnable
193
247
  validator_response_details&.concat(issues)
194
248
 
195
249
  # 6. Return validity
@@ -204,12 +258,12 @@ module Inferno
204
258
 
205
259
  # @private
206
260
  # Gets raw content from validator including error handling
207
- # @param resource [FHIR::Model] the resource to validate
261
+ # @param target [FHIR::Model, Hash] the object to validate
208
262
  # @param profile_url [String] the profile URL to validate against
209
263
  # @param runnable [Object] the runnable context
210
264
  # @return [Faraday::Response] the HTTP response from the validator
211
- def get_raw_validator_content(resource, profile_url, runnable)
212
- response = call_validator(resource, profile_url)
265
+ def get_raw_validator_content(target, profile_url, runnable)
266
+ response = call_validator(target, profile_url)
213
267
 
214
268
  unless response.status == 200
215
269
  raise Inferno::Exceptions::ErrorInValidatorException,
@@ -225,19 +279,19 @@ module Inferno
225
279
 
226
280
  # @private
227
281
  # Adds validation messages to the runnable
228
- def add_validation_messages_to_runnable(runnable, filtered_issues)
282
+ def add_validation_messages_to_runnable(runnable, filtered_issues, message_prefix: '')
229
283
  filtered_issues.each do |issue|
230
- runnable.add_message(issue.severity, issue.message)
284
+ runnable.add_message(issue.severity, "#{message_prefix}#{issue.message}")
231
285
  end
232
286
  end
233
287
 
234
288
  # Warm up the validator session by sending a test validation request.
235
289
  # This initializes the validator session and persists it for future use.
236
290
  #
237
- # @param resource [FHIR::Model] the resource to validate
291
+ # @param target [FHIR::Model, Hash] the object to validate
238
292
  # @param profile_url [String] the profile URL to validate against
239
- def warm_up(resource, profile_url)
240
- response_body = validate(resource, profile_url)
293
+ def warm_up(target, profile_url)
294
+ response_body = validate(target, profile_url)
241
295
  res = JSON.parse(response_body)
242
296
  session_id = res['sessionId']
243
297
  validator_session_repo.save(test_suite_id:, validator_session_id: session_id,
@@ -253,9 +307,9 @@ module Inferno
253
307
  # Recursively processes slice information.
254
308
  #
255
309
  # @param response [Faraday::Response] the HTTP response from the validator
256
- # @param resource [FHIR::Model] the resource being validated
310
+ # @param target [FHIR::Model, Hash] the object being validated
257
311
  # @return [Array<ValidatorIssue>] list of validator issues
258
- def get_issues_from_validator_response(response, resource)
312
+ def get_issues_from_validator_response(response, target)
259
313
  response_body = remove_invalid_characters(response.body)
260
314
  response_hash = JSON.parse(response_body)
261
315
 
@@ -268,7 +322,7 @@ module Inferno
268
322
  raw_issues = response_hash.dig('outcomes', 0, 'issues') || []
269
323
 
270
324
  raw_issues.map do |raw_issue|
271
- convert_raw_issue_to_validator_issue(raw_issue, resource)
325
+ convert_raw_issue_to_validator_issue(raw_issue, target)
272
326
  end
273
327
  end
274
328
 
@@ -277,28 +331,28 @@ module Inferno
277
331
  # Recursively processes sliceInfo if present.
278
332
  #
279
333
  # @param raw_issue [Hash] the raw issue from validator response
280
- # @param resource [FHIR::Model] the resource being validated
334
+ # @param target [FHIR::Model, Hash] the object being validated
281
335
  # @return [ValidatorIssue] the converted validator issue
282
- def convert_raw_issue_to_validator_issue(raw_issue, resource)
336
+ def convert_raw_issue_to_validator_issue(raw_issue, target)
283
337
  # Recursively process sliceInfo
284
338
  slice_info = []
285
339
  if raw_issue['sliceInfo']&.any?
286
340
  slice_info = raw_issue['sliceInfo'].map do |slice_issue|
287
- convert_raw_issue_to_validator_issue(slice_issue, resource)
341
+ convert_raw_issue_to_validator_issue(slice_issue, target)
288
342
  end
289
343
  end
290
344
 
291
345
  ValidatorIssue.new(
292
346
  raw_issue: raw_issue,
293
- resource: resource,
347
+ target: target,
294
348
  slice_info: slice_info,
295
349
  filtered: false
296
350
  )
297
351
  end
298
352
 
299
353
  # @private
300
- def call_validator(resource, profile_url)
301
- request_body = wrap_resource_for_hl7_wrapper(resource, profile_url)
354
+ def call_validator(target, profile_url)
355
+ request_body = wrap_target_for_hl7_wrapper(target, profile_url)
302
356
  Faraday.new(
303
357
  url,
304
358
  request: { timeout: 600 }
@@ -306,14 +360,14 @@ module Inferno
306
360
  end
307
361
 
308
362
  # @private
309
- # Post a resource to the validation service for validating.
363
+ # Post an object to the validation service for validating.
310
364
  # Returns the raw validator response body.
311
365
  #
312
- # @param resource [FHIR::Model]
366
+ # @param target [FHIR::Model, Hash]
313
367
  # @param profile_url [String]
314
368
  # @return [String] the body of the validation response
315
- def validate(resource, profile_url)
316
- call_validator(resource, profile_url).body
369
+ def validate(target, profile_url)
370
+ call_validator(target, profile_url).body
317
371
  end
318
372
 
319
373
  # Add a specific error message for specific network problems to help the user
@@ -351,11 +405,11 @@ module Inferno
351
405
  # Joins additional validation messages to the issues list
352
406
  #
353
407
  # @param issues [Array<ValidatorIssue>] the list of validator issues
354
- # @param resource [FHIR::Model] the resource being validated
408
+ # @param target [FHIR::Model] the object being validated
355
409
  # @param profile_url [String] the profile URL being validated against
356
410
  # @return [Array<ValidatorIssue>] the complete list of issues including additional messages
357
- def join_additional_validation_messages(issues, resource, profile_url)
358
- additional_issues = additional_validation_messages(resource, profile_url)
411
+ def join_additional_validation_messages(issues, target, profile_url)
412
+ additional_issues = additional_validation_messages(target, profile_url)
359
413
  issues + additional_issues
360
414
  end
361
415
 
@@ -376,12 +430,12 @@ module Inferno
376
430
  # Gets additional validation messages from custom validation blocks.
377
431
  # Converts the message hashes to ValidatorIssue objects.
378
432
  #
379
- # @param resource [FHIR::Model] the resource being validated
433
+ # @param target [FHIR::Model, Hash] the object being validated
380
434
  # @param profile_url [String] the profile URL being validated against
381
435
  # @return [Array<ValidatorIssue>] list of additional validator issues
382
- def additional_validation_messages(resource, profile_url)
436
+ def additional_validation_messages(target, profile_url)
383
437
  additional_validations
384
- .flat_map { |step| step.call(resource, profile_url) }
438
+ .flat_map { |step| step.call(target, profile_url) }
385
439
  .select { |message| message.is_a? Hash }
386
440
  .map do |message_hash|
387
441
  # Create a synthetic raw_issue for additional validation messages
@@ -392,7 +446,7 @@ module Inferno
392
446
  }
393
447
  ValidatorIssue.new(
394
448
  raw_issue: synthetic_raw_issue,
395
- resource: resource,
449
+ target: target,
396
450
  slice_info: [],
397
451
  filtered: false
398
452
  )
@@ -536,7 +590,7 @@ module Inferno
536
590
  end
537
591
 
538
592
  # @private
539
- def wrap_resource_for_hl7_wrapper(resource, profile_url)
593
+ def wrap_target_for_hl7_wrapper(target, profile_url)
540
594
  validator_session_id =
541
595
  validator_session_repo.find_validator_session_id(test_suite_id,
542
596
  name.to_s, requirements)
@@ -547,6 +601,13 @@ module Inferno
547
601
  # This allows backward compatibility until the validator-wrapper is updated.
548
602
  context_key = Feature.use_validation_context_key? ? :validationContext : :cliContext
549
603
 
604
+ file_contents =
605
+ if target.is_a?(Hash)
606
+ target.to_json
607
+ else
608
+ target.source_contents
609
+ end
610
+
550
611
  wrapped_resource = {
551
612
  context_key => {
552
613
  **validation_context.definition,
@@ -554,8 +615,8 @@ module Inferno
554
615
  },
555
616
  filesToValidate: [
556
617
  {
557
- fileName: "#{resource.resourceType}/#{resource.id}.json",
558
- fileContent: resource.source_contents,
618
+ fileName: "#{profile_url.split('/').last}.json",
619
+ fileContent: file_contents,
559
620
  fileType: 'json'
560
621
  }
561
622
  ],
@@ -598,16 +659,16 @@ module Inferno
598
659
  # ValidatorIssue represents a single validation issue returned from the FHIR validator
599
660
  class ValidatorIssue
600
661
  attr_accessor :filtered, :raw_issue, :slice_info
601
- attr_reader :resource
662
+ attr_reader :target
602
663
 
603
664
  # Creates a new ValidatorIssue
604
665
  # @param raw_issue [Hash] the raw issue hash from the validator response
605
- # @param resource [FHIR::Model] the resource being validated
666
+ # @param target [FHIR::Model, Hash] the object being validated
606
667
  # @param slice_info [Array<ValidatorIssue>] nested slice information as ValidatorIssue objects
607
668
  # @param filtered [Boolean] whether this issue has been filtered out
608
- def initialize(raw_issue:, resource:, slice_info: [], filtered: false)
669
+ def initialize(raw_issue:, target:, slice_info: [], filtered: false)
609
670
  @raw_issue = raw_issue
610
- @resource = resource
671
+ @target = target
611
672
  @slice_info = slice_info
612
673
  @filtered = filtered
613
674
  end
@@ -641,8 +702,15 @@ module Inferno
641
702
  # Don't add prefix for additional validation messages
642
703
  return details_text if location_value == 'additional_validation'
643
704
 
644
- location_prefix = resource.id ? "#{resource.resourceType}/#{resource.id}" : resource.resourceType
645
- "#{location_prefix}: #{location_value}: #{details_text}"
705
+ "#{location_prefix}#{location_value}: #{details_text}"
706
+ end
707
+
708
+ def location_prefix
709
+ if target.is_a?(Hash)
710
+ ''
711
+ else
712
+ "#{target.id ? "#{target.resourceType}/#{target.id}" : target.resourceType}: "
713
+ end
646
714
  end
647
715
 
648
716
  # Converts the validator's severity level to our standard format
@@ -1,4 +1,5 @@
1
1
  require_relative '../ext/fhir_models'
2
+ require_relative '../exceptions'
2
3
  module Inferno
3
4
  module DSL
4
5
  # This module contains the methods needed to configure a validator to
@@ -32,19 +33,42 @@ module Inferno
32
33
  # @param profile_url [String]
33
34
  # @param validator [Symbol] the name of the validator to use
34
35
  # @param add_messages_to_runnable [Boolean] whether to add validation messages to runnable or not
36
+ # @param message_prefix [String] Prefix to add to the start of logged messages
35
37
  # @param validator_response_details [Array, nil] if not nil, the service will populate this array with
36
38
  # the detailed response message from the validator service
37
39
  # @return [Boolean] whether the resource is valid
38
40
  def resource_is_valid?(
39
41
  resource: self.resource, profile_url: nil,
40
- validator: :default, add_messages_to_runnable: true,
42
+ validator: :default, add_messages_to_runnable: true, message_prefix: '',
41
43
  validator_response_details: nil
42
44
  )
43
45
  find_validator(validator).resource_is_valid?(resource, profile_url, self,
44
46
  add_messages_to_runnable:,
47
+ message_prefix:,
45
48
  validator_response_details:)
46
49
  end
47
50
 
51
+ # Perform validation, and add validation messages to the runnable
52
+ #
53
+ # @param object [Hash] json to verify against the model, as a Hash
54
+ # @param model_url [String]
55
+ # @param validator [Symbol] the name of the validator to use
56
+ # @param add_messages_to_runnable [Boolean] whether to add validation messages to runnable or not
57
+ # @param message_prefix [String] Prefix to add to the start of logged messages
58
+ # @param validator_response_details [Array, nil] if not nil, the service will populate this array with
59
+ # the detailed response message from the validator service
60
+ # @return [Boolean] whether the resource is valid
61
+ def conforms_to_logical_model?(
62
+ object, model_url,
63
+ validator: :default, add_messages_to_runnable: true, message_prefix: '',
64
+ validator_response_details: nil
65
+ )
66
+ find_validator(validator).conforms_to_logical_model?(object, model_url, self,
67
+ add_messages_to_runnable:,
68
+ message_prefix:,
69
+ validator_response_details:)
70
+ end
71
+
48
72
  # Find a particular validator. Looks through a runnable's parents up to
49
73
  # the suite to find a validator with a particular name
50
74
  def find_validator(validator_name)
@@ -122,10 +146,21 @@ module Inferno
122
146
  @exclude_message
123
147
  end
124
148
 
149
+ # rubocop:disable Lint/UnusedMethodArgument
150
+ def conforms_to_logical_model?(resource, profile_url, runnable, add_messages_to_runnable: true,
151
+ message_prefix: '', validator_response_details: nil)
152
+
153
+ raise Exceptions::TestSuiteImplementationException.new(
154
+ 'Logical Model Validation',
155
+ 'Logical model validation not implemented for legacy fhir_validation.'
156
+ )
157
+ end
158
+ # rubocop:enable Lint/UnusedMethodArgument
159
+
125
160
  # @see Inferno::DSL::FHIRValidation#resource_is_valid?
126
161
  # rubocop:disable Metrics/CyclomaticComplexity, Lint/UnusedMethodArgument
127
162
  def resource_is_valid?(resource, profile_url, runnable, add_messages_to_runnable: true,
128
- validator_response_details: nil)
163
+ message_prefix: '', validator_response_details: nil)
129
164
  profile_url ||= FHIR::Definitions.resource_definition(resource.resourceType).url
130
165
 
131
166
  begin
@@ -126,6 +126,34 @@ module Inferno
126
126
  end
127
127
  end
128
128
 
129
+ # Perform an HTTP PUT request
130
+ #
131
+ # @param url [String] if this request is using a defined client, this will
132
+ # be appended to the client's url. Must be an absolute url for requests
133
+ # made without a defined client
134
+ # @param body [String]
135
+ # @param client [Symbol]
136
+ # @param name [Symbol] Name for this request to allow it to be used by
137
+ # other tests
138
+ # @param headers [Hash] Input headers here
139
+ # @param tags [Array<String>] a list of tags to assign to the request
140
+ # @return [Inferno::Entities::Request]
141
+ def put(url = '', body: nil, client: :default, name: nil, headers: nil, tags: [])
142
+ store_request('outgoing', name:, tags:) do
143
+ tcp_exception_handler do
144
+ client = http_client(client)
145
+
146
+ if client
147
+ client.put(url, body, headers)
148
+ elsif url.match?(%r{\Ahttps?://})
149
+ connection.put(url, body, headers)
150
+ else
151
+ raise StandardError, 'Must use an absolute url or define an HTTP client with a base url'
152
+ end
153
+ end
154
+ end
155
+ end
156
+
129
157
  # Perform an HTTP DELETE request
130
158
  #
131
159
  # @param url [String] if this request is using a defined client, this will
@@ -1,4 +1,5 @@
1
1
  require_relative '../utils/markdown_formatter'
2
+ require_relative '../exceptions'
2
3
 
3
4
  module Inferno
4
5
  module DSL
@@ -10,6 +11,15 @@ module Inferno
10
11
  @messages ||= []
11
12
  end
12
13
 
14
+ # Returns true if an error message was logged to the runnable
15
+ #
16
+ # @param message_list [Array] (optional) list of messages to check for error,
17
+ # if not provided, the runnable's current list of messages will be checked
18
+ # @return [Boolean]
19
+ def error_messages?(message_list: messages)
20
+ message_list.any? { |msg| msg[:type] == 'error' }
21
+ end
22
+
13
23
  # Add a message to the result.
14
24
  #
15
25
  # @param type [String] error, warning, or info
@@ -309,6 +309,21 @@ module Inferno
309
309
  !optional?
310
310
  end
311
311
 
312
+ # Mark as simulation verification. Tests are not simulation verification by default.
313
+ #
314
+ # @param simulation_verification [Boolean]
315
+ # @return [void]
316
+ def simulation_verification(simulation_verification = true) # rubocop:disable Style/OptionalBooleanParameter
317
+ @is_simulation_verification = simulation_verification
318
+ end
319
+
320
+ # The test is simulation verification if true
321
+ #
322
+ # @return [Boolean]
323
+ def simulation_verification?
324
+ !!@is_simulation_verification
325
+ end
326
+
312
327
  # @private
313
328
  def default_id
314
329
  to_s
@@ -137,5 +137,12 @@ module Inferno
137
137
  super("Could not find a child with an ID ending in '#{id}' for '#{runnable}'.")
138
138
  end
139
139
  end
140
+
141
+ class TestSuiteImplementationException < StandardError
142
+ def initialize(feature, details)
143
+ super("This test suite incorrectly used the #{feature}. " \
144
+ "Contact the developer with the following details: #{details}")
145
+ end
146
+ end
140
147
  end
141
148
  end