diff --git a/.env b/.env index ff0a5eff..0d30fdc4 100644 --- a/.env +++ b/.env @@ -3,6 +3,13 @@ G10_VALIDATOR_URL=http://validator_service:4567 G10_FHIR_RESOURCE_VALIDATOR_URL=http://hl7_validator_service:3500 REDIS_URL=redis://redis:6379/0 +USE_HL7_RESOURCE_VALIDATOR=false +# To use the HL7 validator wrapper instead of the inferno validator wrapper, set the above to true, +# and uncomment the relevant settings in the following files: +# - docker-compose.yml -- "hl7_validator_service" section at the bottom +# - docker-compose.background.yml -- "hl7_validator_service" section at the bottom +# - nginx.background.conf -- "location /hl7validatorapi/" section at the bottom + # Full path to a custom JWKS json file, will use lib/onc_certification_g10_test_kit/bulk_data_jwks.json if left blank # G10_BULK_DATA_JWKS= diff --git a/config/nginx.background.conf b/config/nginx.background.conf index 629a4cf1..1eeee5f7 100644 --- a/config/nginx.background.conf +++ b/config/nginx.background.conf @@ -68,22 +68,7 @@ http { # proxy_pass http://fhir_validator_app; # } - # location /validatorapi/ { - # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - # proxy_set_header Host $http_host; - # proxy_set_header X-Forwarded-Proto $scheme; - # proxy_set_header X-Forwarded-Port $server_port; - # proxy_redirect off; - # proxy_set_header Connection ''; - # proxy_http_version 1.1; - # chunked_transfer_encoding off; - # proxy_buffering off; - # proxy_cache off; - - # proxy_pass http://validator_service:4567/; - # } - - location /hl7validatorapi/ { + location /validatorapi/ { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; @@ -94,9 +79,27 @@ http { chunked_transfer_encoding off; proxy_buffering off; proxy_cache off; - proxy_read_timeout 600s; - proxy_pass http://hl7_validator_service:3500/; + proxy_pass http://validator_service:4567/; } + + +# To enable the HL7 Validator Wrapper, both the section below and +# the section in docker-compose.background.yml need to be uncommented +# location /hl7validatorapi/ { +# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +# proxy_set_header Host $http_host; +# proxy_set_header X-Forwarded-Proto $scheme; +# proxy_set_header X-Forwarded-Port $server_port; +# proxy_redirect off; +# proxy_set_header Connection ''; +# proxy_http_version 1.1; +# chunked_transfer_encoding off; +# proxy_buffering off; +# proxy_cache off; +# proxy_read_timeout 600s; +# +# proxy_pass http://hl7_validator_service:3500/; +# } } } diff --git a/docker-compose.background.yml b/docker-compose.background.yml index e06e395c..6efd9a23 100644 --- a/docker-compose.background.yml +++ b/docker-compose.background.yml @@ -1,12 +1,12 @@ version: '3' services: - # validator_service: - # image: infernocommunity/fhir-validator-service:v2.3.2 - # environment: - # - DISABLE_TX= true - # - DISPLAY_ISSUES_ARE_WARNINGS=true - # volumes: - # - ./lib/onc_certification_g10_test_kit/igs:/home/igs + validator_service: + image: infernocommunity/fhir-validator-service:v2.3.2 + environment: + - DISABLE_TX= true + - DISPLAY_ISSUES_ARE_WARNINGS=true + volumes: + - ./lib/onc_certification_g10_test_kit/igs:/home/igs # fhir_validator_app: # image: infernocommunity/fhir-validator-app # depends_on: @@ -28,15 +28,15 @@ services: volumes: - ./data/redis:/data command: redis-server --appendonly yes - hl7_validator_service: - # image: markiantorno/validator-wrapper - # If running on the MITRE network, comment out the "image" line above - # and uncomment the "build" section below - build: - context: . - dockerfile: Dockerfile.fhir_resource_validator - volumes: - - ./lib/onc_certification_g10_test_kit/igs:/home/igs - # To let the service share your local FHIR package cache, - # uncomment the below line - # - ~/.fhir:/home/ktor/.fhir \ No newline at end of file + # hl7_validator_service: + # image: markiantorno/validator-wrapper + # # If running on the MITRE network, comment out the "image" line above + # # and uncomment the "build" section below + # # build: + # # context: . + # # dockerfile: Dockerfile.fhir_resource_validator + # volumes: + # - ./lib/onc_certification_g10_test_kit/igs:/home/igs + # # To let the service share your local FHIR package cache, + # # uncomment the below line + # # - ~/.fhir:/home/ktor/.fhir \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index ea9efd18..7c4f9159 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,8 +6,8 @@ services: volumes: - ./data:/opt/inferno/data depends_on: - - hl7_validator_service - # - validator_service + # - hl7_validator_service + - validator_service worker: build: context: ./ @@ -16,10 +16,10 @@ services: command: bundle exec sidekiq -r ./worker.rb depends_on: - redis - # validator_service: - # extends: - # file: docker-compose.background.yml - # service: validator_service + validator_service: + extends: + file: docker-compose.background.yml + service: validator_service # fhir_validator_app: # extends: # file: docker-compose.background.yml @@ -34,7 +34,7 @@ services: extends: file: docker-compose.background.yml service: redis - hl7_validator_service: - extends: - file: docker-compose.background.yml - service: hl7_validator_service \ No newline at end of file + # hl7_validator_service: + # extends: + # file: docker-compose.background.yml + # service: hl7_validator_service \ No newline at end of file diff --git a/lib/onc_certification_g10_test_kit.rb b/lib/onc_certification_g10_test_kit.rb index 826620d3..15d30093 100644 --- a/lib/onc_certification_g10_test_kit.rb +++ b/lib/onc_certification_g10_test_kit.rb @@ -63,7 +63,7 @@ class G10CertificationSuite < Inferno::TestSuite ].freeze ERROR_FILTERS = [ - /\A\S+: \S+: Unknown Code/, + /\A\S+: \S+: Unknown [Cc]ode/, /\A\S+: \S+: None of the codings provided are in the value set/, /\A\S+: \S+: The code provided \(\S*\) is not in the value set/, /\A\S+: \S+: The Coding provided \(\S*\) is not in the value set/, @@ -72,35 +72,39 @@ class G10CertificationSuite < Inferno::TestSuite /\A\S+: \S+: URL value '.*' does not resolve/ ].freeze - [ - G10Options::US_CORE_3_REQUIREMENT, - G10Options::US_CORE_4_REQUIREMENT, - G10Options::US_CORE_5_REQUIREMENT, - G10Options::US_CORE_6_REQUIREMENT + def self.setup_validator(us_core_version_requirement) # rubocop:disable Metrics/CyclomaticComplexity + validator_method = if Feature.use_hl7_resource_validator? + method(:fhir_resource_validator) + else + method(:validator) + end + + validator_method.call :default, required_suite_options: us_core_version_requirement do + if Feature.use_hl7_resource_validator? + url ENV.fetch('G10_FHIR_RESOURCE_VALIDATOR_URL', 'http://hl7_validator_service:3500') + + cli_context do + txServer nil + displayWarnings true + disableDefaultResourceFetcher true + end - ].each do |us_core_version_requirement| - fhir_resource_validator :default, required_suite_options: us_core_version_requirement do - url ENV.fetch('G10_FHIR_RESOURCE_VALIDATOR_URL', 'http://hl7_validator_service:3500') + us_core_version_num = G10Options::US_CORE_VERSION_NUMBERS[us_core_version_requirement[:us_core_version]] - cli_context do - txServer nil - displayWarnings true - disableDefaultResourceFetcher true + igs("hl7.fhir.us.core##{us_core_version_num}") + else + url ENV.fetch('G10_VALIDATOR_URL', 'http://validator_service:4567') end us_core_message_filters = case (us_core_version_requirement[:us_core_version]) when G10Options::US_CORE_3 - igs('hl7.fhir.us.core#3.1.1') USCoreTestKit::USCoreV311::USCoreTestSuite::VALIDATION_MESSAGE_FILTERS when G10Options::US_CORE_4 - igs('hl7.fhir.us.core#4.0.0') USCoreTestKit::USCoreV400::USCoreTestSuite::VALIDATION_MESSAGE_FILTERS when G10Options::US_CORE_5 - igs('hl7.fhir.us.core#5.0.1') USCoreTestKit::USCoreV501::USCoreTestSuite::VALIDATION_MESSAGE_FILTERS when G10Options::US_CORE_6 - igs('hl7.fhir.us.core#6.1.0') USCoreTestKit::USCoreV610::USCoreTestSuite::VALIDATION_MESSAGE_FILTERS end @@ -159,6 +163,16 @@ class G10CertificationSuite < Inferno::TestSuite end end + [ + G10Options::US_CORE_3_REQUIREMENT, + G10Options::US_CORE_4_REQUIREMENT, + G10Options::US_CORE_5_REQUIREMENT, + G10Options::US_CORE_6_REQUIREMENT + + ].each do |us_core_version_requirement| + setup_validator(us_core_version_requirement) + end + def self.jwks_json bulk_data_jwks = JSON.parse(File.read( ENV.fetch('G10_BULK_DATA_JWKS', diff --git a/lib/onc_certification_g10_test_kit/configuration_checker.rb b/lib/onc_certification_g10_test_kit/configuration_checker.rb index fba130a1..b1b4d0a0 100644 --- a/lib/onc_certification_g10_test_kit/configuration_checker.rb +++ b/lib/onc_certification_g10_test_kit/configuration_checker.rb @@ -2,7 +2,8 @@ module ONCCertificationG10TestKit class ConfigurationChecker - EXPECTED_VALIDATOR_VERSION = '"6.2.16-SNAPSHOT"'.freeze + EXPECTED_VALIDATOR_VERSION = '2.3.2'.freeze + EXPECTED_HL7_VALIDATOR_VERSION = '"6.2.16-SNAPSHOT"'.freeze def configuration_messages validator_version_message + terminology_messages + version_message @@ -21,19 +22,38 @@ def validator_url end def validator_version_message - response = Faraday.get "#{validator_url}/validator/version" - version = response.body - if version == EXPECTED_VALIDATOR_VERSION + if Feature.use_hl7_resource_validator? + expected_validator_version = EXPECTED_HL7_VALIDATOR_VERSION + validator_version_url = "#{validator_url}/validator/version" + else + expected_validator_version = EXPECTED_VALIDATOR_VERSION + validator_version_url = "#{validator_url}/version" + end + + response = Faraday.get validator_version_url + if response.body.starts_with? '{' + version_json = JSON.parse(response.body) + version = version_json['inferno-framework/fhir-validator-wrapper'] + else + version = response.body + end + + if version == expected_validator_version [{ type: 'info', - message: "FHIR validator is the expected version `#{EXPECTED_VALIDATOR_VERSION}`" + message: "FHIR validator is the expected version `#{expected_validator_version}`" }] else [{ type: 'error', - message: "Expected FHIR validator version `#{EXPECTED_VALIDATOR_VERSION}`, but found `#{version}`" + message: "Expected FHIR validator version `#{expected_validator_version}`, but found `#{version}`" }] end + rescue JSON::ParserError => e + [{ + type: 'error', + message: "Unable to parse Validator version '`#{response.body}`'. Parser error: `#{e.message}`" + }] rescue StandardError => e [{ type: 'error', diff --git a/lib/onc_certification_g10_test_kit/feature.rb b/lib/onc_certification_g10_test_kit/feature.rb index 06507529..d37d43e6 100644 --- a/lib/onc_certification_g10_test_kit/feature.rb +++ b/lib/onc_certification_g10_test_kit/feature.rb @@ -1,10 +1,9 @@ module ONCCertificationG10TestKit module Feature - class << self # rubocop:disable Lint/EmptyClass - # This is how you can define feature flags to be used in the g10 test kit - # def us_core_v4? - # ENV.fetch('US_CORE_4_ENABLED', 'false')&.casecmp?('true') - # end + class << self + def use_hl7_resource_validator? + ENV.fetch('USE_HL7_RESOURCE_VALIDATOR', 'false')&.casecmp?('true') + end end end end diff --git a/lib/onc_certification_g10_test_kit/g10_options.rb b/lib/onc_certification_g10_test_kit/g10_options.rb index b347bc00..7e50fb69 100644 --- a/lib/onc_certification_g10_test_kit/g10_options.rb +++ b/lib/onc_certification_g10_test_kit/g10_options.rb @@ -5,6 +5,13 @@ module G10Options US_CORE_5 = 'us_core_5'.freeze US_CORE_6 = 'us_core_6'.freeze + US_CORE_VERSION_NUMBERS = { + US_CORE_3 => '3.1.1', + US_CORE_4 => '4.0.0', + US_CORE_5 => '5.0.1', + US_CORE_6 => '6.1.0' + }.freeze + BULK_DATA_1 = 'multi_patient_api_stu1'.freeze BULK_DATA_2 = 'multi_patient_api_stu2'.freeze diff --git a/spec/fixtures/ValidationResponse-code-invalid-1.json b/spec/fixtures/ValidationResponse-code-invalid-1.json new file mode 100644 index 00000000..37752301 --- /dev/null +++ b/spec/fixtures/ValidationResponse-code-invalid-1.json @@ -0,0 +1,42 @@ +{ + "outcomes": [ + { + "fileInfo": { + "fileName": "manually_entered_file.json", + "fileContent": "{\n \"resourceType\": \"Immunization\",\n \"subpotentReason\": [{\n \"coding\": [{\n \"system\": \"http://terminology.hl7.org/CodeSystem/immunization-subpotent-reason\",\n \"code\": \"partialdose\"\n }]\n }]\n}", + "fileType": "json" + }, + "issues": [ + { + "source": "TerminologyEngine", + "server": null, + "line": 3, + "col": 4, + "location": "Immunization.subpotentReason[0].coding[0].code", + "message": "Unknown code 'partialdose' in the CodeSystem 'http://terminology.hl7.org/CodeSystem/immunization-subpotent-reason' version '1.0.0'", + "messageId": null, + "type": "CODEINVALID", + "level": "ERROR", + "html": "Unknown code 'partialdose' in the CodeSystem 'http://terminology.hl7.org/CodeSystem/immunization-subpotent-reason' version '1.0.0'", + "locationLink": null, + "txLink": null, + "sliceHtml": null, + "sliceText": null, + "slicingHint": false, + "signpost": false, + "criticalSignpost": false, + "ruleDate": null, + "matched": false, + "ignorableError": false, + "invId": null, + "comment": null, + "sliceInfo": null, + "error": true, + "display": "ERROR: Immunization.subpotentReason[0].coding[0].code: Unknown code 'partialdose' in the CodeSystem 'http://terminology.hl7.org/CodeSystem/immunization-subpotent-reason' version '1.0.0'" + } + ] + } + ], + "sessionId": "f4af8f4f-15d0-44f3-8401-dc9f9014aeaf", + "validationTimes": {} +} \ No newline at end of file diff --git a/spec/fixtures/ValidationResponse-code-invalid-2.json b/spec/fixtures/ValidationResponse-code-invalid-2.json new file mode 100644 index 00000000..efe93ca7 --- /dev/null +++ b/spec/fixtures/ValidationResponse-code-invalid-2.json @@ -0,0 +1,40 @@ +{ + "outcomes" : [ { + "fileInfo" : { + "fileName" : "manually_entered_file.json", + "fileContent" : "{\n \"resourceType\": \"AllergyIntolerance\",\n \"clinicalStatus\": {\n \"coding\": [\n {\n \"system\": \"http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical\",\n \"code\": \"actives\",\n \"display\": \"Active\"\n }\n ]\n }\n}", + "fileType" : "json" + }, + "issues" : [ + { + "source" : "TerminologyEngine", + "server" : null, + "line" : 3, + "col" : 4, + "location" : "AllergyIntolerance.clinicalStatus", + "message" : "None of the codings provided are in the value set 'AllergyIntolerance Clinical Status Codes' (http://hl7.org/fhir/ValueSet/allergyintolerance-clinical|4.0.1), and a coding from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical#actives)", + "messageId" : "Terminology_TX_NoValid_1_CC", + "type" : "CODEINVALID", + "level" : "ERROR", + "html" : "None of the codings provided are in the value set 'AllergyIntolerance Clinical Status Codes' (http://hl7.org/fhir/ValueSet/allergyintolerance-clinical|4.0.1), and a coding from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical#actives)", + "locationLink" : null, + "txLink" : null, + "sliceHtml" : null, + "sliceText" : null, + "slicingHint" : false, + "signpost" : false, + "criticalSignpost" : false, + "ruleDate" : null, + "matched" : false, + "ignorableError" : false, + "invId" : null, + "comment" : null, + "sliceInfo" : null, + "display" : "ERROR: AllergyIntolerance.clinicalStatus: None of the codings provided are in the value set 'AllergyIntolerance Clinical Status Codes' (http://hl7.org/fhir/ValueSet/allergyintolerance-clinical|4.0.1), and a coding from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical#actives)", + "error" : true + } + ] + } ], + "sessionId" : "7c0cb248-4dd9-4063-9ed9-03623bbe221a", + "validationTimes" : { } +} \ No newline at end of file diff --git a/spec/fixtures/ValidationResponse-code-invalid-3.json b/spec/fixtures/ValidationResponse-code-invalid-3.json new file mode 100644 index 00000000..595c435d --- /dev/null +++ b/spec/fixtures/ValidationResponse-code-invalid-3.json @@ -0,0 +1,38 @@ +{ + "outcomes" : [ { + "fileInfo" : { + "fileName" : "manually_entered_file.json", + "fileContent" : "{\n \"resourceType\": \"Patient\",\n \n \"extension\": [ {\n \"url\": \"http://hl7.org/fhir/us/core/StructureDefinition/us-core-race\",\n \"extension\": [ {\n \"url\": \"ombCategory\",\n \"valueCoding\": {\n \"system\": \"urn:oid:2.16.840.1.113883.6.238\",\n \"code\": \"2299-0\",\n \"display\": \"Asian\"\n }\n }, {\n \"url\": \"text\",\n \"valueString\": \"Asian\"\n } ]\n }, \n {\n \"url\": \"http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity\",\n \"extension\": [ {\n \"url\": \"ombCategory\",\n \"valueCoding\": {\n \"system\": \"urn:oid:2.16.840.1.113883.6.238\",\n \"code\": \"2186-5\",\n \"display\": \"Not Hispanic or Latino\"\n }\n }, {\n \"url\": \"text\",\n \"valueString\": \"Not Hispanic or Latino\"\n } ]\n }\n ]\n}", + "fileType" : "json" + }, + "issues" : [{ + "source" : "TerminologyEngine", + "server" : null, + "line" : 8, + "col" : 12, + "location" : "Patient.extension[0].extension[0].value.ofType(Coding)", + "message" : "The Coding provided (urn:oid:2.16.840.1.113883.6.238#2106-3) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-race-category, and a code is required from this value set. (error message = Not in value set http://hl7.org/fhir/us/core/ValueSet/omb-race-category)", + "messageId" : "Terminology_TX_Confirm_4a", + "type" : "CODEINVALID", + "level" : "ERROR", + "html" : "The Coding provided (urn:oid:2.16.840.1.113883.6.238#2106-3) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-race-category, and a code is required from this value set. (error message = Not in value set http://hl7.org/fhir/us/core/ValueSet/omb-race-category)", + "locationLink" : null, + "txLink" : null, + "sliceHtml" : null, + "sliceText" : null, + "slicingHint" : false, + "signpost" : false, + "criticalSignpost" : false, + "ruleDate" : null, + "matched" : false, + "ignorableError" : false, + "invId" : null, + "comment" : null, + "sliceInfo" : null, + "display" : "ERROR: Patient.extension[0].extension[0].value.ofType(Coding): The Coding provided (urn:oid:2.16.840.1.113883.6.238#2106-3) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-race-category, and a code is required from this value set. (error message = Not in value set http://hl7.org/fhir/us/core/ValueSet/omb-race-category)", + "error" : true + }] + } ], + "sessionId" : "7c0cb248-4dd9-4063-9ed9-03623bbe221a", + "validationTimes" : { } +} \ No newline at end of file diff --git a/spec/fixtures/ValidationResponse-no-name.json b/spec/fixtures/ValidationResponse-no-name.json new file mode 100644 index 00000000..45817a4d --- /dev/null +++ b/spec/fixtures/ValidationResponse-no-name.json @@ -0,0 +1,38 @@ +{ + "outcomes" : [ { + "fileInfo" : { + "fileName" : "manually_entered_file.json", + "fileContent" : "{ \"resourceType\": \"Patient\" }", + "fileType" : "json" + }, + "issues" : [{ + "source" : "InstanceValidator", + "server" : null, + "line" : 1, + "col" : 30, + "location" : "Patient", + "message" : "Patient.name: minimum required = 1, but only found 0 (from http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient|3.1.1)", + "messageId" : "Validation_VAL_Profile_Minimum", + "type" : "STRUCTURE", + "level" : "ERROR", + "html" : "Patient.name: minimum required = 1, but only found 0 (from http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient|3.1.1)", + "locationLink" : null, + "txLink" : null, + "sliceHtml" : null, + "sliceText" : null, + "slicingHint" : false, + "signpost" : false, + "criticalSignpost" : false, + "ruleDate" : null, + "matched" : false, + "ignorableError" : false, + "invId" : null, + "comment" : null, + "sliceInfo" : null, + "display" : "ERROR: Patient: Patient.name: minimum required = 1, but only found 0 (from http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient|3.1.1)", + "error" : true + } ] + } ], + "sessionId" : "4d9d2dc3-5df1-461f-a4d6-bfc2788a1933", + "validationTimes" : { } + } \ No newline at end of file diff --git a/spec/onc_certification_g10_test_kit/bulk_data_group_export_validation_spec.rb b/spec/onc_certification_g10_test_kit/bulk_data_group_export_validation_spec.rb index be7d8568..595d8627 100644 --- a/spec/onc_certification_g10_test_kit/bulk_data_group_export_validation_spec.rb +++ b/spec/onc_certification_g10_test_kit/bulk_data_group_export_validation_spec.rb @@ -124,6 +124,9 @@ def run(runnable, inputs = {}) ] ) end + let(:validation_response_no_name) do + File.read(File.join(__dir__, '..', 'fixtures', 'ValidationResponse-no-name.json')) + end let(:validator_url) { runnable.find_validator(:default).url } before do @@ -178,8 +181,17 @@ def run(runnable, inputs = {}) .with(headers: { 'Accept' => 'application/fhir+ndjson' }) .to_return(status: 200, body: contents, headers:) - validation_request = stub_request(:post, "#{validator_url}/validate?profile=http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient|3.1.1") - .to_return(status: 200, body: operation_outcome_no_name.to_json) + # Ideally this would run twice, once with each validator setting + if ONCCertificationG10TestKit::Feature.use_hl7_resource_validator? + validation_stub_url = "#{validator_url}/validate" + validation_stub_body = validation_response_no_name + else + validation_stub_url = "#{validator_url}/validate?profile=http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient|3.1.1" + validation_stub_body = operation_outcome_no_name.to_json + end + + validation_request = stub_request(:post, validation_stub_url) + .to_return(status: 200, body: validation_stub_body) result = run(runnable, patient_input) diff --git a/spec/onc_certification_g10_test_kit/resource_validation_spec.rb b/spec/onc_certification_g10_test_kit/resource_validation_spec.rb index 80cfe959..2cb59029 100644 --- a/spec/onc_certification_g10_test_kit/resource_validation_spec.rb +++ b/spec/onc_certification_g10_test_kit/resource_validation_spec.rb @@ -1,52 +1,117 @@ RSpec.describe 'Resource Validation' do # rubocop:disable RSpec/DescribeClass - let(:runnable) { ONCCertificationG10TestKit::G10CertificationSuite.groups.first.groups.first.tests.first.new } let(:resource) { FHIR.from_contents('{"resourceType": "Immunization", "id": "123"}') } - let(:validator_url) { runnable.find_validator(:default).url } - let(:operation_outcome_string1) do - File.read(File.join(__dir__, '..', 'fixtures', 'OperationOutcome-code-invalid-1.json')) - end - let(:operation_outcome_string2) do - File.read(File.join(__dir__, '..', 'fixtures', 'OperationOutcome-code-invalid-2.json')) - end - it 'excludes Unknown Code messages' do - stub_request(:post, "#{validator_url}/validate?profile=PROFILE") - .to_return(status: 200, body: operation_outcome_string1) + def reset_g10_validators(use_hl7_resource_validator) + ONCCertificationG10TestKit::G10CertificationSuite.fhir_validators[:default].clear + + ENV['USE_HL7_RESOURCE_VALIDATOR'] = use_hl7_resource_validator.to_s - expect(runnable.resource_is_valid?(resource:, profile_url: 'PROFILE')).to be(true) - expect(runnable.messages.length).to be_zero + [ + ONCCertificationG10TestKit::G10Options::US_CORE_3_REQUIREMENT, + ONCCertificationG10TestKit::G10Options::US_CORE_4_REQUIREMENT, + ONCCertificationG10TestKit::G10Options::US_CORE_5_REQUIREMENT, + ONCCertificationG10TestKit::G10Options::US_CORE_6_REQUIREMENT + + ].each do |us_core_version_requirement| + ONCCertificationG10TestKit::G10CertificationSuite.setup_validator(us_core_version_requirement) + end end - it 'excludes CodeableConcept not in VS messages' do - stub_request(:post, "#{validator_url}/validate?profile=PROFILE") - .to_return(status: 200, body: operation_outcome_string2) + describe 'with Inferno validator wrapper' do + before do + reset_g10_validators(false) + end + + let(:runnable) { ONCCertificationG10TestKit::G10CertificationSuite.groups.first.groups.first.tests.first.new } + let(:validator_url) { runnable.find_validator(:default).url } + let(:operation_outcome_string1) do + File.read(File.join(__dir__, '..', 'fixtures', 'OperationOutcome-code-invalid-1.json')) + end + let(:operation_outcome_string2) do + File.read(File.join(__dir__, '..', 'fixtures', 'OperationOutcome-code-invalid-2.json')) + end + + it 'excludes Unknown Code messages' do + stub_request(:post, "#{validator_url}/validate?profile=PROFILE") + .to_return(status: 200, body: operation_outcome_string1) + + expect(runnable.resource_is_valid?(resource:, profile_url: 'PROFILE')).to be(true) + expect(runnable.messages.length).to be_zero + end + + it 'excludes CodeableConcept not in VS messages' do + stub_request(:post, "#{validator_url}/validate?profile=PROFILE") + .to_return(status: 200, body: operation_outcome_string2) + + expect(runnable.resource_is_valid?(resource:, profile_url: 'PROFILE')).to be(true) + expect(runnable.messages.length).to be_zero + end - expect(runnable.resource_is_valid?(resource:, profile_url: 'PROFILE')).to be(true) - expect(runnable.messages.length).to be_zero + it 'excludes Coding not in VS messages' do + oo = FHIR::OperationOutcome.new( + issue: [ + { + severity: 'error', + code: 'code-invalid', + details: { + text: 'The Coding provided (urn:oid:2.16.840.1.113883.6.238#2106-3) is not in the value set ' \ + 'http://hl7.org/fhir/us/core/ValueSet/omb-race-category, and a code is required from ' \ + 'this value set. (error message = Not in value set ' \ + 'http://hl7.org/fhir/us/core/ValueSet/omb-race-category)' + }, + expression: [ + 'Patient.extension[0].extension[0].value.ofType(Coding)' + ] + } + ] + ) + stub_request(:post, "#{validator_url}/validate?profile=PROFILE") + .to_return(status: 200, body: oo.to_json) + + expect(runnable.resource_is_valid?(resource:, profile_url: 'PROFILE')).to be(true) + expect(runnable.messages.length).to be_zero + end end - it 'excludes Coding not in VS messages' do - oo = FHIR::OperationOutcome.new( - issue: [ - { - severity: 'error', - code: 'code-invalid', - details: { - text: 'The Coding provided (urn:oid:2.16.840.1.113883.6.238#2106-3) is not in the value set ' \ - 'http://hl7.org/fhir/us/core/ValueSet/omb-race-category, and a code is required from ' \ - 'this value set. (error message = Not in value set ' \ - 'http://hl7.org/fhir/us/core/ValueSet/omb-race-category)' - }, - expression: [ - 'Patient.extension[0].extension[0].value.ofType(Coding)' - ] - } - ] - ) - stub_request(:post, "#{validator_url}/validate?profile=PROFILE") - .to_return(status: 200, body: oo.to_json) - - expect(runnable.resource_is_valid?(resource:, profile_url: 'PROFILE')).to be(true) - expect(runnable.messages.length).to be_zero + describe 'with HL7 validator wrapper' do + before do + reset_g10_validators(true) + end + + let(:runnable) { ONCCertificationG10TestKit::G10CertificationSuite.groups.first.groups.first.tests.first.new } + let(:validator_url) { runnable.find_validator(:default).url } + let(:validation_response_string1) do + File.read(File.join(__dir__, '..', 'fixtures', 'ValidationResponse-code-invalid-1.json')) + end + let(:validation_response_string2) do + File.read(File.join(__dir__, '..', 'fixtures', 'ValidationResponse-code-invalid-2.json')) + end + let(:validation_response_string3) do + File.read(File.join(__dir__, '..', 'fixtures', 'ValidationResponse-code-invalid-3.json')) + end + + it 'excludes Unknown Code messages' do + stub_request(:post, "#{validator_url}/validate") + .to_return(status: 200, body: validation_response_string1) + + expect(runnable.resource_is_valid?(resource:, profile_url: 'PROFILE')).to be(true) + expect(runnable.messages.length).to be_zero + end + + it 'excludes CodeableConcept not in VS messages' do + stub_request(:post, "#{validator_url}/validate") + .to_return(status: 200, body: validation_response_string2) + + expect(runnable.resource_is_valid?(resource:, profile_url: 'PROFILE')).to be(true) + expect(runnable.messages.length).to be_zero + end + + it 'excludes Coding not in VS messages' do + stub_request(:post, "#{validator_url}/validate") + .to_return(status: 200, body: validation_response_string3) + + expect(runnable.resource_is_valid?(resource:, profile_url: 'PROFILE')).to be(true) + expect(runnable.messages.length).to be_zero + end end end