diff --git a/server/src/test/java/com/mirth/connect/model/converters/TestUtil.java b/server/src/test/java/com/mirth/connect/model/converters/TestUtil.java index 405550a60f..49ce526840 100644 --- a/server/src/test/java/com/mirth/connect/model/converters/TestUtil.java +++ b/server/src/test/java/com/mirth/connect/model/converters/TestUtil.java @@ -52,4 +52,12 @@ public static String prettyPrintXml(String input) throws Exception { public static String convertCRToCRLF(String input) { return input.replaceAll("\r", "\r\n"); } + + /** + * Collapses CRLF/CR/LF to LF and trims surrounding whitespace, so that expected values stored + * in CRLF fixture files compare equal to output whose line separator is platform-dependent. + */ + public static String normalizeLineEndings(String input) { + return input.replaceAll("\r\n|\r|\n", "\n").trim(); + } } diff --git a/server/src/test/java/com/mirth/connect/plugins/datatypes/hl7v2/ER7SerializerTest.java b/server/src/test/java/com/mirth/connect/plugins/datatypes/hl7v2/ER7SerializerTest.java index a7319dca5a..45072bdc82 100644 --- a/server/src/test/java/com/mirth/connect/plugins/datatypes/hl7v2/ER7SerializerTest.java +++ b/server/src/test/java/com/mirth/connect/plugins/datatypes/hl7v2/ER7SerializerTest.java @@ -1,27 +1,95 @@ package com.mirth.connect.plugins.datatypes.hl7v2; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; import org.apache.commons.io.FileUtils; import org.junit.BeforeClass; import org.junit.Test; +import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import com.mirth.connect.donkey.model.message.MessageSerializerException; import com.mirth.connect.model.datatype.SerializerProperties; public class ER7SerializerTest { + private static final String XML_DECL = ""; + private static final String MSH_ER7 = "MSH|^~\\&|A\r"; + private static final String MSH_XML = "|^~\\&A"; + private static ER7Serializer serializer; - + @BeforeClass public static void setupClass() throws Exception { SerializerProperties serializerProperties = new SerializerProperties(new HL7v2SerializationProperties(), new HL7v2DeserializationProperties(), null); serializer = new ER7Serializer(serializerProperties); } + private static ER7Serializer serializerWith(boolean convertLineBreaks, String deserializationSegmentDelimiter) { + HL7v2SerializationProperties serializationProperties = new HL7v2SerializationProperties(); + serializationProperties.setConvertLineBreaks(convertLineBreaks); + + HL7v2DeserializationProperties deserializationProperties = new HL7v2DeserializationProperties(); + deserializationProperties.setSegmentDelimiter(deserializationSegmentDelimiter); + + return new ER7Serializer(new SerializerProperties(serializationProperties, deserializationProperties, null)); + } + + @Test + public void testTransformWithoutSerializingConvertsToOutboundDelimiter() throws Exception { + ER7Serializer inbound = serializerWith(true, "\\r"); + ER7Serializer outbound = serializerWith(true, "\\n"); + + assertEquals("MSH|^~\\&|A\nPID|1", inbound.transformWithoutSerializing("MSH|^~\\&|A\rPID|1", outbound)); + } + + @Test + public void testTransformWithoutSerializingNormalizesMixedLineBreaks() throws Exception { + ER7Serializer sameEnds = serializerWith(true, "\\r"); + + assertEquals("MSH|^~\\&|A\rPID|1", sameEnds.transformWithoutSerializing("MSH|^~\\&|A\r\nPID|1", sameEnds)); + } + + @Test + public void testTransformWithoutSerializingStillConvertsWhenDelimitersMatch() throws Exception { + /* + * convertLineBreaks is on, so the message goes through conversion and is returned even + * though it is unchanged. Only the convertLineBreaks-off case short-circuits to null. + */ + ER7Serializer sameEnds = serializerWith(true, "\\r"); + + assertEquals("MSH|^~\\&|A\rPID|1", sameEnds.transformWithoutSerializing("MSH|^~\\&|A\rPID|1", sameEnds)); + } + + @Test + public void testTransformWithoutSerializingReplacesDelimiterWhenLineBreakConversionIsOff() throws Exception { + /* + * The only test that reaches the StringUtils.replace branch at ER7Serializer.java:170. + * Every convertLineBreaks=true configuration short-circuits earlier: either the + * skipIntermediateDelimiter fast path at line 162, or (for the delimiters-match case) + * the convertLineBreaks(...) call at line 165 with transformed already true. Only + * convertLineBreaks=false skips that whole block and falls through to the delimiter + * comparison at line 169. + */ + ER7Serializer inbound = serializerWith(false, "\\r"); + ER7Serializer outbound = serializerWith(true, "\\n"); + + assertEquals("MSH|^~\\&|A\nPID|1", inbound.transformWithoutSerializing("MSH|^~\\&|A\rPID|1", outbound)); + } + + @Test + public void testTransformWithoutSerializingReturnsNullWhenNothingToDo() throws Exception { + ER7Serializer inbound = serializerWith(false, "\\r"); + ER7Serializer outbound = serializerWith(true, "\\r"); + + assertNull(inbound.transformWithoutSerializing("MSH|^~\\&|A\rPID|1", outbound)); + } + @Test public void testFromXMLWithExternalDTD() throws Exception { String xml = FileUtils.readFileToString(new File("tests/test-xxe-hl7-example.xml"), "UTF-8"); @@ -53,4 +121,86 @@ public void testValidFromXMLWithExternalDTD() throws Exception { assertFalse(exceptionThrown); } + + @Test + public void testToXMLWithCustomEncodingCharacters() throws Exception { + String er7 = "MSH#@%\\$#App1#Fac1\rPID#1#Smith@John"; + + assertEquals(XML_DECL + "#@%\\$" + + "App1" + + "Fac1" + + "1" + + "SmithJohn", + serializer.toXML(er7)); + } + + @Test + public void testToXMLWithHeaderOnlyAndNoTrailingFieldSeparator() throws Exception { + // Covers ER7Reader's nextDelimiter == -1 branch: MSH-2 runs to end of message. + assertEquals(XML_DECL + "|^~\\&", + serializer.toXML("MSH|^~\\&")); + } + + @Test + public void testToXMLAppliesMirth1544FixupToNonHeaderFirstSegment() throws Exception { + /* + * Characterization of ER7Reader's MIRTH-1544 fixup. The "^~&|" check is a positional + * substring test that does not require a header segment, so it also fires on a Z-segment, + * installing '&' as the subcomponent separator. Recorded as current behavior, not endorsed. + */ + assertEquals(XML_DECL + "" + + "" + + "" + + "ab" + + "", + serializer.toXML("ZZZ|^~&|a&b")); + } + + @Test + public void testToXMLRejectsMessageShorterThanSixCharacters() throws Exception { + try { + serializer.toXML("MSH"); + fail("expected MessageSerializerException"); + } catch (MessageSerializerException e) { + assertTrue(e.getCause() instanceof SAXException); + assertEquals("Unable to parse message. It is NULL or too short. MSH", e.getCause().getMessage()); + } + } + + @Test + public void testToXMLWithConsecutiveRepetitionSeparators() throws Exception { + assertEquals(XML_DECL + "" + MSH_XML + "" + + "a" + + "" + + "b" + + "", + serializer.toXML(MSH_ER7 + "PID|a~~b")); + } + + @Test + public void testToXMLWithTrailingRepetitionSeparator() throws Exception { + assertEquals(XML_DECL + "" + MSH_XML + "" + + "a" + + "" + + "", + serializer.toXML(MSH_ER7 + "PID|a~")); + } + + @Test + public void testToXMLWithTrailingEmptyFields() throws Exception { + assertEquals(XML_DECL + "" + MSH_XML + "" + + "a" + + "" + + "" + + "", + serializer.toXML(MSH_ER7 + "PID|a||")); + } + + @Test + public void testToXMLSkipsEmptySegments() throws Exception { + // StringUtils.split drops empty tokens, so a blank line between segments simply vanishes. + assertEquals(XML_DECL + "" + MSH_XML + + "1", + serializer.toXML("MSH|^~\\&|A\r\rPID|1")); + } } diff --git a/server/src/test/java/com/mirth/connect/plugins/datatypes/hl7v2/HL7SerializerTests.java b/server/src/test/java/com/mirth/connect/plugins/datatypes/hl7v2/HL7SerializerTest.java similarity index 74% rename from server/src/test/java/com/mirth/connect/plugins/datatypes/hl7v2/HL7SerializerTests.java rename to server/src/test/java/com/mirth/connect/plugins/datatypes/hl7v2/HL7SerializerTest.java index aae28422bb..2d3340472e 100644 --- a/server/src/test/java/com/mirth/connect/plugins/datatypes/hl7v2/HL7SerializerTests.java +++ b/server/src/test/java/com/mirth/connect/plugins/datatypes/hl7v2/HL7SerializerTest.java @@ -11,27 +11,19 @@ import java.io.File; -import junit.framework.Assert; - import org.apache.commons.io.FileUtils; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import com.mirth.connect.model.converters.TestUtil; -public class HL7SerializerTests { +public class HL7SerializerTest { private HL7v2DataTypeProperties defaultProperties; @Before public void setUp() throws Exception { defaultProperties = new HL7v2DataTypeProperties(); - -// defaultProperties = new Properties(); -// defaultProperties.put("useStrictParser", "false"); -// defaultProperties.put("handleRepetitions", "false"); -// defaultProperties.put("handleSubcomponents", "false"); -// defaultProperties.put("inputSegmentDelimiter", "\\r\\n|\\r|\\n"); -// defaultProperties.put("outputSegmentDelimiter", "\\r"); } @Test @@ -39,7 +31,7 @@ public void testToXmlDefault() throws Exception { String input = FileUtils.readFileToString(new File("tests/test-hl7-input.txt")); String output = FileUtils.readFileToString(new File("tests/test-hl7-output.xml")); ER7Serializer serializer = new ER7Serializer(defaultProperties.getSerializerProperties()); - Assert.assertEquals(output, TestUtil.prettyPrintXml(serializer.toXML(input))); + Assert.assertEquals(TestUtil.normalizeLineEndings(output), TestUtil.normalizeLineEndings(TestUtil.prettyPrintXml(serializer.toXML(input)))); } @Test @@ -55,7 +47,7 @@ public void testToXmlWhitepsace() throws Exception { String input = FileUtils.readFileToString(new File("tests/test-hl7-whitespace-input.txt")); String output = FileUtils.readFileToString(new File("tests/test-hl7-whitespace-output.xml")); ER7Serializer serializer = new ER7Serializer(defaultProperties.getSerializerProperties()); - Assert.assertEquals(output, TestUtil.prettyPrintXml(serializer.toXML(input))); + Assert.assertEquals(TestUtil.normalizeLineEndings(output), TestUtil.normalizeLineEndings(TestUtil.prettyPrintXml(serializer.toXML(input)))); } @Test @@ -98,11 +90,6 @@ public void testFromXmlMissingSubcomponents() throws Exception { @Test public void testFromXmlSingleSegment() throws Exception { HL7v2DataTypeProperties properties = new HL7v2DataTypeProperties(); -// properties.put("useStrictParser", "false"); -// properties.put("handleRepetitions", "false"); -// properties.put("handleSubcomponents", "true"); -// properties.put("inputSegmentDelimiter", "\\r\\n|\\r|\\n"); -// properties.put("outputSegmentDelimiter", "\\r"); String input = FileUtils.readFileToString(new File("tests/test-hl7-single-segment-input.xml")); String output = FileUtils.readFileToString(new File("tests/test-hl7-single-segment-output.txt")); @@ -121,28 +108,16 @@ public void testFromXmlSingleField() throws Exception { @Test public void testToXmlWithSubcomponents() throws Exception { HL7v2DataTypeProperties properties = new HL7v2DataTypeProperties(); -// Properties properties = new Properties(); -// properties.put("useStrictParser", "false"); -// properties.put("handleRepetitions", "false"); -// properties.put("handleSubcomponents", "true"); -// properties.put("inputSegmentDelimiter", "\\r\\n|\\r|\\n"); -// properties.put("outputSegmentDelimiter", "\\r"); String input = FileUtils.readFileToString(new File("tests/test-hl7-subcomponents-input.txt")); String output = FileUtils.readFileToString(new File("tests/test-hl7-subcomponents-output.xml")); ER7Serializer serializer = new ER7Serializer(properties.getSerializerProperties()); - Assert.assertEquals(output, TestUtil.prettyPrintXml(serializer.toXML(input))); + Assert.assertEquals(TestUtil.normalizeLineEndings(output), TestUtil.normalizeLineEndings(TestUtil.prettyPrintXml(serializer.toXML(input)))); } @Test public void testFromXmlWithSubcomponents() throws Exception { HL7v2DataTypeProperties properties = new HL7v2DataTypeProperties(); -// Properties properties = new Properties(); -// properties.put("useStrictParser", "false"); -// properties.put("handleRepetitions", "false"); -// properties.put("handleSubcomponents", "true"); -// properties.put("inputSegmentDelimiter", "\\r\\n|\\r|\\n"); -// properties.put("outputSegmentDelimiter", "\\r"); String input = FileUtils.readFileToString(new File("tests/test-hl7-subcomponents-output.xml")); String output = FileUtils.readFileToString(new File("tests/test-hl7-subcomponents-input.txt")); @@ -153,28 +128,16 @@ public void testFromXmlWithSubcomponents() throws Exception { @Test public void testToXmlWithRepetitions() throws Exception { HL7v2DataTypeProperties properties = new HL7v2DataTypeProperties(); -// Properties properties = new Properties(); -// properties.put("useStrictParser", "false"); -// properties.put("handleRepetitions", "true"); -// properties.put("handleSubcomponents", "false"); -// properties.put("inputSegmentDelimiter", "\\r\\n|\\r|\\n"); -// properties.put("outputSegmentDelimiter", "\\r"); String input = FileUtils.readFileToString(new File("tests/test-hl7-repetitions-input.txt")); String output = FileUtils.readFileToString(new File("tests/test-hl7-repetitions-output.xml")); ER7Serializer serializer = new ER7Serializer(properties.getSerializerProperties()); - Assert.assertEquals(output, TestUtil.prettyPrintXml(serializer.toXML(input))); + Assert.assertEquals(TestUtil.normalizeLineEndings(output), TestUtil.normalizeLineEndings(TestUtil.prettyPrintXml(serializer.toXML(input)))); } @Test public void testFromXmlWithRepetitions() throws Exception { HL7v2DataTypeProperties properties = new HL7v2DataTypeProperties(); -// Properties properties = new Properties(); -// properties.put("useStrictParser", "false"); -// properties.put("handleRepetitions", "true"); -// properties.put("handleSubcomponents", "false"); -// properties.put("inputSegmentDelimiter", "\\r\\n|\\r|\\n"); -// properties.put("outputSegmentDelimiter", "\\r"); String input = FileUtils.readFileToString(new File("tests/test-hl7-repetitions-output.xml")); String output = FileUtils.readFileToString(new File("tests/test-hl7-repetitions-input.txt")); @@ -187,7 +150,7 @@ public void testToXmlWithBatch() throws Exception { String input = FileUtils.readFileToString(new File("tests/test-hl7-batch-input.txt")); String output = FileUtils.readFileToString(new File("tests/test-hl7-batch-output.xml")); ER7Serializer serializer = new ER7Serializer(defaultProperties.getSerializerProperties()); - Assert.assertEquals(output, TestUtil.prettyPrintXml(serializer.toXML(input))); + Assert.assertEquals(TestUtil.normalizeLineEndings(output), TestUtil.normalizeLineEndings(TestUtil.prettyPrintXml(serializer.toXML(input)))); } @Test @@ -197,4 +160,19 @@ public void testFromXmlWithBatch() throws Exception { ER7Serializer serializer = new ER7Serializer(defaultProperties.getSerializerProperties()); Assert.assertEquals(output, TestUtil.convertCRToCRLF(serializer.fromXML(input))); } + + @Test + public void testRoundTripAllEr7Fixtures() throws Exception { + String[] fixtures = { "tests/test-hl7-input.txt", "tests/test-hl7-whitespace-input.txt", + "tests/test-hl7-subcomponents-input.txt", "tests/test-hl7-repetitions-input.txt", + "tests/test-hl7-batch-input.txt" }; + + ER7Serializer serializer = new ER7Serializer(defaultProperties.getSerializerProperties()); + + for (String fixture : fixtures) { + String er7 = FileUtils.readFileToString(new File(fixture)); + + Assert.assertEquals(fixture, TestUtil.normalizeLineEndings(er7), TestUtil.normalizeLineEndings(serializer.fromXML(serializer.toXML(er7)))); + } + } } diff --git a/server/tests/test-hl7-batch-output.xml b/server/tests/test-hl7-batch-output.xml index f42adc8565..bd1535a037 100644 --- a/server/tests/test-hl7-batch-output.xml +++ b/server/tests/test-hl7-batch-output.xml @@ -236,7 +236,10 @@ MRS Shephard - Jane MRI W/&W/O CONTRAST + + Jane MRI W/ + W/O CONTRAST + 70553 diff --git a/server/tests/test-hl7-output.xml b/server/tests/test-hl7-output.xml index 4628e91f72..f8e5fc4dda 100644 --- a/server/tests/test-hl7-output.xml +++ b/server/tests/test-hl7-output.xml @@ -218,7 +218,10 @@ MRS Shephard - Jane MRI W/&W/O CONTRAST + + Jane MRI W/ + W/O CONTRAST + 70553