package gov.samhsa.acs.documentsegmentation; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyMapOf; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import gov.samhsa.acs.audit.AcsAuditVerb; import gov.samhsa.acs.audit.AuditServiceImpl; import gov.samhsa.acs.audit.AuditVerb; import gov.samhsa.acs.audit.PredicateKey; import gov.samhsa.acs.brms.RuleExecutionService; import gov.samhsa.acs.brms.domain.Confidentiality; import gov.samhsa.acs.brms.domain.FactModel; import gov.samhsa.acs.brms.domain.ObligationPolicyDocument; import gov.samhsa.acs.brms.domain.RefrainPolicy; import gov.samhsa.acs.brms.domain.RuleExecutionContainer; import gov.samhsa.acs.brms.domain.RuleExecutionResponse; import gov.samhsa.acs.brms.domain.Sensitivity; import gov.samhsa.acs.brms.domain.SubjectPurposeOfUse; import gov.samhsa.acs.brms.domain.UsPrivacyLaw; import gov.samhsa.acs.brms.domain.XacmlResult; import gov.samhsa.acs.common.exception.DS4PException; import gov.samhsa.acs.common.tool.DocumentAccessorImpl; import gov.samhsa.acs.common.tool.DocumentXmlConverterImpl; import gov.samhsa.acs.common.tool.FileReader; import gov.samhsa.acs.common.tool.FileReaderImpl; import gov.samhsa.acs.common.tool.SimpleMarshallerImpl; import gov.samhsa.acs.common.tool.XmlTransformerImpl; import gov.samhsa.acs.common.util.FileHelper; import gov.samhsa.acs.common.validation.XmlValidation; import gov.samhsa.acs.common.validation.XmlValidationResult; import gov.samhsa.acs.common.validation.exception.InvalidXmlDocumentException; import gov.samhsa.acs.common.validation.exception.XmlDocumentReadFailureException; import gov.samhsa.acs.documentsegmentation.dto.SegmentDocumentResponse; import gov.samhsa.acs.documentsegmentation.exception.InvalidOriginalClinicalDocumentException; import gov.samhsa.acs.documentsegmentation.exception.InvalidSegmentedClinicalDocumentException; import gov.samhsa.acs.documentsegmentation.tools.AdditionalMetadataGeneratorForSegmentedClinicalDocumentImpl; import gov.samhsa.acs.documentsegmentation.tools.DocumentEditorImpl; import gov.samhsa.acs.documentsegmentation.tools.DocumentFactModelExtractorImpl; import gov.samhsa.acs.documentsegmentation.tools.DocumentRedactor; import gov.samhsa.acs.documentsegmentation.tools.DocumentRedactorImpl; import gov.samhsa.acs.documentsegmentation.tools.DocumentTaggerImpl; import gov.samhsa.acs.documentsegmentation.tools.EmbeddedClinicalDocumentExtractorImpl; import gov.samhsa.acs.documentsegmentation.tools.MetadataGeneratorImpl; import gov.samhsa.acs.documentsegmentation.tools.dto.RedactedDocument; import gov.samhsa.acs.documentsegmentation.valueset.ValueSetServiceImplMock; import gov.samhsa.consent2share.schema.ruleexecutionservice.AssertAndExecuteClinicalFactsResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESedeKeySpec; import javax.xml.xpath.XPathExpressionException; import org.apache.xml.security.encryption.XMLCipher; import org.apache.xml.security.encryption.XMLEncryptionException; import org.apache.xml.security.utils.EncryptionConstants; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.test.util.ReflectionTestUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import ch.qos.logback.audit.AuditException; public class DocumentSegmentationImplTest { private static String xacmlResult; private static String factModel; private static XacmlResult xacmlResultObj; private static FactModel factModelObj; private static RuleExecutionContainer ruleExecutionContainerObj; private static String senderEmailAddress; private static String recipientEmailAddress; private static FileReader fileReader; private static DocumentXmlConverterImpl documentXmlConverter; private static SimpleMarshallerImpl marshaller; private static RuleExecutionService ruleExecutionServiceClientMock; private static AuditServiceImpl auditServiceMock; private static DocumentEditorImpl documentEditorMock; private static DocumentFactModelExtractorImpl documentFactModelExtractorMock; private static SimpleMarshallerImpl marshallerMock; private static DocumentRedactor documentRedactorMock; private static DocumentTaggerImpl documentTaggerMock; private static EmbeddedClinicalDocumentExtractorImpl embeddedClinicalDocumentExtractorMock; private static AdditionalMetadataGeneratorForSegmentedClinicalDocumentImpl additionalMetadataGeneratorForSegmentedClinicalDocumentImplMock; private static XmlValidation xmlValidatorMock; private static RedactedDocument redactedDocumentMock; private static String testOriginal_C32_xml; private static String testFactModel_xml; private static String testExecutionResponseContainer_xml; private static String testRedacted_C32_xml; private static String testTagged_C32_xml; private static String testMasked_C32_xml; private static String testEncrypted_C32_xml; private static String testAdditionalMetadata_xml; private static final String PURPOSE_OF_USE = "TREATMENT"; private static final String XDS_ENTRY_ID = "123"; private static final String MESSAGE_ID = "cf8cace6-6331-4a45-8e79-5bf503925be4"; private static DocumentSegmentation documentSegmentation; private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Before public void setUp() throws XPathExpressionException, XMLEncryptionException, Exception { senderEmailAddress = "leo.smith@direct.obhita-stage.org"; recipientEmailAddress = "Duane_Decouteau@direct.healthvault-stage.com"; // File reader fileReader = new FileReaderImpl(); // Marshaller marshaller = new SimpleMarshallerImpl(); // Document-Xml converter documentXmlConverter = new DocumentXmlConverterImpl(); testOriginal_C32_xml = fileReader.readFile("testOriginal_C32.xml"); testFactModel_xml = fileReader.readFile("testFactModel.xml"); testExecutionResponseContainer_xml = fileReader .readFile("testExecutionResponseContainer.xml"); testRedacted_C32_xml = fileReader.readFile("testRedacted_C32.xml"); testTagged_C32_xml = fileReader.readFile("testTagged_C32.xml"); testMasked_C32_xml = fileReader.readFile("testMasked_C32.xml"); testEncrypted_C32_xml = fileReader.readFile("testEncrypted_C32.xml"); testAdditionalMetadata_xml = fileReader .readFile("testAdditionalMetadata.xml"); redactedDocumentMock = mock(RedactedDocument.class); when(redactedDocumentMock.getRedactedDocument()).thenReturn( testRedacted_C32_xml); // Xacml result xacmlResult = "<xacmlResult><pdpDecision>PERMIT</pdpDecision><purposeOfUse>TREATMENT</purposeOfUse><messageId>cf8cace6-6331-4a45-8e79-5bf503925be4</messageId><homeCommunityId>2.16.840.1.113883.3.467</homeCommunityId><pdpObligation>51848-0</pdpObligation><pdpObligation>121181</pdpObligation><pdpObligation>47420-5</pdpObligation><pdpObligation>46240-8</pdpObligation><pdpObligation>ETH</pdpObligation><pdpObligation>GDIS</pdpObligation><pdpObligation>PSY</pdpObligation><pdpObligation>SEX</pdpObligation><pdpObligation>18748-4</pdpObligation><pdpObligation>11504-8</pdpObligation><pdpObligation>34117-2</pdpObligation></xacmlResult>"; xacmlResultObj = setXacmlResult(); // FactModel factModel = "<FactModel><xacmlResult><pdpDecision>PERMIT</pdpDecision><purposeOfUse>TREATMENT</purposeOfUse><messageId>cf8cace6-6331-4a45-8e79-5bf503925be4</messageId><homeCommunityId>2.16.840.1.113883.3.467</homeCommunityId><pdpObligation>51848-0</pdpObligation><pdpObligation>121181</pdpObligation><pdpObligation>47420-5</pdpObligation><pdpObligation>46240-8</pdpObligation><pdpObligation>ETH</pdpObligation><pdpObligation>GDIS</pdpObligation><pdpObligation>PSY</pdpObligation><pdpObligation>SEX</pdpObligation><pdpObligation>18748-4</pdpObligation><pdpObligation>11504-8</pdpObligation><pdpObligation>34117-2</pdpObligation></xacmlResult><ClinicalFacts><ClinicalFact><code>111880001</code><displayName>Acute HIV</displayName><codeSystem>2.16.840.1.113883.6.96</codeSystem><codeSystemName/><c32SectionTitle>Problems</c32SectionTitle><c32SectionLoincCode>11450-4</c32SectionLoincCode><observationId>d11275e7-67ae-11db-bd13-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>66214007</code><displayName>Substance Abuse Disorder</displayName><codeSystem>2.16.840.1.113883.6.96</codeSystem><codeSystemName/><c32SectionTitle>Problems</c32SectionTitle><c32SectionLoincCode>11450-4</c32SectionLoincCode><observationId>e11275e7-67ae-11db-bd13-0800200c9a66b827vs52h7</observationId></ClinicalFact><ClinicalFact><code>234391009</code><displayName>Sickle Cell Anemia</displayName><codeSystem>2.16.840.1.113883.6.96</codeSystem><codeSystemName>SNOMED</codeSystemName><c32SectionTitle>Problems</c32SectionTitle><c32SectionLoincCode>11450-4</c32SectionLoincCode><observationId>ab1791b0-5c71-11db-b0de-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>233604007</code><displayName>Pneumonia</displayName><codeSystem>2.16.840.1.113883.6.96</codeSystem><codeSystemName>SNOMED</codeSystemName><c32SectionTitle>Problems</c32SectionTitle><c32SectionLoincCode>11450-4</c32SectionLoincCode><observationId>9d3d416d-45ab-4da1-912f-4583e0632000</observationId></ClinicalFact><ClinicalFact><code>22298006</code><displayName>Myocardial infarction</displayName><codeSystem>2.16.840.1.113883.6.96</codeSystem><codeSystemName>SNOMED</codeSystemName><c32SectionTitle>Problems</c32SectionTitle><c32SectionLoincCode>11450-4</c32SectionLoincCode><observationId/></ClinicalFact><ClinicalFact><code>77386006</code><displayName>Patient currently pregnant</displayName><codeSystem>2.16.840.1.113883.6.96</codeSystem><codeSystemName>SNOMED</codeSystemName><c32SectionTitle>Problems</c32SectionTitle><c32SectionLoincCode>11450-4</c32SectionLoincCode><observationId/></ClinicalFact><ClinicalFact><code>70618</code><displayName>Penicillin</displayName><codeSystem>2.16.840.1.113883.6.88</codeSystem><codeSystemName>RxNorm</codeSystemName><c32SectionTitle>Allergies and Adverse Reactions</c32SectionTitle><c32SectionLoincCode>48765-2</c32SectionLoincCode><observationId>4adc1020-7b14-11db-9fe1-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>1191</code><displayName>Aspirin</displayName><codeSystem>2.16.840.1.113883.6.88</codeSystem><codeSystemName>RxNorm</codeSystemName><c32SectionTitle>Allergies and Adverse Reactions</c32SectionTitle><c32SectionLoincCode>48765-2</c32SectionLoincCode><observationId>eb936011-7b17-11db-9fe1-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>2670</code><displayName>Codeine</displayName><codeSystem>2.16.840.1.113883.6.88</codeSystem><codeSystemName>RxNorm</codeSystemName><c32SectionTitle>Allergies and Adverse Reactions</c32SectionTitle><c32SectionLoincCode>48765-2</c32SectionLoincCode><observationId>c3df3b60-7b18-11db-9fe1-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>6736007</code><displayName>moderate</displayName><codeSystem>2.16.840.1.113883.6.96</codeSystem><codeSystemName>SNOMED</codeSystemName><c32SectionTitle>Allergies and Adverse Reactions</c32SectionTitle><c32SectionLoincCode>48765-2</c32SectionLoincCode><observationId/></ClinicalFact><ClinicalFact><code>993536</code><displayName>Bupropion Hydrochloride 200 MG Extended Release Tablet</displayName><codeSystem>2.16.840.1.113883.6.88</codeSystem><codeSystemName>RxNorm</codeSystemName><c32SectionTitle>Medications</c32SectionTitle><c32SectionLoincCode>10160-0</c32SectionLoincCode><observationId>cdbd5b05-6cde-11db-9fe1-08002tg964rfh8823ejba-00c9a66</observationId></ClinicalFact><ClinicalFact><code>309362</code><displayName>Clopidogrel 75 MG oral tablet</displayName><codeSystem>2.16.840.1.113883.6.88</codeSystem><codeSystemName>RxNorm</codeSystemName><c32SectionTitle>Medications</c32SectionTitle><c32SectionLoincCode>10160-0</c32SectionLoincCode><observationId>cdbd5b05-6cde-11db-9fe1-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>430618</code><displayName>Metoprolol 25 MG oral tablet</displayName><codeSystem>2.16.840.1.113883.6.88</codeSystem><codeSystemName>RxNorm</codeSystemName><c32SectionTitle>Medications</c32SectionTitle><c32SectionLoincCode>10160-0</c32SectionLoincCode><observationId>cdbd5b01-6cde-11db-9fe1-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>312615</code><displayName>Prednisone 20 MG oral tablet</displayName><codeSystem>2.16.840.1.113883.6.88</codeSystem><codeSystemName>RxNorm</codeSystemName><c32SectionTitle>Medications</c32SectionTitle><c32SectionLoincCode>10160-0</c32SectionLoincCode><observationId>cdbd5b03-6cde-11db-9fe1-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>197454</code><displayName>Cephalexin 500 MG oral tablet</displayName><codeSystem>2.16.840.1.113883.6.88</codeSystem><codeSystemName>RxNorm</codeSystemName><c32SectionTitle>Medications</c32SectionTitle><c32SectionLoincCode>10160-0</c32SectionLoincCode><observationId>cdbd5b07-6cde-11db-9fe1-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>30313-1</code><displayName>HGB</displayName><codeSystem>2.16.840.1.113883.6.1</codeSystem><codeSystemName/><c32SectionTitle>Diagnostic Results</c32SectionTitle><c32SectionLoincCode>30954-2</c32SectionLoincCode><observationId>107c2dc0-67a5-11db-bd13-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>33765-9</code><displayName>WBC</displayName><codeSystem>2.16.840.1.113883.6.1</codeSystem><codeSystemName/><c32SectionTitle>Diagnostic Results</c32SectionTitle><c32SectionLoincCode>30954-2</c32SectionLoincCode><observationId>8b3fa370-67a5-11db-bd13-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>26515-7</code><displayName>PLT</displayName><codeSystem>2.16.840.1.113883.6.1</codeSystem><codeSystemName/><c32SectionTitle>Diagnostic Results</c32SectionTitle><c32SectionLoincCode>30954-2</c32SectionLoincCode><observationId>80a6c740-67a5-11db-bd13-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>2951-2</code><displayName>NA</displayName><codeSystem>2.16.840.1.113883.6.1</codeSystem><codeSystemName/><c32SectionTitle>Diagnostic Results</c32SectionTitle><c32SectionLoincCode>30954-2</c32SectionLoincCode><observationId>a40027e1-67a5-11db-bd13-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>2823-3</code><displayName>K</displayName><codeSystem>2.16.840.1.113883.6.1</codeSystem><codeSystemName/><c32SectionTitle>Diagnostic Results</c32SectionTitle><c32SectionLoincCode>30954-2</c32SectionLoincCode><observationId>a40027e2-67a5-11db-bd13-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>2075-0</code><displayName>CL</displayName><codeSystem>2.16.840.1.113883.6.1</codeSystem><codeSystemName/><c32SectionTitle>Diagnostic Results</c32SectionTitle><c32SectionLoincCode>30954-2</c32SectionLoincCode><observationId>a40027e3-67a5-11db-bd13-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>1963-8</code><displayName>HCO3</displayName><codeSystem>2.16.840.1.113883.6.1</codeSystem><codeSystemName/><c32SectionTitle>Diagnostic Results</c32SectionTitle><c32SectionLoincCode>30954-2</c32SectionLoincCode><observationId>a40027e4-67a5-11db-bd13-0800200c9a66</observationId></ClinicalFact><ClinicalFact><code>43789009</code><displayName>CBC WO DIFFERENTIAL</displayName><codeSystem>2.16.840.1.113883.6.96</codeSystem><codeSystemName/><c32SectionTitle>Diagnostic Results</c32SectionTitle><c32SectionLoincCode>30954-2</c32SectionLoincCode><observationId/></ClinicalFact><ClinicalFact><code>20109005</code><displayName>LYTES</displayName><codeSystem>2.16.840.1.113883.6.96</codeSystem><codeSystemName>SNOMED CT</codeSystemName><c32SectionTitle>Diagnostic Results</c32SectionTitle><c32SectionLoincCode>30954-2</c32SectionLoincCode><observationId/></ClinicalFact></ClinicalFacts></FactModel>"; factModelObj = marshaller.unmarshalFromXml(FactModel.class, factModel); // Document editor mock documentEditorMock = mock(DocumentEditorImpl.class); when(documentEditorMock.setDocumentCreationDate(testOriginal_C32_xml)) .thenReturn(testOriginal_C32_xml); when( documentEditorMock.setDocumentPayloadRawData(anyString(), anyBoolean(), anyString(), anyString(), any(XacmlResult.class), anyString(), any(byte[].class), any(byte[].class))).thenReturn(null); // Fact model extractor mock documentFactModelExtractorMock = mock(DocumentFactModelExtractorImpl.class); when( documentFactModelExtractorMock.extractFactModel( testOriginal_C32_xml, xacmlResult)).thenReturn( testFactModel_xml); // BRMS client mock ruleExecutionServiceClientMock = mock(RuleExecutionService.class); when( ruleExecutionServiceClientMock .assertAndExecuteClinicalFacts(testFactModel_xml)) .thenReturn(mock(AssertAndExecuteClinicalFactsResponse.class)); when( ruleExecutionServiceClientMock.assertAndExecuteClinicalFacts( testFactModel_xml).getRuleExecutionResponseContainer()) .thenReturn(testExecutionResponseContainer_xml); final AssertAndExecuteClinicalFactsResponse cfr = mock(AssertAndExecuteClinicalFactsResponse.class); when(cfr.getRuleExecutionResponseContainer()).thenReturn( testExecutionResponseContainer_xml); doReturn(cfr).when(ruleExecutionServiceClientMock) .assertAndExecuteClinicalFacts(factModelObj); // Marshaller mock marshallerMock = mock(SimpleMarshallerImpl.class); ruleExecutionContainerObj = setRuleExecutionContainer(); when( marshallerMock.unmarshalFromXml( eq(RuleExecutionContainer.class), anyString())) .thenReturn(ruleExecutionContainerObj); when( marshallerMock.unmarshalFromXml(eq(XacmlResult.class), anyString())).thenReturn(xacmlResultObj); when(marshallerMock.unmarshalFromXml(eq(FactModel.class), anyString())) .thenReturn(factModelObj); when(marshallerMock.marshal(ruleExecutionContainerObj)).thenReturn( testExecutionResponseContainer_xml); // Documnent redactor documentRedactorMock = mock(DocumentRedactorImpl.class); when( documentRedactorMock.redactDocument(eq(testOriginal_C32_xml), eq(ruleExecutionContainerObj), isA(FactModel.class))) .thenReturn(redactedDocumentMock); // Document tagger documentTaggerMock = mock(DocumentTaggerImpl.class); when( documentTaggerMock.tagDocument(testRedacted_C32_xml, testExecutionResponseContainer_xml)).thenReturn( testTagged_C32_xml); when(documentRedactorMock.cleanUpGeneratedEntryIds(testTagged_C32_xml)) .thenReturn(testTagged_C32_xml); when( documentRedactorMock .cleanUpGeneratedServiceEventIds(testTagged_C32_xml)) .thenReturn(testTagged_C32_xml); // Audit service mock auditServiceMock = mock(AuditServiceImpl.class); doNothing().when(auditServiceMock).audit(anyObject(), anyString(), isA(AuditVerb.class), anyString(), anyMapOf(PredicateKey.class, String.class)); // Document masker /* * documentMaskerMock = mock(DocumentMaskerImpl.class); when( * documentMaskerMock.maskDocument(eq(testTagged_C32_xml), * any(Key.class), eq(ruleExecutionContainerObj), * eq(xacmlResultObj))).thenReturn(testMasked_C32_xml); */ // Document encrypter /* * documentEncrypterMock = mock(DocumentEncrypterImpl.class); when( * documentEncrypterMock.encryptDocument(any(Key.class), * eq(testMasked_C32_xml), eq(ruleExecutionContainerObj))) * .thenReturn(testEncrypted_C32_xml); */ // Additional Metadata Generator For Segmented Clinical Document additionalMetadataGeneratorForSegmentedClinicalDocumentImplMock = mock(AdditionalMetadataGeneratorForSegmentedClinicalDocumentImpl.class); when( additionalMetadataGeneratorForSegmentedClinicalDocumentImplMock .generateMetadataXml(MESSAGE_ID, testTagged_C32_xml, testExecutionResponseContainer_xml, senderEmailAddress, recipientEmailAddress, PURPOSE_OF_USE, XDS_ENTRY_ID)).thenReturn( testAdditionalMetadata_xml); embeddedClinicalDocumentExtractorMock = mock(EmbeddedClinicalDocumentExtractorImpl.class); when( embeddedClinicalDocumentExtractorMock .extractClinicalDocumentFromFactModel(testFactModel_xml)) .thenReturn(testOriginal_C32_xml); xmlValidatorMock = mock(XmlValidation.class); when(xmlValidatorMock.validate(testOriginal_C32_xml)).thenReturn(true); when(xmlValidatorMock.validate(testTagged_C32_xml)).thenReturn(true); when(xmlValidatorMock.validate(testMasked_C32_xml)).thenReturn(true); when(xmlValidatorMock.validate(testEncrypted_C32_xml)).thenReturn(true); final XmlValidationResult xmlValidationResultMock = mock(XmlValidationResult.class); when(xmlValidationResultMock.isValid()).thenReturn(true); when(xmlValidatorMock.validateWithAllErrors(testOriginal_C32_xml)) .thenReturn(xmlValidationResultMock); when(xmlValidatorMock.validateWithAllErrors(testTagged_C32_xml)) .thenReturn(xmlValidationResultMock); when(xmlValidatorMock.validateWithAllErrors(testMasked_C32_xml)) .thenReturn(xmlValidationResultMock); when(xmlValidatorMock.validateWithAllErrors(testEncrypted_C32_xml)) .thenReturn(xmlValidationResultMock); documentSegmentation = new DocumentSegmentationImpl( ruleExecutionServiceClientMock, auditServiceMock, documentEditorMock, marshallerMock, documentRedactorMock, documentTaggerMock, documentFactModelExtractorMock, embeddedClinicalDocumentExtractorMock, new ValueSetServiceImplMock(fileReader), additionalMetadataGeneratorForSegmentedClinicalDocumentImplMock); } @Test public void testSegmentDocument_Decrypt_Document_From_Zip() { Document processedDoc; DESedeKeySpec desedeEncryptKeySpec; DESedeKeySpec desedeMaskKeySpec; try { org.apache.xml.security.Init.init(); final ZipInputStream zis = new ZipInputStream(Thread .currentThread().getContextClassLoader() .getResourceAsStream("Patientone_Asample_XDM.zip")); final byte[] processDocBytes = entryBytesFromZipBytes(zis, "SUBSET01/DOCUMENT.xml"); final String processDocString = new String(processDocBytes); FileHelper.writeStringToFile(processDocString, "processDocString.xml"); processedDoc = documentXmlConverter.loadDocument(processDocString); final byte[] kekEncryptionKeyBytes = entryBytesFromZipBytes(zis, "kekEncryptionKey"); desedeEncryptKeySpec = new DESedeKeySpec(kekEncryptionKeyBytes); final SecretKeyFactory skfEncrypt = SecretKeyFactory .getInstance("DESede"); final SecretKey desedeEncryptKey = skfEncrypt .generateSecret(desedeEncryptKeySpec); final byte[] kekMaskingKeyBytes = entryBytesFromZipBytes(zis, "kekMaskingKey"); desedeMaskKeySpec = new DESedeKeySpec(kekMaskingKeyBytes); final SecretKeyFactory skfMask = SecretKeyFactory .getInstance("DESede"); final SecretKey desedeMaskKey = skfMask .generateSecret(desedeMaskKeySpec); zis.close(); /************************************************* * DECRYPT DOCUMENT *************************************************/ final Element encryptedDataElement = (Element) processedDoc .getElementsByTagNameNS( EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_ENCRYPTEDDATA).item(0); /* * The key to be used for decrypting xml data would be obtained from * the keyinfo of the EncrypteData using the kek. */ final XMLCipher xmlCipher = XMLCipher.getInstance(); xmlCipher.init(XMLCipher.DECRYPT_MODE, null); xmlCipher.setKEK(desedeEncryptKey); /* * The following doFinal call replaces the encrypted data with * decrypted contents in the document. */ if (encryptedDataElement != null) { xmlCipher.doFinal(processedDoc, encryptedDataElement); } /************************************************* * DECRYPT ELEMENTS *************************************************/ NodeList encryptedDataElements = processedDoc .getElementsByTagNameNS( EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_ENCRYPTEDDATA); while (encryptedDataElements.getLength() > 0) { /* * The key to be used for decrypting xml data would be obtained * from the keyinfo of the EncrypteData using the kek. */ final XMLCipher xmlMaskCipher = XMLCipher.getInstance(); xmlMaskCipher.init(XMLCipher.DECRYPT_MODE, null); xmlMaskCipher.setKEK(desedeMaskKey); xmlMaskCipher.doFinal(processedDoc, (Element) encryptedDataElements.item(0)); encryptedDataElements = processedDoc.getElementsByTagNameNS( EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_ENCRYPTEDDATA); } FileHelper.writeDocToFile(processedDoc, "unitTest_DecryptedUnMasked_C32_from_zip.xml"); } catch (final Exception e) { logger.error(e.getMessage(), e); } } @Test(expected = DS4PException.class) public void testSegmentDocument_Given_Real_DocumentEditor_Throws_DS4PException() throws IOException, InvalidOriginalClinicalDocumentException, InvalidSegmentedClinicalDocumentException, AuditException { // Arrange final boolean xdm = true; final boolean ecrypt = true; final XmlValidationResult xmlValidationResultTrue = mock(XmlValidationResult.class); when(xmlValidationResultTrue.isValid()).thenReturn(true); when(xmlValidatorMock.validateWithAllErrors("")).thenReturn( xmlValidationResultTrue); final DocumentEditorImpl realDocumentEditorImpl = new DocumentEditorImpl( new MetadataGeneratorImpl(new XmlTransformerImpl( new SimpleMarshallerImpl())), new FileReaderImpl(), new DocumentXmlConverterImpl(), new DocumentAccessorImpl()); final DocumentSegmentationImpl documentSegmentationWithRealDocumentEditor = new DocumentSegmentationImpl( ruleExecutionServiceClientMock, auditServiceMock, realDocumentEditorImpl, marshallerMock, documentRedactorMock, documentTaggerMock, documentFactModelExtractorMock, embeddedClinicalDocumentExtractorMock, new ValueSetServiceImplMock(fileReader), additionalMetadataGeneratorForSegmentedClinicalDocumentImplMock); ReflectionTestUtils.setField( documentSegmentationWithRealDocumentEditor, "xmlValidator", xmlValidatorMock); // Act @SuppressWarnings("unused") final SegmentDocumentResponse resp = documentSegmentationWithRealDocumentEditor .segmentDocument("", "", false, true, true); // Assert // expect DS4PException } @Test(expected = DS4PException.class) public void testSegmentDocument_Given_Real_Marshaller_Throws_DS4PException() throws IOException, InvalidOriginalClinicalDocumentException, InvalidSegmentedClinicalDocumentException, AuditException { // Arrange final boolean xdm = true; final boolean ecrypt = true; final XmlValidationResult xmlValidationResultMock = mock(XmlValidationResult.class); when(xmlValidationResultMock.isValid()).thenReturn(true); when(xmlValidatorMock.validateWithAllErrors("")).thenReturn( xmlValidationResultMock); final DocumentSegmentationImpl documentSegmentationWithRealMarshaller = new DocumentSegmentationImpl( ruleExecutionServiceClientMock, auditServiceMock, documentEditorMock, new SimpleMarshallerImpl(), documentRedactorMock, documentTaggerMock, documentFactModelExtractorMock, embeddedClinicalDocumentExtractorMock, new ValueSetServiceImplMock(fileReader), additionalMetadataGeneratorForSegmentedClinicalDocumentImplMock); ReflectionTestUtils.setField(documentSegmentationWithRealMarshaller, "xmlValidator", xmlValidatorMock); // Act @SuppressWarnings("unused") final SegmentDocumentResponse resp = documentSegmentationWithRealMarshaller .segmentDocument("", "", false, true, true); // Assert // expect DS4PException } @Test(expected = XmlDocumentReadFailureException.class) public void testSegmentDocument_Given_Real_Marshaller_Throws_XmlDocumentReadFailureException() throws IOException, InvalidXmlDocumentException, AuditException { // Arrange final boolean xdm = true; final boolean ecrypt = true; final XmlValidation validationMock = mock(XmlValidation.class); doThrow(XmlDocumentReadFailureException.class).when(validationMock) .validateWithAllErrors(""); final DocumentSegmentationImpl documentSegmentationWithRealMarshaller = new DocumentSegmentationImpl( ruleExecutionServiceClientMock, auditServiceMock, documentEditorMock, new SimpleMarshallerImpl(), documentRedactorMock, documentTaggerMock, documentFactModelExtractorMock, embeddedClinicalDocumentExtractorMock, new ValueSetServiceImplMock(fileReader), additionalMetadataGeneratorForSegmentedClinicalDocumentImplMock); ReflectionTestUtils.setField(documentSegmentationWithRealMarshaller, "xmlValidator", validationMock); // Act @SuppressWarnings("unused") final SegmentDocumentResponse resp = documentSegmentationWithRealMarshaller .segmentDocument("", "", false, true, true); // Assert // expect DS4PException } @Test public void testSegmentDocument_Given_XdmFalse_EncryptFalse() throws Exception { // Arrange final boolean xdm = false; final boolean ecrypt = false; ReflectionTestUtils.setField(documentSegmentation, "xmlValidator", xmlValidatorMock); // Act final SegmentDocumentResponse resp = documentSegmentation .segmentDocument(testOriginal_C32_xml, xacmlResult, false, true, true); documentSegmentation.setAdditionalMetadataForSegmentedClinicalDocument( resp, senderEmailAddress, recipientEmailAddress, XDS_ENTRY_ID, xacmlResultObj); documentSegmentation.setDocumentPayloadRawData(resp, xdm, senderEmailAddress, recipientEmailAddress, xacmlResultObj); // Assert validateResponse(resp, ecrypt); } @Test public void testSegmentDocument_Given_XdmFalse_EncryptFalse_AuditTrue() throws Exception { // Arrange final boolean xdm = false; final boolean ecrypt = false; ReflectionTestUtils.setField(documentSegmentation, "xmlValidator", xmlValidatorMock); // Act final SegmentDocumentResponse resp = documentSegmentation .segmentDocument(testOriginal_C32_xml, xacmlResult, true, true, true); documentSegmentation.setAdditionalMetadataForSegmentedClinicalDocument( resp, senderEmailAddress, recipientEmailAddress, XDS_ENTRY_ID, xacmlResultObj); documentSegmentation.setDocumentPayloadRawData(resp, xdm, senderEmailAddress, recipientEmailAddress, xacmlResultObj); // Assert validateResponse(resp, ecrypt); final String s = null; verify(auditServiceMock, times(1)).audit( eq((Object) documentSegmentation), eq("cf8cace6-6331-4a45-8e79-5bf503925be4"), eq((AuditVerb) AcsAuditVerb.SEGMENT_DOCUMENT), eq(s), anyMapOf(PredicateKey.class, String.class)); } @SuppressWarnings("unchecked") @Test(expected = InvalidSegmentedClinicalDocumentException.class) public void testSegmentDocument_Given_XdmFalse_EncryptFalse_Throws_InvalidSegmentedClinicalDocumentException() throws Exception { // Arrange final boolean xdm = false; final boolean ecrypt = false; final XmlValidation validationMock = mock(XmlValidation.class); final XmlValidationResult xmlValidationResultMockFalse = mock(XmlValidationResult.class); final XmlValidationResult xmlValidationResultMockTrue = mock(XmlValidationResult.class); when(xmlValidationResultMockFalse.isValid()).thenReturn(false); when(xmlValidationResultMockTrue.isValid()).thenReturn(true); when(validationMock.validateWithAllErrors(testOriginal_C32_xml)) .thenReturn(xmlValidationResultMockTrue); when(validationMock.validateWithAllErrors(testTagged_C32_xml)) .thenReturn(xmlValidationResultMockFalse); ReflectionTestUtils.setField(documentSegmentation, "xmlValidator", validationMock); // Act final SegmentDocumentResponse resp = documentSegmentation .segmentDocument(testOriginal_C32_xml, xacmlResult, false, true, true); documentSegmentation.setAdditionalMetadataForSegmentedClinicalDocument( resp, senderEmailAddress, recipientEmailAddress, XDS_ENTRY_ID, xacmlResultObj); documentSegmentation.setDocumentPayloadRawData(resp, xdm, senderEmailAddress, recipientEmailAddress, xacmlResultObj); // Assert validateResponse(resp, ecrypt); } @SuppressWarnings("unchecked") @Test(expected = XmlDocumentReadFailureException.class) public void testSegmentDocument_Given_XdmFalse_EncryptFalse_Throws_XmlDocumentReadFailureException() throws Exception { // Arrange final boolean xdm = false; final boolean ecrypt = false; final XmlValidation validationMock = mock(XmlValidation.class); final XmlValidationResult xmlValidationResultTrue = mock(XmlValidationResult.class); when(xmlValidationResultTrue.isValid()).thenReturn(true); when(validationMock.validateWithAllErrors(testOriginal_C32_xml)) .thenReturn(xmlValidationResultTrue); when(validationMock.validateWithAllErrors(testTagged_C32_xml)) .thenThrow(XmlDocumentReadFailureException.class); ReflectionTestUtils.setField(documentSegmentation, "xmlValidator", validationMock); // Act final SegmentDocumentResponse resp = documentSegmentation .segmentDocument(testOriginal_C32_xml, xacmlResult, false, true, true); documentSegmentation.setAdditionalMetadataForSegmentedClinicalDocument( resp, senderEmailAddress, recipientEmailAddress, XDS_ENTRY_ID, xacmlResultObj); documentSegmentation.setDocumentPayloadRawData(resp, xdm, senderEmailAddress, recipientEmailAddress, xacmlResultObj); // Assert validateResponse(resp, ecrypt); } @Test public void testSegmentDocument_Given_XdmTrue_EncryptFalse() throws Exception { // Arrange final boolean xdm = true; final boolean ecrypt = false; ReflectionTestUtils.setField(documentSegmentation, "xmlValidator", xmlValidatorMock); // Act final SegmentDocumentResponse resp = documentSegmentation .segmentDocument(testOriginal_C32_xml, xacmlResult, false, true, true); documentSegmentation.setDocumentPayloadRawData(resp, xdm, senderEmailAddress, recipientEmailAddress, xacmlResultObj); documentSegmentation.setAdditionalMetadataForSegmentedClinicalDocument( resp, senderEmailAddress, recipientEmailAddress, "123", xacmlResultObj); // Assert validateResponse(resp, ecrypt); } private byte[] entryBytesFromZipBytes(ZipInputStream zip_inputstream, String entryName) throws IOException { ZipEntry current_zip_entry = null; byte[] buf = new byte[4096]; boolean found = false; current_zip_entry = zip_inputstream.getNextEntry(); while (current_zip_entry != null && !found) { if (current_zip_entry.getName().equals(entryName)) { found = true; final ByteArrayOutputStream output = streamToOutputByteStream(zip_inputstream); buf = output.toByteArray(); output.flush(); output.close(); } else { current_zip_entry = zip_inputstream.getNextEntry(); } } return buf; } private ByteArrayOutputStream streamToOutputByteStream( ZipInputStream zip_inputstream) throws IOException { final ByteArrayOutputStream output = new ByteArrayOutputStream(); int data = 0; while ((data = zip_inputstream.read()) != -1) { output.write(data); } return output; } private void validateResponse(SegmentDocumentResponse resp, boolean encrypt) throws SAXException, IOException { // Assert masked document logger.debug(resp.getSegmentedDocumentXml()); assertEquals(testTagged_C32_xml, resp.getSegmentedDocumentXml()); // Assert processed document logger.debug(resp.getDocumentPayloadRawData().toString()); assertTrue(resp.getDocumentPayloadRawData().toString() .startsWith("javax.activation.DataHandler@")); // Assert processing metadata final String metadata = resp.getPostSegmentationMetadataXml(); logger.debug(metadata); assertNotNull(metadata); assertTrue(metadata .contains("<rim:Value>2.16.840.1.113883.1.11.16926</rim:Value>")); assertTrue(metadata .contains("<rim:Value>^^Internet^leo.smith@direct.obhita-stage.org</rim:Value>")); assertTrue(metadata .contains("<rim:Value>^^Internet^Duane_Decouteau@direct.healthvault-stage.com</rim:Value>")); assertTrue(metadata.contains("nodeRepresentation=\"TREATMENT\">")); assertTrue(metadata .contains("<rim:LocalizedString value=\"Treatment\"/>")); assertTrue(metadata.contains("nodeRepresentation=\"ENCRYPT\">")); assertTrue(metadata .contains("<rim:LocalizedString value=\"ENCRYPT\"/>")); assertTrue(metadata.contains("nodeRepresentation=\"NODSCLCD\">")); assertTrue(metadata .contains("<rim:LocalizedString value=\"NODSCLCD\"/>")); } private static RuleExecutionContainer setRuleExecutionContainer() { final RuleExecutionContainer container = new RuleExecutionContainer(); final RuleExecutionResponse r1 = new RuleExecutionResponse(); r1.setC32SectionLoincCode("11450-4"); r1.setC32SectionTitle("Problems"); r1.setCode("66214007"); r1.setCodeSystemName("SNOMED CT"); r1.setDisplayName("Substance Abuse Disorder"); r1.setDocumentObligationPolicy(ObligationPolicyDocument.ENCRYPT); r1.setDocumentRefrainPolicy(RefrainPolicy.NODSCLCD); r1.setImpliedConfSection(Confidentiality.R); r1.setItemAction("REDACT"); r1.setObservationId("e11275e7-67ae-11db-bd13-0800200c9a66b827vs52h7"); r1.setSensitivity(Sensitivity.ETH); r1.setUSPrivacyLaw(UsPrivacyLaw._42CFRPart2); final RuleExecutionResponse r2 = new RuleExecutionResponse(); r2.setC32SectionLoincCode("11450-4"); r2.setC32SectionTitle("Problems"); r2.setCode("111880001"); r2.setCodeSystemName("SNOMED CT"); r2.setDisplayName("Acute HIV"); r2.setDocumentObligationPolicy(ObligationPolicyDocument.ENCRYPT); r2.setDocumentRefrainPolicy(RefrainPolicy.NODSCLCD); r2.setImpliedConfSection(Confidentiality.R); r2.setItemAction("MASK"); r2.setObservationId("d11275e7-67ae-11db-bd13-0800200c9a66"); r2.setSensitivity(Sensitivity.HIV); r2.setUSPrivacyLaw(UsPrivacyLaw._42CFRPart2); final List<RuleExecutionResponse> list = new LinkedList<RuleExecutionResponse>(); list.add(r1); list.add(r2); container.setExecutionResponseList(list); return container; } private static XacmlResult setXacmlResult() { final XacmlResult xacmlResultObject = new XacmlResult(); xacmlResultObject.setPdpDecision("PERMIT"); xacmlResultObject .setSubjectPurposeOfUse(SubjectPurposeOfUse.HEALTHCARE_TREATMENT); xacmlResultObject.setMessageId("cf8cace6-6331-4a45-8e79-5bf503925be4"); xacmlResultObject.setHomeCommunityId("2.16.840.1.113883.3.467"); final String[] o = { "51848-0", "121181", "47420-5", "46240-8", "ETH", "GDIS", "PSY", "SEX", "18748-4", "11504-8", "34117-2" }; final List<String> obligations = Arrays.asList(o); xacmlResultObject.setPdpObligations(obligations); return xacmlResultObject; } }