Introduce new logical AND boolean in CachingValidationSupport that influences Coding validation in VersionSpecificWorkerContextWrapper (#4433)
* Failing unit test with a combination of both communication language types plus two passing tests with each language type individually. * Failing unit test with a combination of both communication language types plus two passing tests with each language type individually. * Change patient name and birhdate. * Latest unit test enhancements. * Preliminary solution that seems to fix the bug and not break module unit tests. Instead of VersionSpecificWorkerContextWrapper.validateCode() returning immediately upon the first OK validation, collect all the OKs and return the first if all codings are OK. * Try another solution to output the correct error message without breaking other tests. * Try a solution focused on a ValueSet with a languages URI. * Working but messy config to toggle the AND/OR behaviour in VersionSpecificWorkerContextWrapper.validateCode(). Need to do a lot of cleanup, unit/integration tests and possibly documentation. * Working but messy solution to toggle the AND/OR behaviour in VersionSpecificWorkerContextWrapper.validateCode(). Need to do a lot of cleanup, unit/integration tests and possibly documentation. * Reverse changes that are not needed for the final solution. Also, add a convenient method to ValidationSupportContext and call it. * Update FhirInstanceValidatorR4Test to set the new logical AND flag. Rename all references to the new flag. * Revert all classes to master that were changed to call the new constructor. Add new changelog. * Revert other false param constructor calls. * Add code reuse to ValidationSupportConfigUtil and increase test coverage for CachingValidationSupportTest. * Code review feedback: Clarify property description. Fix parameter names. Add extra check for empty codings.
This commit is contained in:
parent
527a948285
commit
1755eb46e6
|
@ -928,5 +928,15 @@ public interface IValidationSupport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See VersionSpecificWorkerContextWrapper#validateCode in hapi-fhir-validation.
|
||||||
|
* <p>
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,4 +42,8 @@ public class ValidationSupportContext {
|
||||||
public Set<String> getCurrentlyGeneratingSnapshots() {
|
public Set<String> getCurrentlyGeneratingSnapshots() {
|
||||||
return myCurrentlyGeneratingSnapshots;
|
return myCurrentlyGeneratingSnapshots;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEnabledValidationForCodingsLogicalAnd() {
|
||||||
|
return myRootValidationSupport.isEnabledValidationForCodingsLogicalAnd();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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."
|
|
@ -27,10 +27,14 @@ public final class ValidationSupportConfigUtil {
|
||||||
private ValidationSupportConfigUtil() {}
|
private ValidationSupportConfigUtil() {}
|
||||||
|
|
||||||
public static CachingValidationSupport newCachingValidationSupport(JpaValidationSupportChain theJpaValidationSupportChain) {
|
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
|
// Short timeout for code translation because TermConceptMappingSvcImpl has its own caching
|
||||||
CachingValidationSupport.CacheTimeouts cacheTimeouts = CachingValidationSupport.CacheTimeouts.defaultValues()
|
CachingValidationSupport.CacheTimeouts cacheTimeouts = CachingValidationSupport.CacheTimeouts.defaultValues()
|
||||||
.setTranslateCodeMillis(1000);
|
.setTranslateCodeMillis(1000);
|
||||||
|
|
||||||
return new CachingValidationSupport(theJpaValidationSupportChain, cacheTimeouts);
|
return new CachingValidationSupport(theJpaValidationSupportChain, cacheTimeouts, theIsEnabledValidationForCodingsLogicalAnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
|
||||||
private final ThreadPoolExecutor myBackgroundExecutor;
|
private final ThreadPoolExecutor myBackgroundExecutor;
|
||||||
private final Map<Object, Object> myNonExpiringCache;
|
private final Map<Object, Object> myNonExpiringCache;
|
||||||
private final Cache<String, Object> myExpandValueSetCache;
|
private final Cache<String, Object> myExpandValueSetCache;
|
||||||
|
private final boolean myIsEnabledValidationForCodingsLogicalAnd;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor with default timeouts
|
* Constructor with default timeouts
|
||||||
|
@ -52,7 +53,15 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
|
||||||
* @param theWrap The validation support module to wrap
|
* @param theWrap The validation support module to wrap
|
||||||
*/
|
*/
|
||||||
public CachingValidationSupport(IValidationSupport theWrap) {
|
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 theWrap The validation support module to wrap
|
||||||
* @param theCacheTimeouts The timeouts to use
|
* @param theCacheTimeouts The timeouts to use
|
||||||
*/
|
*/
|
||||||
public CachingValidationSupport(IValidationSupport theWrap, CacheTimeouts theCacheTimeouts) {
|
public CachingValidationSupport(IValidationSupport theWrap, CacheTimeouts theCacheTimeouts, boolean theIsEnabledValidationForCodingsLogicalAnd) {
|
||||||
super(theWrap.getFhirContext(), theWrap);
|
super(theWrap.getFhirContext(), theWrap);
|
||||||
myExpandValueSetCache = CacheFactory.build(theCacheTimeouts.getExpandValueSetMillis(), 100);
|
myExpandValueSetCache = CacheFactory.build(theCacheTimeouts.getExpandValueSetMillis(), 100);
|
||||||
myValidateCodeCache = CacheFactory.build(theCacheTimeouts.getValidateCodeMillis(), 5000);
|
myValidateCodeCache = CacheFactory.build(theCacheTimeouts.getValidateCodeMillis(), 5000);
|
||||||
|
@ -85,6 +94,7 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
|
||||||
threadFactory,
|
threadFactory,
|
||||||
new ThreadPoolExecutor.DiscardPolicy());
|
new ThreadPoolExecutor.DiscardPolicy());
|
||||||
|
|
||||||
|
myIsEnabledValidationForCodingsLogicalAnd = theIsEnabledValidationForCodingsLogicalAnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -314,4 +324,8 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
|
||||||
.setMiscMillis(10 * DateUtils.MILLIS_PER_MINUTE);
|
.setMiscMillis(10 * DateUtils.MILLIS_PER_MINUTE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEnabledValidationForCodingsLogicalAnd() {
|
||||||
|
return myIsEnabledValidationForCodingsLogicalAnd;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -523,13 +523,22 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult validateCode(ValidationOptions theOptions, org.hl7.fhir.r5.model.CodeableConcept code, org.hl7.fhir.r5.model.ValueSet theVs) {
|
public ValidationResult validateCode(ValidationOptions theOptions, org.hl7.fhir.r5.model.CodeableConcept code, org.hl7.fhir.r5.model.ValueSet theVs) {
|
||||||
|
List<ValidationResult> validationResultsOk = new ArrayList<>();
|
||||||
for (Coding next : code.getCoding()) {
|
for (Coding next : code.getCoding()) {
|
||||||
ValidationResult retVal = validateCode(theOptions, next, theVs);
|
ValidationResult retVal = validateCode(theOptions, next, theVs);
|
||||||
if (retVal.isOk()) {
|
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);
|
return new ValidationResult(ValidationMessage.IssueSeverity.ERROR, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,15 @@ import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
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.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -30,8 +34,10 @@ public class CachingValidationSupportTest {
|
||||||
@Mock
|
@Mock
|
||||||
private IValidationSupport myValidationSupport;
|
private IValidationSupport myValidationSupport;
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
public void testAsyncBackgroundLoading() {
|
@NullSource
|
||||||
|
@ValueSource(booleans = {true, false})
|
||||||
|
public void testAsyncBackgroundLoading(Boolean theIsEnabledValidationForCodingsLogicalAnd) {
|
||||||
StructureDefinition sd0 = (StructureDefinition) new StructureDefinition().setId("SD0");
|
StructureDefinition sd0 = (StructureDefinition) new StructureDefinition().setId("SD0");
|
||||||
StructureDefinition sd1 = (StructureDefinition) new StructureDefinition().setId("SD1");
|
StructureDefinition sd1 = (StructureDefinition) new StructureDefinition().setId("SD1");
|
||||||
StructureDefinition sd2 = (StructureDefinition) new StructureDefinition().setId("SD2");
|
StructureDefinition sd2 = (StructureDefinition) new StructureDefinition().setId("SD2");
|
||||||
|
@ -45,10 +51,10 @@ public class CachingValidationSupportTest {
|
||||||
return Collections.singletonList(responses.remove(0));
|
return Collections.singletonList(responses.remove(0));
|
||||||
});
|
});
|
||||||
|
|
||||||
CachingValidationSupport.CacheTimeouts cacheTimeouts = CachingValidationSupport.CacheTimeouts
|
final CachingValidationSupport.CacheTimeouts cacheTimeouts = CachingValidationSupport.CacheTimeouts
|
||||||
.defaultValues()
|
.defaultValues()
|
||||||
.setMiscMillis(1000);
|
.setMiscMillis(1000);
|
||||||
CachingValidationSupport support = new CachingValidationSupport(myValidationSupport, cacheTimeouts);
|
final CachingValidationSupport support = getSupport(cacheTimeouts, theIsEnabledValidationForCodingsLogicalAnd);
|
||||||
|
|
||||||
assertEquals(3, responses.size());
|
assertEquals(3, responses.size());
|
||||||
List<IBaseResource> fetched = support.fetchAllNonBaseStructureDefinitions();
|
List<IBaseResource> fetched = support.fetchAllNonBaseStructureDefinitions();
|
||||||
|
@ -68,15 +74,21 @@ public class CachingValidationSupportTest {
|
||||||
assert fetched != null;
|
assert fetched != null;
|
||||||
assertSame(sd1, fetched.get(0));
|
assertSame(sd1, fetched.get(0));
|
||||||
assertEquals(1, responses.size());
|
assertEquals(1, responses.size());
|
||||||
|
|
||||||
|
assertEquals(theIsEnabledValidationForCodingsLogicalAnd != null && theIsEnabledValidationForCodingsLogicalAnd,
|
||||||
|
support.isEnabledValidationForCodingsLogicalAnd());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedTest
|
||||||
public void fetchBinary_normally_accessesSuperOnlyOnce() {
|
@NullSource
|
||||||
|
@ValueSource(booleans = {true, false})
|
||||||
|
public void fetchBinary_normally_accessesSuperOnlyOnce(Boolean theIsEnabledValidationForCodingsLogicalAnd) {
|
||||||
final byte[] EXPECTED_BINARY = "dummyBinaryContent".getBytes();
|
final byte[] EXPECTED_BINARY = "dummyBinaryContent".getBytes();
|
||||||
final String EXPECTED_BINARY_KEY = "dummyBinaryKey";
|
final String EXPECTED_BINARY_KEY = "dummyBinaryKey";
|
||||||
when(myValidationSupport.getFhirContext()).thenReturn(ourCtx);
|
when(myValidationSupport.getFhirContext()).thenReturn(ourCtx);
|
||||||
when(myValidationSupport.fetchBinary(EXPECTED_BINARY_KEY)).thenReturn(EXPECTED_BINARY);
|
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);
|
final byte[] firstActualBinary = support.fetchBinary(EXPECTED_BINARY_KEY);
|
||||||
assertEquals(EXPECTED_BINARY,firstActualBinary);
|
assertEquals(EXPECTED_BINARY,firstActualBinary);
|
||||||
|
@ -85,5 +97,25 @@ public class CachingValidationSupportTest {
|
||||||
final byte[] secondActualBinary = support.fetchBinary(EXPECTED_BINARY_KEY);
|
final byte[] secondActualBinary = support.fetchBinary(EXPECTED_BINARY_KEY);
|
||||||
assertEquals(EXPECTED_BINARY,secondActualBinary);
|
assertEquals(EXPECTED_BINARY,secondActualBinary);
|
||||||
verify(myValidationSupport, times(1)).fetchBinary(EXPECTED_BINARY_KEY);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.test.BaseTest;
|
import ca.uhn.fhir.test.BaseTest;
|
||||||
import ca.uhn.fhir.test.utilities.LoggingExtension;
|
import ca.uhn.fhir.test.utilities.LoggingExtension;
|
||||||
|
@ -124,6 +123,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
private Set<String> myValidSystems = new HashSet<>();
|
private Set<String> myValidSystems = new HashSet<>();
|
||||||
private Map<String, StructureDefinition> myStructureDefinitionMap = new HashMap<>();
|
private Map<String, StructureDefinition> myStructureDefinitionMap = new HashMap<>();
|
||||||
private CachingValidationSupport myValidationSupport;
|
private CachingValidationSupport myValidationSupport;
|
||||||
|
private IValidationSupport myMockSupport;
|
||||||
|
|
||||||
private void addValidConcept(String theSystem, String theCode) {
|
private void addValidConcept(String theSystem, String theCode) {
|
||||||
myValidSystems.add(theSystem);
|
myValidSystems.add(theSystem);
|
||||||
|
@ -149,26 +149,13 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() {
|
public void before() {
|
||||||
myFhirValidator = ourCtx.newValidator();
|
buildValidationSupportWithLogicalAndSupport(false);
|
||||||
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);
|
|
||||||
|
|
||||||
mySupportedCodeSystemsForExpansion = new HashMap<>();
|
mySupportedCodeSystemsForExpansion = new HashMap<>();
|
||||||
|
|
||||||
myValidConcepts = new ArrayList<>();
|
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);
|
ValueSet arg = (ValueSet) t.getArgument(2, IBaseResource.class);
|
||||||
ValueSetExpansionComponent retVal = mySupportedCodeSystemsForExpansion.get(arg.getCompose().getIncludeFirstRep().getSystem());
|
ValueSetExpansionComponent retVal = mySupportedCodeSystemsForExpansion.get(arg.getCompose().getIncludeFirstRep().getSystem());
|
||||||
if (retVal == null) {
|
if (retVal == null) {
|
||||||
|
@ -181,7 +168,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
valueset.setExpansion(retVal);
|
valueset.setExpansion(retVal);
|
||||||
return new ValueSetExpander.ValueSetExpansionOutcome(valueset);
|
return new ValueSetExpander.ValueSetExpansionOutcome(valueset);
|
||||||
});
|
});
|
||||||
when(mockSupport.isCodeSystemSupported(any(), nullable(String.class))).thenAnswer(new Answer<Boolean>() {
|
when(myMockSupport.isCodeSystemSupported(any(), nullable(String.class))).thenAnswer(new Answer<Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
public Boolean answer(InvocationOnMock theInvocation) {
|
public Boolean answer(InvocationOnMock theInvocation) {
|
||||||
String argument = theInvocation.getArgument(1, String.class);
|
String argument = theInvocation.getArgument(1, String.class);
|
||||||
|
@ -190,7 +177,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
when(mockSupport.fetchResource(nullable(Class.class), nullable(String.class))).thenAnswer(new Answer<IBaseResource>() {
|
when(myMockSupport.fetchResource(nullable(Class.class), nullable(String.class))).thenAnswer(new Answer<IBaseResource>() {
|
||||||
@Override
|
@Override
|
||||||
public IBaseResource answer(InvocationOnMock theInvocation) throws Throwable {
|
public IBaseResource answer(InvocationOnMock theInvocation) throws Throwable {
|
||||||
IBaseResource retVal;
|
IBaseResource retVal;
|
||||||
|
@ -207,7 +194,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
when(mockSupport.validateCode(any(), any(), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer<IValidationSupport.CodeValidationResult>() {
|
when(myMockSupport.validateCode(any(), any(), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer<IValidationSupport.CodeValidationResult>() {
|
||||||
@Override
|
@Override
|
||||||
public IValidationSupport.CodeValidationResult answer(InvocationOnMock theInvocation) {
|
public IValidationSupport.CodeValidationResult answer(InvocationOnMock theInvocation) {
|
||||||
ConceptValidationOptions options = theInvocation.getArgument(1, ConceptValidationOptions.class);
|
ConceptValidationOptions options = theInvocation.getArgument(1, ConceptValidationOptions.class);
|
||||||
|
@ -227,7 +214,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
when(mockSupport.fetchCodeSystem(nullable(String.class))).thenAnswer(new Answer<CodeSystem>() {
|
when(myMockSupport.fetchCodeSystem(nullable(String.class))).thenAnswer(new Answer<CodeSystem>() {
|
||||||
@Override
|
@Override
|
||||||
public CodeSystem answer(InvocationOnMock theInvocation) {
|
public CodeSystem answer(InvocationOnMock theInvocation) {
|
||||||
String system = theInvocation.getArgument(0, String.class);
|
String system = theInvocation.getArgument(0, String.class);
|
||||||
|
@ -244,7 +231,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
when(mockSupport.fetchStructureDefinition(nullable(String.class))).thenAnswer(new Answer<IBaseResource>() {
|
when(myMockSupport.fetchStructureDefinition(nullable(String.class))).thenAnswer(new Answer<IBaseResource>() {
|
||||||
@Override
|
@Override
|
||||||
public IBaseResource answer(InvocationOnMock theInvocation) {
|
public IBaseResource answer(InvocationOnMock theInvocation) {
|
||||||
String id = (String) theInvocation.getArguments()[0];
|
String id = (String) theInvocation.getArguments()[0];
|
||||||
|
@ -258,7 +245,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
when(mockSupport.fetchAllStructureDefinitions()).thenAnswer(new Answer<List<StructureDefinition>>() {
|
when(myMockSupport.fetchAllStructureDefinitions()).thenAnswer(new Answer<List<StructureDefinition>>() {
|
||||||
@Override
|
@Override
|
||||||
public List<StructureDefinition> answer(InvocationOnMock theInvocation) {
|
public List<StructureDefinition> answer(InvocationOnMock theInvocation) {
|
||||||
List<StructureDefinition> retVal = new ArrayList<>(myDefaultValidationSupport.fetchAllStructureDefinitions());
|
List<StructureDefinition> retVal = new ArrayList<>(myDefaultValidationSupport.fetchAllStructureDefinitions());
|
||||||
|
@ -267,7 +254,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
return retVal;
|
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 system = t.getArgument(1, String.class);
|
||||||
String code = t.getArgument(2, String.class);
|
String code = t.getArgument(2, String.class);
|
||||||
if (myValidConcepts.contains(system + "___" + code)) {
|
if (myValidConcepts.contains(system + "___" + code)) {
|
||||||
|
@ -276,7 +263,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
return null;
|
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 system = t.getArgument(2, String.class);
|
||||||
String code = t.getArgument(3, String.class);
|
String code = t.getArgument(3, String.class);
|
||||||
if (myValidConcepts.contains(system + "___" + code)) {
|
if (myValidConcepts.contains(system + "___" + code)) {
|
||||||
|
@ -848,7 +835,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
public void testValidateProfileWithExtension() throws IOException, FHIRException {
|
public void testValidateProfileWithExtension() throws IOException, FHIRException {
|
||||||
PrePopulatedValidationSupport valSupport = new PrePopulatedValidationSupport(ourCtx);
|
PrePopulatedValidationSupport valSupport = new PrePopulatedValidationSupport(ourCtx);
|
||||||
DefaultProfileValidationSupport defaultSupport = new DefaultProfileValidationSupport(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
|
// Prepopulate SDs
|
||||||
valSupport.addStructureDefinition(loadStructureDefinition(defaultSupport, "/r4/myconsent-profile.xml"));
|
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<SingleValidationMessage> 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<SingleValidationMessage> 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<SingleValidationMessage> 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<SingleValidationMessage> 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<SingleValidationMessage> 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<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||||
|
assertTrue(errors.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
private Bundle buildBundle(int theSize, boolean theValidBundle) throws IOException {
|
private Bundle buildBundle(int theSize, boolean theValidBundle) throws IOException {
|
||||||
BundleBuilder bundleBuilder = new BundleBuilder(ourCtx);
|
BundleBuilder bundleBuilder = new BundleBuilder(ourCtx);
|
||||||
Patient p = ourCtx.newJsonParser().parseResource(Patient.class, loadResource("/r4/concurrent-bundle/patient.json"));
|
Patient p = ourCtx.newJsonParser().parseResource(Patient.class, loadResource("/r4/concurrent-bundle/patient.json"));
|
||||||
|
@ -1656,5 +1703,19 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
TestUtil.randomizeLocaleAndTimezone();
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue