diff --git a/rs-e2e/src/main/java/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7Message.java b/rs-e2e/src/main/java/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7Message.java index 6d6d74afc..8e0fd9ea1 100644 --- a/rs-e2e/src/main/java/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7Message.java +++ b/rs-e2e/src/main/java/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7Message.java @@ -74,8 +74,6 @@ public HL7Segment getSegment(String name) throws HL7MessageException { public String getValue(String path) throws HL7MessageException { HL7Path hl7Path = HL7Parser.parsePath(path); - List fields = getSegment(hl7Path.segmentName()).fields(); - char[] segmentDelimiters = this.encoding.getOrderedDelimiters(); - return HL7Parser.parseFieldValue(hl7Path, fields, segmentDelimiters); + return HL7Parser.parseMessageFieldValue(hl7Path, this); } } diff --git a/rs-e2e/src/main/java/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7Parser.java b/rs-e2e/src/main/java/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7Parser.java index bbccada20..b38a1acf7 100644 --- a/rs-e2e/src/main/java/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7Parser.java +++ b/rs-e2e/src/main/java/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7Parser.java @@ -37,8 +37,16 @@ public static HL7Message parseMessage(String content) { return new HL7Message(segments, HL7Encoding.fromEncodingField(encodingCharactersField)); } - public static String parseFieldValue(HL7Path hl7Path, List fields, char[] delimiters) { + public static String parseMessageFieldValue(HL7Path hl7Path, HL7Message message) + throws HL7MessageException { + if (hl7Path == null || hl7Path.indices().length == 0) { + return ""; + } + int[] indices = hl7Path.indices(); + List fields = message.getSegment(hl7Path.segmentName()).fields(); + char[] delimiters = message.getEncoding().getOrderedDelimiters(); + if (fields == null || fields.isEmpty() || indices[0] > fields.size()) { return ""; } diff --git a/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7EncodingTest.groovy b/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7EncodingTest.groovy new file mode 100644 index 000000000..17ef6ef81 --- /dev/null +++ b/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7EncodingTest.groovy @@ -0,0 +1,55 @@ +package gov.hhs.cdc.trustedintermediary.rse2e.hl7 + +import spock.lang.Specification + +class HL7EncodingTest extends Specification { + + def "defaultEncoding should use expected default characters"() { + when: + def encodingChars = HL7Encoding.defaultEncoding() + + then: + encodingChars.getFieldDelimiter() == '|' as char + encodingChars.getComponentDelimiter() == '^' as char + encodingChars.getRepetitionDelimiter() == '~' as char + encodingChars.getEscapeCharacter() == '\\' as char + encodingChars.getSubcomponentDelimiter() == '&' as char + } + + def "fromEncodingField should use custom encoding characters when provided"() { + given: + def customEncodingChars = "@#+_" + + when: + def encodingChars = HL7Encoding.fromEncodingField(customEncodingChars) + + then: + encodingChars.getFieldDelimiter() == '|' as char + encodingChars.getComponentDelimiter() == '@' as char + encodingChars.getRepetitionDelimiter() == '#' as char + encodingChars.getEscapeCharacter() == '+' as char + encodingChars.getSubcomponentDelimiter() == '_' as char + } + + def "fromEncodingField should use default characters when encoding characters passed is blank or null"() { + when: + def blankEncodingChars = HL7Encoding.fromEncodingField("") + + then: + blankEncodingChars.getFieldDelimiter() == '|' as char + blankEncodingChars.getComponentDelimiter() == '^' as char + blankEncodingChars.getRepetitionDelimiter() == '~' as char + blankEncodingChars.getEscapeCharacter() == '\\' as char + blankEncodingChars.getSubcomponentDelimiter() == '&' as char + + when: + def nullEncodingChars = HL7Encoding.fromEncodingField(null) + + then: + nullEncodingChars.getFieldDelimiter() == '|' as char + nullEncodingChars.getComponentDelimiter() == '^' as char + nullEncodingChars.getRepetitionDelimiter() == '~' as char + nullEncodingChars.getEscapeCharacter() == '\\' as char + nullEncodingChars.getSubcomponentDelimiter() == '&' as char + } +} diff --git a/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7ExpressionEvaluatorTest.groovy b/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7ExpressionEvaluatorTest.groovy index 3bd96e6c9..813a12fd0 100644 --- a/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7ExpressionEvaluatorTest.groovy +++ b/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7ExpressionEvaluatorTest.groovy @@ -253,16 +253,16 @@ PID|1||11102779^^^CR^MR||SMITH^BB SARAH^^^^^L""" result == msh3 } - def "getLiteralOrFieldValue returns null when there's an exception getting the value"() { + def "getLiteralOrFieldValue throws exception when using invalid operand"() { given: def operand = "invalidField" def inputMessage = Mock(HL7Message) when: - def result = evaluator.getLiteralOrFieldValue(hl7Message, inputMessage, operand) + evaluator.getLiteralOrFieldValue(hl7Message, inputMessage, operand) then: - result == null + thrown(HL7ParserException) } def "getFieldValue returns specified field value"() { @@ -278,16 +278,16 @@ PID|1||11102779^^^CR^MR||SMITH^BB SARAH^^^^^L""" result == msh3 } - def "getFieldValue returns null for non numeric field index"() { + def "getFieldValue throws exception for non numeric field index"() { given: def fieldName = "MSH-three" def inputMessage = Mock(HL7Message) when: - def result = evaluator.getFieldValue(hl7Message, inputMessage, fieldName) + evaluator.getFieldValue(hl7Message, inputMessage, fieldName) then: - result == null + thrown(HL7ParserException) } def "getFieldValue throws exception for empty field name"() { @@ -299,8 +299,7 @@ PID|1||11102779^^^CR^MR||SMITH^BB SARAH^^^^^L""" evaluator.getFieldValue(hl7Message, inputMessage, fieldName) then: - def e = thrown(HL7ParserException) - e.getMessage().contains("Invalid field name format") + thrown(HL7ParserException) } def "getMessageBySource should return input message when source is input"() { diff --git a/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7MessageTest.groovy b/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7MessageTest.groovy index 2c64fd958..d939316c1 100644 --- a/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7MessageTest.groovy +++ b/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7MessageTest.groovy @@ -78,10 +78,6 @@ OBX|2|NM|||3122||||||F|||202402221854-0500||""" def "getValue returns the expected value given the segment name and indices"() { expect: - message.getValue("PID", 3) == message.getValue("PID-3") - message.getValue("PID", 3, 0) == message.getValue("PID-3.0") - message.getValue("PID", 40, 4, 1) == message.getValue("PID-40.4.1") - message.getValue("NK1", 33, 4, 1, 1) == message.getValue("NK1-33.4.1.1") message.getValue("PID-3") == "1300974^^^Baptist East^MR" message.getValue("PID-3.0") == "" message.getValue("PID-3.1") == "1300974" @@ -105,14 +101,15 @@ OBX|2|NM|||3122||||||F|||202402221854-0500||""" def "getValue should throws an exception when the segment is not found"() { when: - message.getValue("ZZZ", 1) + message.getValue("ZZZ-1") then: thrown(HL7MessageException) } + // TODO: move to HL7EncodingTest def "getEscapeCharacter should return the escape character"() { expect: - message.getEscapeCharacter() == '\\' as char + message.getEncoding().getEscapeCharacter() == '\\' as char } } diff --git a/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7ParserTest.groovy b/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7ParserTest.groovy index e3aea308b..7556a47da 100644 --- a/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7ParserTest.groovy +++ b/rs-e2e/src/test/groovy/gov/hhs/cdc/trustedintermediary/rse2e/hl7/HL7ParserTest.groovy @@ -5,7 +5,7 @@ import spock.lang.Specification class HL7ParserTest extends Specification { - def "parse should handle basic HL7 message"() { + def "parseMessage should handle basic HL7 message"() { given: def content = """MSH|^~\\&|sending_app|sending_facility PID|||12345||Doe^John||19800101|M""" @@ -26,7 +26,7 @@ PID|||12345||Doe^John||19800101|M""" segments[1].fields().get(4) == "Doe^John" } - def "parse should handle empty lines in message"() { + def "parseMessage should handle empty lines in message"() { given: def content = """MSH|^~\\&|sending_app @@ -39,7 +39,7 @@ PID|||12345""" result.getSegments().size() == 2 } - def "parse should preserve empty fields"() { + def "parseMessage should preserve empty fields"() { given: def content = "MSH|^~\\&|sending_app||sending_facility" @@ -50,8 +50,11 @@ PID|||12345""" result.getSegments().get(0).fields().get(3) == "" } - def "parseFieldValue should handle different field levels"() { + def "parseMessageFieldValue should handle different field levels"() { given: + def message = HL7Parser.parseMessage("""delimiters +""" + ) def fields = [ "value1", "component1^component2", @@ -61,13 +64,14 @@ PID|||12345""" def delimiters = ['|', '^', '~', '&'] as char[] when: - def result = HL7Parser.parseFieldValue(fields, delimiters, indices as int[]) + def hl7Path = HL7Parser.parsePath(path) + def result = HL7Parser.parseMessageFieldValue(hl7Path, fields, delimiters) then: result == expectedValue where: - scenario | indices | expectedValue + scenario | path | expectedValue "simple field" | [1] | "value1" "component" | [2, 2] | "component2" "repetition" | [3, 1, 2] | "rep2" @@ -75,124 +79,46 @@ PID|||12345""" "invalid index" | [5] | "" } - def "parseFieldValue returns an empty string when inputs are null"() { + def "parseMessageFieldValue returns an empty string when inputs are null"() { when: - def result = HL7Parser.parseFieldValue(null, [] as char[], 1) + def result = HL7Parser.parseMessageFieldValue(null, null) then: result == "" } - def "getEncodingCharacterMap should use defaults when no encoding characters provided"() { - when: - def encodingChars = HL7Parser.getEncodingCharacterMap(null) - - then: - encodingChars["field"] == '|' as char - encodingChars["component"] == '^' as char - encodingChars["repetition"] == '~' as char - encodingChars["escape"] == '\\' as char - encodingChars["subcomponent"] == '&' as char - } - - def "getEncodingCharacterMap should use default character when one is missing"() { - given: - def customEncodingChars = "_" - - when: - def encodingChars = HL7Parser.getEncodingCharacterMap(customEncodingChars) - - then: - encodingChars["field"] == '|' as char - encodingChars["component"] == '_' as char - encodingChars["repetition"] == '~' as char - encodingChars["escape"] == '\\' as char - encodingChars["subcomponent"] == '&' as char - } - - def "getEncodingCharacterMap should use custom encoding characters when provided"() { - given: - def customEncodingChars = "@#+_" - - when: - def result = HL7Parser.getEncodingCharacterMap(customEncodingChars) - - then: - result["field"] == '|' as char - result["component"] == '@' as char - result["repetition"] == '#' as char - result["escape"] == '+' as char - result["subcomponent"] == '_' as char - } - - def "parseFieldValue returns an empty string if a null list of fields is given"() { + def "parseMessageFieldValue returns an empty string if an empty message is given"() { given: - def nullList = null - def delimiters = ['|'] + def hl7Path = HL7Parser.parsePath("MSH-3") + def message = HL7Parser.parseMessage("") when: - def out = HL7Parser.parseFieldValue(nullList, delimiters as char[]) + def out = HL7Parser.parseMessageFieldValue(hl7Path, message) then: out == "" } - def "parseFieldValue returns an empty string if an empty list of fields is given"() { + def "parseMessageFieldValue returns an empty string if an empty hl7 path is given"() { given: - def emptyList = [] - def delimiters = ['|'] + def hl7Path = HL7Parser.parsePath("") when: - def out = HL7Parser.parseFieldValue(emptyList, delimiters as char[]) + def out = HL7Parser.parseMessageFieldValue(hl7Path, _ as HL7Message) then: out == "" } - def "parseFieldValue returns an empty string if the indices are pointing outside the expected range"() { + def "parseMessageFieldValue returns an empty string if the indices in hl7 path are pointing outside the expected range"() { given: - def emptyList = [ - "MSH|fakeValues", - "OBR|fakeValues" - ] - def delimiters = ['|'] + def message = HL7Parser.parseMessage("MSH|fakeValues\nOBR|fakeValues") + def hl7Path = HL7Parser.parsePath("MSH-3") when: - def out = HL7Parser.parseFieldValue(emptyList, delimiters as char[], 10, 20) + def out = HL7Parser.parseMessageFieldValue(hl7Path, message) then: out == "" } - - def "getEncodingCharacterMap uses default definitions when encoding characters are not available"() { - when: - def out = HL7Parser.getEncodingCharacterMap("tes") - - then: - out.size() > 0 - } - - def "getEncodingCharacterMap uses default definitions if the encoding characters are blank"() { - when: - def out = HL7Parser.getEncodingCharacterMap(" ") - - then: - out.size() > 0 - } - - def "getEncodingCharacterMap uses default definitions if the encoding characters are whitespace"() { - when: - def out = HL7Parser.getEncodingCharacterMap("") - - then: - out.size() > 0 - } - - def "getEncodingCharacterMap uses default definitions if the encoding characters are null"() { - when: - def out = HL7Parser.getEncodingCharacterMap(null) - - then: - out.size() > 0 - } }