diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java index 6b998e592af..e19719532d1 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java @@ -928,5 +928,15 @@ public interface IValidationSupport { } } - + /** + * See VersionSpecificWorkerContextWrapper#validateCode in hapi-fhir-validation. + *

+ * If true, validation for codings will return a positive result if all codings are valid. + * If false, validation for codings will return a positive result if there is any coding that is valid. + * + * @return if the application has configured validation to use logical AND, as opposed to logical OR, which is the default + */ + default boolean isEnabledValidationForCodingsLogicalAnd() { + return false; + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/ValidationSupportContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/ValidationSupportContext.java index 5e4c6dc2b0a..174102c6554 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/ValidationSupportContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/ValidationSupportContext.java @@ -42,4 +42,8 @@ public class ValidationSupportContext { public Set getCurrentlyGeneratingSnapshots() { return myCurrentlyGeneratingSnapshots; } + + public boolean isEnabledValidationForCodingsLogicalAnd() { + return myRootValidationSupport.isEnabledValidationForCodingsLogicalAnd(); + } } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_4_0/4461-validation-language-coding-inconsistent-one-incorrect-vs-combo-with-correct.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_4_0/4461-validation-language-coding-inconsistent-one-incorrect-vs-combo-with-correct.yaml new file mode 100644 index 00000000000..614661ab784 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_4_0/4461-validation-language-coding-inconsistent-one-incorrect-vs-combo-with-correct.yaml @@ -0,0 +1,8 @@ +--- +type: fix +issue: 4461 +jira: SMILE-5442 +title: "There is inconsistent validation in the case of an incorrect communications language coding due to validation that looks for the first correct Coding. + In the case of a single incorrect code there is one validation result. + In the case of one correct code and one incorrect code, there is an entirely different result. + This has been fixed by introducing a new boolean flag in CachingValidationSupport as well as a new constructor to leverage it." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/ValidationSupportConfigUtil.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/ValidationSupportConfigUtil.java index 578553a333d..caa10977064 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/ValidationSupportConfigUtil.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/ValidationSupportConfigUtil.java @@ -27,10 +27,14 @@ public final class ValidationSupportConfigUtil { private ValidationSupportConfigUtil() {} public static CachingValidationSupport newCachingValidationSupport(JpaValidationSupportChain theJpaValidationSupportChain) { + return newCachingValidationSupport(theJpaValidationSupportChain, false); + } + + public static CachingValidationSupport newCachingValidationSupport(JpaValidationSupportChain theJpaValidationSupportChain, boolean theIsEnabledValidationForCodingsLogicalAnd) { // Short timeout for code translation because TermConceptMappingSvcImpl has its own caching CachingValidationSupport.CacheTimeouts cacheTimeouts = CachingValidationSupport.CacheTimeouts.defaultValues() .setTranslateCodeMillis(1000); - return new CachingValidationSupport(theJpaValidationSupportChain, cacheTimeouts); + return new CachingValidationSupport(theJpaValidationSupportChain, cacheTimeouts, theIsEnabledValidationForCodingsLogicalAnd); } } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java index 5a1e8778e6b..83b9e258cb0 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java @@ -45,6 +45,7 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple private final ThreadPoolExecutor myBackgroundExecutor; private final Map myNonExpiringCache; private final Cache myExpandValueSetCache; + private final boolean myIsEnabledValidationForCodingsLogicalAnd; /** * Constructor with default timeouts @@ -52,7 +53,15 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple * @param theWrap The validation support module to wrap */ public CachingValidationSupport(IValidationSupport theWrap) { - this(theWrap, CacheTimeouts.defaultValues()); + this(theWrap, CacheTimeouts.defaultValues(), false); + } + + public CachingValidationSupport(IValidationSupport theWrap, boolean theIsEnabledValidationForCodingsLogicalAnd) { + this(theWrap, CacheTimeouts.defaultValues(), theIsEnabledValidationForCodingsLogicalAnd); + } + + public CachingValidationSupport(IValidationSupport theWrap, CacheTimeouts theCacheTimeouts) { + this(theWrap, theCacheTimeouts, false); } /** @@ -61,7 +70,7 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple * @param theWrap The validation support module to wrap * @param theCacheTimeouts The timeouts to use */ - public CachingValidationSupport(IValidationSupport theWrap, CacheTimeouts theCacheTimeouts) { + public CachingValidationSupport(IValidationSupport theWrap, CacheTimeouts theCacheTimeouts, boolean theIsEnabledValidationForCodingsLogicalAnd) { super(theWrap.getFhirContext(), theWrap); myExpandValueSetCache = CacheFactory.build(theCacheTimeouts.getExpandValueSetMillis(), 100); myValidateCodeCache = CacheFactory.build(theCacheTimeouts.getValidateCodeMillis(), 5000); @@ -85,6 +94,7 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple threadFactory, new ThreadPoolExecutor.DiscardPolicy()); + myIsEnabledValidationForCodingsLogicalAnd = theIsEnabledValidationForCodingsLogicalAnd; } @Override @@ -314,4 +324,8 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple .setMiscMillis(10 * DateUtils.MILLIS_PER_MINUTE); } } + + public boolean isEnabledValidationForCodingsLogicalAnd() { + return myIsEnabledValidationForCodingsLogicalAnd; + } } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java index 1973482b947..435aadb4977 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java @@ -523,13 +523,22 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo @Override public ValidationResult validateCode(ValidationOptions theOptions, org.hl7.fhir.r5.model.CodeableConcept code, org.hl7.fhir.r5.model.ValueSet theVs) { + List validationResultsOk = new ArrayList<>(); for (Coding next : code.getCoding()) { ValidationResult retVal = validateCode(theOptions, next, theVs); if (retVal.isOk()) { - return retVal; + if (myValidationSupportContext.isEnabledValidationForCodingsLogicalAnd()) { + validationResultsOk.add(retVal); + } else { + return retVal; + } } } + if (code.getCoding().size() > 0 && validationResultsOk.size() == code.getCoding().size()) { + return validationResultsOk.get(0); + } + return new ValidationResult(ValidationMessage.IssueSeverity.ERROR, null); } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupportTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupportTest.java index 782864acd9d..16d81f7b191 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupportTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupportTest.java @@ -5,11 +5,15 @@ import ca.uhn.fhir.context.support.IValidationSupport; import com.google.common.collect.Lists; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.StructureDefinition; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.Collections; import java.util.List; @@ -30,8 +34,10 @@ public class CachingValidationSupportTest { @Mock private IValidationSupport myValidationSupport; - @Test - public void testAsyncBackgroundLoading() { + @ParameterizedTest + @NullSource + @ValueSource(booleans = {true, false}) + public void testAsyncBackgroundLoading(Boolean theIsEnabledValidationForCodingsLogicalAnd) { StructureDefinition sd0 = (StructureDefinition) new StructureDefinition().setId("SD0"); StructureDefinition sd1 = (StructureDefinition) new StructureDefinition().setId("SD1"); StructureDefinition sd2 = (StructureDefinition) new StructureDefinition().setId("SD2"); @@ -45,10 +51,10 @@ public class CachingValidationSupportTest { return Collections.singletonList(responses.remove(0)); }); - CachingValidationSupport.CacheTimeouts cacheTimeouts = CachingValidationSupport.CacheTimeouts + final CachingValidationSupport.CacheTimeouts cacheTimeouts = CachingValidationSupport.CacheTimeouts .defaultValues() .setMiscMillis(1000); - CachingValidationSupport support = new CachingValidationSupport(myValidationSupport, cacheTimeouts); + final CachingValidationSupport support = getSupport(cacheTimeouts, theIsEnabledValidationForCodingsLogicalAnd); assertEquals(3, responses.size()); List fetched = support.fetchAllNonBaseStructureDefinitions(); @@ -68,15 +74,21 @@ public class CachingValidationSupportTest { assert fetched != null; assertSame(sd1, fetched.get(0)); assertEquals(1, responses.size()); + + assertEquals(theIsEnabledValidationForCodingsLogicalAnd != null && theIsEnabledValidationForCodingsLogicalAnd, + support.isEnabledValidationForCodingsLogicalAnd()); } - @Test - public void fetchBinary_normally_accessesSuperOnlyOnce() { + @ParameterizedTest + @NullSource + @ValueSource(booleans = {true, false}) + public void fetchBinary_normally_accessesSuperOnlyOnce(Boolean theIsEnabledValidationForCodingsLogicalAnd) { final byte[] EXPECTED_BINARY = "dummyBinaryContent".getBytes(); final String EXPECTED_BINARY_KEY = "dummyBinaryKey"; when(myValidationSupport.getFhirContext()).thenReturn(ourCtx); when(myValidationSupport.fetchBinary(EXPECTED_BINARY_KEY)).thenReturn(EXPECTED_BINARY); - CachingValidationSupport support = new CachingValidationSupport(myValidationSupport); + + final CachingValidationSupport support = getSupport(null, theIsEnabledValidationForCodingsLogicalAnd); final byte[] firstActualBinary = support.fetchBinary(EXPECTED_BINARY_KEY); assertEquals(EXPECTED_BINARY,firstActualBinary); @@ -85,5 +97,25 @@ public class CachingValidationSupportTest { final byte[] secondActualBinary = support.fetchBinary(EXPECTED_BINARY_KEY); assertEquals(EXPECTED_BINARY,secondActualBinary); verify(myValidationSupport, times(1)).fetchBinary(EXPECTED_BINARY_KEY); + + assertEquals(theIsEnabledValidationForCodingsLogicalAnd != null && theIsEnabledValidationForCodingsLogicalAnd, + support.isEnabledValidationForCodingsLogicalAnd()); + } + + @Nonnull + private CachingValidationSupport getSupport(@Nullable CachingValidationSupport.CacheTimeouts theCacheTimeouts, @Nullable Boolean theIsEnabledValidationForCodingsLogicalAnd) { + if (theCacheTimeouts == null) { + if (theIsEnabledValidationForCodingsLogicalAnd == null) { + return new CachingValidationSupport(myValidationSupport); + } + + return new CachingValidationSupport(myValidationSupport, theIsEnabledValidationForCodingsLogicalAnd); + } + + if (theIsEnabledValidationForCodingsLogicalAnd == null) { + return new CachingValidationSupport(myValidationSupport, theCacheTimeouts); + } + + return new CachingValidationSupport(myValidationSupport, theCacheTimeouts, theIsEnabledValidationForCodingsLogicalAnd); } } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java index 35a121071c6..32d108e7448 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java @@ -6,7 +6,6 @@ import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; -import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.test.BaseTest; import ca.uhn.fhir.test.utilities.LoggingExtension; @@ -124,6 +123,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { private Set myValidSystems = new HashSet<>(); private Map myStructureDefinitionMap = new HashMap<>(); private CachingValidationSupport myValidationSupport; + private IValidationSupport myMockSupport; private void addValidConcept(String theSystem, String theCode) { myValidSystems.add(theSystem); @@ -149,26 +149,13 @@ public class FhirInstanceValidatorR4Test extends BaseTest { @SuppressWarnings("unchecked") @BeforeEach public void before() { - myFhirValidator = ourCtx.newValidator(); - myFhirValidator.setValidateAgainstStandardSchema(false); - myFhirValidator.setValidateAgainstStandardSchematron(false); - // This is only used if the validation is performed with validationOptions.isConcurrentBundleValidation = true - myFhirValidator.setExecutorService(Executors.newFixedThreadPool(4)); - - IValidationSupport mockSupport = mock(IValidationSupport.class); - when(mockSupport.getFhirContext()).thenReturn(ourCtx); - - ValidationSupportChain chain = new ValidationSupportChain(myDefaultValidationSupport, mockSupport, new InMemoryTerminologyServerValidationSupport(ourCtx), new CommonCodeSystemsTerminologyService(ourCtx), new SnapshotGeneratingValidationSupport(ourCtx)); - myValidationSupport = new CachingValidationSupport(chain); - myInstanceVal = new FhirInstanceValidator(myValidationSupport); - - myFhirValidator.registerValidatorModule(myInstanceVal); + buildValidationSupportWithLogicalAndSupport(false); mySupportedCodeSystemsForExpansion = new HashMap<>(); myValidConcepts = new ArrayList<>(); - when(mockSupport.expandValueSet(any(), nullable(ValueSetExpansionOptions.class), any(IBaseResource.class))).thenAnswer(t -> { + when(myMockSupport.expandValueSet(any(), nullable(ValueSetExpansionOptions.class), any(IBaseResource.class))).thenAnswer(t -> { ValueSet arg = (ValueSet) t.getArgument(2, IBaseResource.class); ValueSetExpansionComponent retVal = mySupportedCodeSystemsForExpansion.get(arg.getCompose().getIncludeFirstRep().getSystem()); if (retVal == null) { @@ -181,7 +168,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { valueset.setExpansion(retVal); return new ValueSetExpander.ValueSetExpansionOutcome(valueset); }); - when(mockSupport.isCodeSystemSupported(any(), nullable(String.class))).thenAnswer(new Answer() { + when(myMockSupport.isCodeSystemSupported(any(), nullable(String.class))).thenAnswer(new Answer() { @Override public Boolean answer(InvocationOnMock theInvocation) { String argument = theInvocation.getArgument(1, String.class); @@ -190,7 +177,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { return retVal; } }); - when(mockSupport.fetchResource(nullable(Class.class), nullable(String.class))).thenAnswer(new Answer() { + when(myMockSupport.fetchResource(nullable(Class.class), nullable(String.class))).thenAnswer(new Answer() { @Override public IBaseResource answer(InvocationOnMock theInvocation) throws Throwable { IBaseResource retVal; @@ -207,7 +194,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { return retVal; } }); - when(mockSupport.validateCode(any(), any(), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer() { + when(myMockSupport.validateCode(any(), any(), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer() { @Override public IValidationSupport.CodeValidationResult answer(InvocationOnMock theInvocation) { ConceptValidationOptions options = theInvocation.getArgument(1, ConceptValidationOptions.class); @@ -227,7 +214,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { return retVal; } }); - when(mockSupport.fetchCodeSystem(nullable(String.class))).thenAnswer(new Answer() { + when(myMockSupport.fetchCodeSystem(nullable(String.class))).thenAnswer(new Answer() { @Override public CodeSystem answer(InvocationOnMock theInvocation) { String system = theInvocation.getArgument(0, String.class); @@ -244,7 +231,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { return retVal; } }); - when(mockSupport.fetchStructureDefinition(nullable(String.class))).thenAnswer(new Answer() { + when(myMockSupport.fetchStructureDefinition(nullable(String.class))).thenAnswer(new Answer() { @Override public IBaseResource answer(InvocationOnMock theInvocation) { String id = (String) theInvocation.getArguments()[0]; @@ -258,7 +245,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { return retVal; } }); - when(mockSupport.fetchAllStructureDefinitions()).thenAnswer(new Answer>() { + when(myMockSupport.fetchAllStructureDefinitions()).thenAnswer(new Answer>() { @Override public List answer(InvocationOnMock theInvocation) { List retVal = new ArrayList<>(myDefaultValidationSupport.fetchAllStructureDefinitions()); @@ -267,7 +254,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { return retVal; } }); - when(mockSupport.lookupCode(any(), any(), any(), any())).thenAnswer(t -> { + when(myMockSupport.lookupCode(any(), any(), any(), any())).thenAnswer(t -> { String system = t.getArgument(1, String.class); String code = t.getArgument(2, String.class); if (myValidConcepts.contains(system + "___" + code)) { @@ -276,7 +263,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { return null; } }); - when(mockSupport.validateCodeInValueSet(any(), any(), any(), any(), any(), any())).thenAnswer(t -> { + when(myMockSupport.validateCodeInValueSet(any(), any(), any(), any(), any(), any())).thenAnswer(t -> { String system = t.getArgument(2, String.class); String code = t.getArgument(3, String.class); if (myValidConcepts.contains(system + "___" + code)) { @@ -848,7 +835,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { public void testValidateProfileWithExtension() throws IOException, FHIRException { PrePopulatedValidationSupport valSupport = new PrePopulatedValidationSupport(ourCtx); DefaultProfileValidationSupport defaultSupport = new DefaultProfileValidationSupport(ourCtx); - CachingValidationSupport support = new CachingValidationSupport(new ValidationSupportChain(defaultSupport, valSupport, new InMemoryTerminologyServerValidationSupport(ourCtx))); + CachingValidationSupport support = new CachingValidationSupport(new ValidationSupportChain(defaultSupport, valSupport, new InMemoryTerminologyServerValidationSupport(ourCtx)), false); // Prepopulate SDs valSupport.addStructureDefinition(loadStructureDefinition(defaultSupport, "/r4/myconsent-profile.xml")); @@ -1615,6 +1602,66 @@ public class FhirInstanceValidatorR4Test extends BaseTest { } } + @Test + public void testPatientSingleCommunicationLanguage_en() throws IOException { + final String encoded = loadResource("patient-with-single-comm-lang-en.json"); + + final ValidationResult output = myFhirValidator.validateWithResult(encoded); + final List errors = logResultsAndReturnNonInformationalOnes(output); + + assertTrue(errors.isEmpty()); + } + + @Test + public void testPatientSingleCommunicationLanguage_en_US_UNDERSCORE_config_false() throws IOException { + final String encoded = loadResource("patient-with-single-comm-lang-en_US-UNDERSCORE.json"); + + final ValidationResult output = myFhirValidator.validateWithResult(encoded); + final List errors = logResultsAndReturnNonInformationalOnes(output); + + assertTrue(errors.isEmpty()); + } + + @Test + public void testPatientSingleCommunicationLanguage_en_US_DASH() throws IOException { + final String encoded = loadResource("patient-with-single-comm-lang-en-US-DASH.json"); + + final ValidationResult output = myFhirValidator.validateWithResult(encoded); + final List errors = logResultsAndReturnNonInformationalOnes(output); + + assertTrue(errors.isEmpty()); + } + + @Test + public void testPatientMultipleCommunicationLanguages_en_US_and_en_UNDERSCORE_config_true() throws IOException { + buildValidationSupportWithLogicalAndSupport(true); + final String encoded = loadResource("patient-with-multiple-comm-langs-en_US-and-en-UNDERSCORE.json"); + + final ValidationResult output = myFhirValidator.validateWithResult(encoded); + final List errors = logResultsAndReturnNonInformationalOnes(output); + + assertTrue(errors.isEmpty()); + } + + @Test + public void testPatientMultipleCommunicationLanguages_en_US_and_en_UNDERSCORE_config_false() throws IOException { + final String encoded = loadResource("patient-with-multiple-comm-langs-en_US-and-en-UNDERSCORE.json"); + + final ValidationResult output = myFhirValidator.validateWithResult(encoded); + final List errors = logResultsAndReturnNonInformationalOnes(output); + + assertFalse(errors.isEmpty()); + } + + @Test + public void testPatientMultipleCommunicationLanguages_en_US_and_en_DASH() throws IOException { + final String encoded = loadResource("patient-with-multiple-comm-langs-en-US-and-en-DASH.json"); + + final ValidationResult output = myFhirValidator.validateWithResult(encoded); + final List errors = logResultsAndReturnNonInformationalOnes(output); + assertTrue(errors.isEmpty()); + } + private Bundle buildBundle(int theSize, boolean theValidBundle) throws IOException { BundleBuilder bundleBuilder = new BundleBuilder(ourCtx); Patient p = ourCtx.newJsonParser().parseResource(Patient.class, loadResource("/r4/concurrent-bundle/patient.json")); @@ -1656,5 +1703,19 @@ public class FhirInstanceValidatorR4Test extends BaseTest { TestUtil.randomizeLocaleAndTimezone(); } + private void buildValidationSupportWithLogicalAndSupport(boolean theLogicalAnd) { + myFhirValidator = ourCtx.newValidator(); + myFhirValidator.setValidateAgainstStandardSchema(false); + myFhirValidator.setValidateAgainstStandardSchematron(false); + // This is only used if the validation is performed with validationOptions.isConcurrentBundleValidation = true + myFhirValidator.setExecutorService(Executors.newFixedThreadPool(4)); + + myMockSupport = mock(IValidationSupport.class); + when(myMockSupport.getFhirContext()).thenReturn(ourCtx); + ValidationSupportChain chain = new ValidationSupportChain(myDefaultValidationSupport, myMockSupport, new InMemoryTerminologyServerValidationSupport(ourCtx), new CommonCodeSystemsTerminologyService(ourCtx), new SnapshotGeneratingValidationSupport(ourCtx)); + myValidationSupport = new CachingValidationSupport(chain, theLogicalAnd); + myInstanceVal = new FhirInstanceValidator(myValidationSupport); + myFhirValidator.registerValidatorModule(myInstanceVal); + } } diff --git a/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-US-and-en-DASH.json b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-US-and-en-DASH.json new file mode 100644 index 00000000000..bcd0bf718c3 --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-US-and-en-DASH.json @@ -0,0 +1,31 @@ +{ + "resourceType": "Patient", + "name": [ + { + "family": "Doe", + "given": [ + "Jane", + "Philip" + ] + } + ], + "birthDate": "1912-04-14", + "communication": [ + { + "language": { + "coding": [ + { + "system": "urn:ietf:bcp:47", + "code": "en-US", + "display": "US English" + }, + { + "system": "urn:ietf:bcp:47", + "code": "en", + "display": "English" + } + ] + } + } + ] +} diff --git a/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-and-en_US-UNDERSCORE.json b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-and-en_US-UNDERSCORE.json new file mode 100644 index 00000000000..6423ce7a84f --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-and-en_US-UNDERSCORE.json @@ -0,0 +1,31 @@ +{ + "resourceType": "Patient", + "name": [ + { + "family": "Doe", + "given": [ + "Jane", + "Philip" + ] + } + ], + "birthDate": "1912-04-14", + "communication": [ + { + "language": { + "coding": [ + { + "system": "urn:ietf:bcp:47", + "code": "en", + "display": "English" + }, + { + "system": "urn:ietf:bcp:47", + "code": "en_US", + "display": "US English" + } + ] + } + } + ] +} diff --git a/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en_US-and-en-UNDERSCORE.json b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en_US-and-en-UNDERSCORE.json new file mode 100644 index 00000000000..65526ac8421 --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en_US-and-en-UNDERSCORE.json @@ -0,0 +1,31 @@ +{ + "resourceType": "Patient", + "name": [ + { + "family": "Doe", + "given": [ + "Jane", + "Philip" + ] + } + ], + "birthDate": "1912-04-14", + "communication": [ + { + "language": { + "coding": [ + { + "system": "urn:ietf:bcp:47", + "code": "en_US", + "display": "US English" + }, + { + "system": "urn:ietf:bcp:47", + "code": "en", + "display": "English" + } + ] + } + } + ] +} diff --git a/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en-US-DASH.json b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en-US-DASH.json new file mode 100644 index 00000000000..c169a6c379f --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en-US-DASH.json @@ -0,0 +1,26 @@ +{ + "resourceType": "Patient", + "name": [ + { + "family": "Doe", + "given": [ + "Jane", + "Philip" + ] + } + ], + "birthDate": "1912-04-14", + "communication": [ + { + "language": { + "coding": [ + { + "system": "urn:ietf:bcp:47", + "code": "en-US", + "display": "US English" + } + ] + } + } + ] +} diff --git a/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en.json b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en.json new file mode 100644 index 00000000000..86fc6f27920 --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en.json @@ -0,0 +1,26 @@ +{ + "resourceType": "Patient", + "name": [ + { + "family": "Doe", + "given": [ + "Jane", + "Philip" + ] + } + ], + "birthDate": "1912-04-14", + "communication": [ + { + "language": { + "coding": [ + { + "system": "urn:ietf:bcp:47", + "code": "en", + "display": "English" + } + ] + } + } + ] +} diff --git a/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en_US-UNDERSCORE.json b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en_US-UNDERSCORE.json new file mode 100644 index 00000000000..c5ed811442e --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en_US-UNDERSCORE.json @@ -0,0 +1,26 @@ +{ + "resourceType": "Patient", + "name": [ + { + "family": "Doe", + "given": [ + "Jane", + "Philip" + ] + } + ], + "birthDate": "1912-04-14", + "communication": [ + { + "language": { + "coding": [ + { + "system": "urn:ietf:bcp:47", + "code": "en_US", + "display": "US English" + } + ] + } + } + ] +}