#![expect(
clippy::assertions_on_result_states,
clippy::indexing_slicing,
clippy::panic,
clippy::uninlined_format_args,
clippy::unwrap_used,
reason = "pre-existing parser unit test debt moved into hl7v2; cleanup is split from topology collapse"
)]
use crate::model::*;
use crate::{get, get_presence, parse, parse_batch, parse_file_batch, parse_mllp};
#[test]
fn test_parse_simple_adt_a01() {
let hl7 = b"MSH|^~\\&|SendingApp|SendingFac|ReceivingApp|ReceivingFac|20250128152312||ADT^A01|ABC123|P|2.5.1\rPID|1||123456^^^HOSP^MR||Doe^John\r";
let message = parse(hl7).unwrap();
assert_eq!(message.delims.field, '|');
assert_eq!(message.delims.comp, '^');
assert_eq!(message.delims.rep, '~');
assert_eq!(message.delims.esc, '\\');
assert_eq!(message.delims.sub, '&');
assert_eq!(message.segments.len(), 2);
assert_eq!(&message.segments[0].id, b"MSH");
assert_eq!(&message.segments[1].id, b"PID");
}
#[test]
fn test_parse_oru_r01() {
let hl7 = b"MSH|^~\\&|LabSys|Lab|LIS|Hospital|20250128150000||ORU^R01|MSG003|P|2.5\rPID|1||MRN789^^^Lab^MR||Patient^Test||19850610|M\rOBR|1|ORD123|FIL456|CBC^Complete Blood Count|||20250128120000\rOBX|1|NM|WBC^White Blood Count||7.5|10^9/L|4.0-11.0|N|||F\r";
let message = parse(hl7).unwrap();
assert_eq!(message.segments.len(), 4);
assert_eq!(&message.segments[0].id, b"MSH");
assert_eq!(&message.segments[1].id, b"PID");
assert_eq!(&message.segments[2].id, b"OBR");
assert_eq!(&message.segments[3].id, b"OBX");
}
#[test]
fn test_parse_adt_a04() {
let hl7 = b"MSH|^~\\&|RegSys|Hospital|ADT|Hospital|20250128140000||ADT^A04|MSG002|P|2.5\rPID|1||MRN456^^^Hospital^MR||Smith^Jane^M||19900215|F\r";
let message = parse(hl7).unwrap();
assert_eq!(message.segments.len(), 2);
assert_eq!(get(&message, "MSH.9.1"), Some("ADT"));
assert_eq!(get(&message, "MSH.9.2"), Some("A04"));
assert_eq!(get(&message, "PID.5.1"), Some("Smith"));
assert_eq!(get(&message, "PID.5.2"), Some("Jane"));
}
#[test]
fn test_parse_custom_delimiters() {
let hl7 =
b"MSH#$*@!App#Fac#Rec#RecFac#20250128120000##ADT$A01#1#P#2.5\rPID#1##123##Name$First\r";
let message = parse(hl7).unwrap();
assert_eq!(message.delims.field, '#');
assert_eq!(message.delims.comp, '$');
assert_eq!(message.delims.rep, '*');
assert_eq!(message.delims.esc, '@');
assert_eq!(message.delims.sub, '!');
assert_eq!(message.segments.len(), 2);
}
#[test]
fn test_default_delimiters() {
let delims = Delims::default();
assert_eq!(delims.field, '|');
assert_eq!(delims.comp, '^');
assert_eq!(delims.rep, '~');
assert_eq!(delims.esc, '\\');
assert_eq!(delims.sub, '&');
}
#[test]
fn test_get_simple_field() {
let hl7 = b"MSH|^~\\&|SendingApp|SendingFac|ReceivingApp|ReceivingFac|20250128152312||ADT^A01|ABC123|P|2.5.1\rPID|1||123456^^^HOSP^MR||Doe^John\r";
let message = parse(hl7).unwrap();
assert_eq!(get(&message, "PID.5.1"), Some("Doe"));
assert_eq!(get(&message, "PID.5.2"), Some("John"));
assert_eq!(get(&message, "PID.3.1"), Some("123456"));
}
#[test]
fn test_get_msh_fields() {
let hl7 = b"MSH|^~\\&|SendingApp|SendingFac|ReceivingApp|ReceivingFac|20250128152312||ADT^A01|ABC123|P|2.5.1\r";
let message = parse(hl7).unwrap();
assert_eq!(get(&message, "MSH.3"), Some("SendingApp"));
assert_eq!(get(&message, "MSH.4"), Some("SendingFac"));
assert_eq!(get(&message, "MSH.5"), Some("ReceivingApp"));
assert_eq!(get(&message, "MSH.6"), Some("ReceivingFac"));
assert_eq!(get(&message, "MSH.9.1"), Some("ADT"));
assert_eq!(get(&message, "MSH.9.2"), Some("A01"));
assert_eq!(get(&message, "MSH.10"), Some("ABC123"));
assert_eq!(get(&message, "MSH.11"), Some("P"));
assert_eq!(get(&message, "MSH.12"), Some("2.5.1"));
}
#[test]
fn test_get_with_repetitions() {
let hl7 = b"MSH|^~\\&|SendingApp|SendingFac\rPID|1||123456^^^HOSP^MR||Doe^John~Smith^Jane~Brown^Bob\r";
let message = parse(hl7).unwrap();
assert_eq!(get(&message, "PID.5.1"), Some("Doe"));
assert_eq!(get(&message, "PID.5.2"), Some("John"));
assert_eq!(get(&message, "PID.5[2].1"), Some("Smith"));
assert_eq!(get(&message, "PID.5[2].2"), Some("Jane"));
assert_eq!(get(&message, "PID.5[3].1"), Some("Brown"));
assert_eq!(get(&message, "PID.5[3].2"), Some("Bob"));
}
#[test]
fn test_get_missing_field() {
let hl7 = b"MSH|^~\\&|App|Fac\rPID|1||123||Doe^John\r";
let message = parse(hl7).unwrap();
assert_eq!(get(&message, "PID.100"), None);
assert_eq!(get(&message, "ZZZ.1"), None);
}
#[test]
fn test_presence_semantics() {
let hl7 = b"MSH|^~\\&|App|Fac\rPID|1||123456^^^HOSP^MR||Doe^John|||\r";
let message = parse(hl7).unwrap();
match get_presence(&message, "PID.5.1") {
Presence::Value(val) => assert_eq!(val, "Doe"),
_ => panic!("Expected Value"),
}
match get_presence(&message, "PID.8.1") {
Presence::Empty => {}
_ => panic!("Expected Empty"),
}
match get_presence(&message, "PID.50.1") {
Presence::Missing => {}
_ => panic!("Expected Missing"),
}
}
#[test]
fn test_escape_sequences() {
let hl7 = b"MSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\rPID|1||123||Test\\F\\Field\r";
let message = parse(hl7).unwrap();
assert_eq!(get(&message, "PID.5.1"), Some("Test|Field"));
}
#[test]
fn test_escape_special_chars() {
let hl7 = b"MSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\rPID|1||123||Test\\F\\Field\r";
let message = parse(hl7).unwrap();
let value = get(&message, "PID.5.1").unwrap();
assert!(
value.contains('|'),
"Should contain unescaped field separator"
);
}
#[test]
fn test_parse_mllp() {
let hl7 = b"MSH|^~\\&|SendingApp|SendingFac|ReceivingApp|ReceivingFac|20250128152312||ADT^A01|ABC123|P|2.5.1\r";
let framed = crate::transport::mllp::wrap_mllp(hl7);
let message = parse_mllp(&framed).unwrap();
assert_eq!(message.segments.len(), 1);
assert_eq!(&message.segments[0].id, b"MSH");
}
#[test]
fn test_parse_single_message_as_batch() {
let hl7 = b"MSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\rPID|1||123||Test\r";
let batch = parse_batch(hl7).unwrap();
assert!(batch.header.is_none());
assert!(batch.trailer.is_none());
assert_eq!(batch.messages.len(), 1);
}
#[test]
fn test_parse_batch_with_header() {
let hl7 = b"BHS|^~\\&|App|Fac|||20250128120000\rMSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\rPID|1||123||Test\rBTS|1\r";
let batch = parse_batch(hl7).unwrap();
assert!(batch.header.is_some());
assert!(batch.trailer.is_some());
assert_eq!(batch.messages.len(), 1);
}
#[test]
fn test_parse_file_batch() {
let hl7 = b"FHS|^~\\&|App|Fac|||20250128120000\rMSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\rPID|1||123||Test\rFTS|1\r";
let file_batch = parse_file_batch(hl7).unwrap();
assert!(file_batch.header.is_some());
assert!(file_batch.trailer.is_some());
assert_eq!(file_batch.batches.len(), 1);
}
#[test]
fn test_error_empty_message() {
let result = parse(b"");
assert!(result.is_err());
}
#[test]
fn test_error_no_msh_segment() {
let result = parse(b"PID|1||123||Doe^John\r");
assert!(result.is_err());
}
#[test]
fn test_error_invalid_segment_id() {
let result = parse(b"MSH|^~\\&|App|Fac\rabc|invalid\r");
assert!(result.is_err());
}
#[test]
fn test_error_invalid_charset() {
let result = parse(&[0xFF, 0xFE, 0xFD]);
assert!(result.is_err());
}
#[test]
fn test_error_truncated_message() {
let result = parse(b"MSH");
assert!(result.is_err());
}
#[test]
fn test_error_duplicate_delimiters() {
let result = parse(b"MSH|||||App|Fac\r");
assert!(result.is_err());
}
#[test]
fn test_empty_fields() {
let hl7 = b"MSH|^~\\&|SendingApp|SendingFac|||||ADT^A01|1|P|2.5\rPID|1|||||||||||\rPV1|1||||||||||||||\r";
let message = parse(hl7).unwrap();
assert_eq!(message.segments.len(), 3);
match get_presence(&message, "PID.3.1") {
Presence::Empty => {}
_ => panic!("Expected Empty for PID.3.1"),
}
}
#[test]
fn test_very_long_field() {
let long_value: String = "A".repeat(1000);
let hl7 = format!(
"MSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\rPID|1||{}||Test\r",
long_value
);
let message = parse(hl7.as_bytes()).unwrap();
assert_eq!(get(&message, "PID.3.1"), Some(long_value.as_str()));
}
#[test]
fn test_multiple_segments_same_type() {
let hl7 = b"MSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\rOBX|1|ST|Test1||Value1\rOBX|2|ST|Test2||Value2\rOBX|3|ST|Test3||Value3\r";
let message = parse(hl7).unwrap();
assert_eq!(message.segments.len(), 4); }
#[test]
fn test_null_value() {
let hl7 = b"MSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\rPID|1||\"\"||Test\r";
let message = parse(hl7).unwrap();
match get_presence(&message, "PID.3.1") {
Presence::Null => {}
_ => panic!("Expected Null for PID.3.1"),
}
}
#[test]
fn test_subcomponent_parsing() {
let hl7 = b"MSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\rPID|1||123||Test&Sub1&Sub2\r";
let message = parse(hl7).unwrap();
assert_eq!(get(&message, "PID.5.1"), Some("Test"));
}
#[test]
fn test_component_parsing() {
let hl7 = b"MSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\rPID|1||123^^^HOSP^MR||Test\r";
let message = parse(hl7).unwrap();
assert_eq!(get(&message, "PID.3.1"), Some("123"));
assert_eq!(get(&message, "PID.3.4"), Some("HOSP"));
assert_eq!(get(&message, "PID.3.5"), Some("MR"));
}
#[test]
fn test_segment_with_trailing_delimiters() {
let hl7 = b"MSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\rPID|1||123||Test||||||\r";
let message = parse(hl7).unwrap();
assert_eq!(message.segments.len(), 2);
assert_eq!(get(&message, "PID.5.1"), Some("Test"));
}
#[test]
fn test_msh_encoding_characters_field() {
let hl7 = b"MSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\r";
let message = parse(hl7).unwrap();
assert_eq!(get(&message, "MSH.2"), Some("^~\\&"));
}
#[test]
fn test_charset_extraction() {
let hl7 = b"MSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5|||||||ASCII\r";
let message = parse(hl7).unwrap();
assert_eq!(message.charsets, vec!["ASCII"]);
}
#[test]
fn test_no_charset() {
let hl7 = b"MSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\r";
let message = parse(hl7).unwrap();
assert!(message.charsets.is_empty());
}
#[test]
fn test_carriage_return_line_ending() {
let hl7 = b"MSH|^~\\&|App|Fac\rPID|1||123||Test\r";
let message = parse(hl7).unwrap();
assert_eq!(message.segments.len(), 2);
}
#[test]
fn test_trailing_carriage_return() {
let hl7 = b"MSH|^~\\&|App|Fac\rPID|1||123||Test\r";
let message = parse(hl7).unwrap();
assert_eq!(message.segments.len(), 2);
}
#[test]
fn test_valid_segment_ids() {
let hl7 = b"MSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\rPID|1\rPV1|1\rEVN|A01\r";
let message = parse(hl7).unwrap();
assert_eq!(message.segments.len(), 4);
assert_eq!(&message.segments[0].id, b"MSH");
assert_eq!(&message.segments[1].id, b"PID");
assert_eq!(&message.segments[2].id, b"PV1");
assert_eq!(&message.segments[3].id, b"EVN");
}
#[test]
fn test_numeric_segment_id() {
let hl7 = b"MSH|^~\\&|App|Fac|||20250128120000||ADT^A01|1|P|2.5\rZ01|1\r";
let message = parse(hl7).unwrap();
assert_eq!(message.segments.len(), 2);
assert_eq!(&message.segments[1].id, b"Z01");
}