#![expect(
clippy::assertions_on_result_states,
clippy::expect_used,
clippy::indexing_slicing,
clippy::uninlined_format_args,
clippy::unwrap_used,
reason = "legacy CLI integration tests use static fixtures; cleanup is tracked in policy/clippy-debt.toml"
)]
mod common;
use predicates::prelude::*;
use common::{
cli_command, create_temp_dir, create_temp_file, create_temp_hl7_file,
create_temp_hl7_with_content, create_temp_mllp_file, create_temp_profile, invalid_hl7_message,
is_valid_json, minimal_profile, read_file, simple_template, strict_profile,
truncated_hl7_message,
};
mod help_and_version {
use super::*;
#[test]
fn test_help_flag() {
let mut cmd = cli_command();
cmd.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("HL7 v2 parser"));
}
#[test]
fn test_parse_help() {
let mut cmd = cli_command();
cmd.args(["parse", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("Parse HL7 v2 message"));
}
#[test]
fn test_norm_help() {
let mut cmd = cli_command();
cmd.args(["norm", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("Normalize"));
}
#[test]
fn test_val_help() {
let mut cmd = cli_command();
cmd.args(["val", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("Validate"));
}
#[test]
fn test_ack_help() {
let mut cmd = cli_command();
cmd.args(["ack", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("Generate ACK"));
}
#[test]
fn test_gen_help() {
let mut cmd = cli_command();
cmd.args(["gen", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("Generate synthetic"));
}
}
mod parse_command {
use super::*;
#[test]
fn test_parse_valid_file() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
cmd.args(["parse", hl7_file.to_str().unwrap()])
.assert()
.success()
.stdout(predicate::str::contains("MSH"));
}
#[test]
fn test_parse_output_is_json() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
let output = cmd
.args(["parse", hl7_file.to_str().unwrap()])
.output()
.expect("Failed to execute command");
assert!(is_valid_json(&output.stdout));
}
#[test]
fn test_parse_with_json_flag() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
cmd.args(["parse", hl7_file.to_str().unwrap(), "--json"])
.assert()
.success()
.stdout(predicate::str::contains("MSH"));
}
#[test]
fn test_parse_with_summary_flag() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
cmd.args(["parse", hl7_file.to_str().unwrap(), "--summary"])
.assert()
.success()
.stdout(predicate::str::contains("Parse Summary"));
}
#[test]
fn test_parse_mllp_framed_file() {
let dir = create_temp_dir();
let mllp_file = create_temp_mllp_file(&dir, "test_mllp.hl7");
let mut cmd = cli_command();
cmd.args(["parse", mllp_file.to_str().unwrap(), "--mllp"])
.assert()
.success()
.stdout(predicate::str::contains("MSH"));
}
#[test]
fn test_parse_missing_file() {
let mut cmd = cli_command();
cmd.args(["parse", "/nonexistent/file.hl7"])
.assert()
.failure()
.stderr(predicate::str::contains("Error"));
}
#[test]
fn test_parse_invalid_file() {
let dir = create_temp_dir();
let invalid_file = create_temp_file(&dir, "invalid.hl7", invalid_hl7_message().as_bytes());
let mut cmd = cli_command();
cmd.args(["parse", invalid_file.to_str().unwrap()])
.assert()
.failure();
}
#[test]
fn test_parse_truncated_file() {
let dir = create_temp_dir();
let truncated_file =
create_temp_file(&dir, "truncated.hl7", truncated_hl7_message().as_bytes());
let mut cmd = cli_command();
let result = cmd
.args(["parse", truncated_file.to_str().unwrap()])
.output();
assert!(result.is_ok());
}
#[test]
fn test_parse_shows_segment_count_in_summary() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
cmd.args(["parse", hl7_file.to_str().unwrap(), "--summary"])
.assert()
.success()
.stdout(predicate::str::contains("Segments:"));
}
#[test]
fn test_parse_shows_file_size_in_summary() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
cmd.args(["parse", hl7_file.to_str().unwrap(), "--summary"])
.assert()
.success()
.stdout(predicate::str::contains("File size:"));
}
}
mod norm_command {
use super::*;
#[test]
fn test_norm_valid_file() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let output_file = dir.path().join("output.hl7");
let mut cmd = cli_command();
cmd.args([
"norm",
hl7_file.to_str().unwrap(),
"-o",
output_file.to_str().unwrap(),
])
.assert()
.success();
assert!(output_file.exists());
}
#[test]
fn test_norm_output_to_stdout() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
cmd.args(["norm", hl7_file.to_str().unwrap()])
.assert()
.success()
.stdout(predicate::str::contains("MSH"));
}
#[test]
fn test_norm_with_summary() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let output_file = dir.path().join("output.hl7");
let mut cmd = cli_command();
cmd.args([
"norm",
hl7_file.to_str().unwrap(),
"-o",
output_file.to_str().unwrap(),
"--summary",
])
.assert()
.success()
.stdout(predicate::str::contains("Normalize Summary"));
}
#[test]
fn test_norm_with_mllp_output() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
let output = cmd
.args(["norm", hl7_file.to_str().unwrap(), "--mllp-out"])
.output()
.expect("Failed to execute command");
assert_eq!(output.stdout[0], 0x0B); assert!(output.stdout.len() > 2);
}
#[test]
fn test_norm_missing_file() {
let mut cmd = cli_command();
cmd.args(["norm", "/nonexistent/file.hl7"])
.assert()
.failure();
}
#[test]
fn test_norm_roundtrip_preserves_content() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let output_file = dir.path().join("output.hl7");
let mut cmd = cli_command();
cmd.args([
"norm",
hl7_file.to_str().unwrap(),
"-o",
output_file.to_str().unwrap(),
])
.assert()
.success();
let original_content = read_file(&hl7_file);
let normalized_content = read_file(&output_file);
let original_msg = hl7v2::parse(&original_content).expect("Original should parse");
let normalized_msg = hl7v2::parse(&normalized_content).expect("Normalized should parse");
assert_eq!(original_msg.segments.len(), normalized_msg.segments.len());
}
}
mod validate_command {
use super::*;
#[test]
fn test_validate_with_valid_profile() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let profile_file = create_temp_profile(&dir, "profile.yaml", minimal_profile());
let mut cmd = cli_command();
let result = cmd
.args([
"val",
hl7_file.to_str().unwrap(),
"--profile",
profile_file.to_str().unwrap(),
])
.output();
assert!(result.is_ok());
}
#[test]
fn test_validate_with_strict_profile() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let profile_file = create_temp_profile(&dir, "strict.yaml", strict_profile());
let mut cmd = cli_command();
let result = cmd
.args([
"val",
hl7_file.to_str().unwrap(),
"--profile",
profile_file.to_str().unwrap(),
])
.output();
assert!(result.is_ok());
}
#[test]
fn test_validate_with_detailed_output() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let profile_file = create_temp_profile(&dir, "profile.yaml", minimal_profile());
let mut cmd = cli_command();
let result = cmd
.args([
"val",
hl7_file.to_str().unwrap(),
"--profile",
profile_file.to_str().unwrap(),
"--detailed",
])
.output();
assert!(result.is_ok());
}
#[test]
fn test_validate_with_summary() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let profile_file = create_temp_profile(&dir, "profile.yaml", minimal_profile());
let mut cmd = cli_command();
let result = cmd
.args([
"val",
hl7_file.to_str().unwrap(),
"--profile",
profile_file.to_str().unwrap(),
"--summary",
])
.output();
assert!(result.is_ok());
}
#[test]
fn test_validate_missing_hl7_file() {
let dir = create_temp_dir();
let profile_file = create_temp_profile(&dir, "profile.yaml", minimal_profile());
let mut cmd = cli_command();
cmd.args([
"val",
"/nonexistent/file.hl7",
"--profile",
profile_file.to_str().unwrap(),
])
.assert()
.failure();
}
#[test]
fn test_validate_missing_profile_file() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
cmd.args([
"val",
hl7_file.to_str().unwrap(),
"--profile",
"/nonexistent/profile.yaml",
])
.assert()
.failure();
}
#[test]
fn test_validate_mllp_input() {
let dir = create_temp_dir();
let mllp_file = create_temp_mllp_file(&dir, "test_mllp.hl7");
let profile_file = create_temp_profile(&dir, "profile.yaml", minimal_profile());
let mut cmd = cli_command();
let result = cmd
.args([
"val",
mllp_file.to_str().unwrap(),
"--profile",
profile_file.to_str().unwrap(),
"--mllp",
])
.output();
assert!(result.is_ok());
}
}
mod ack_command {
use super::*;
#[test]
fn test_generate_ack_aa() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
let result = cmd
.args(["ack", hl7_file.to_str().unwrap(), "--code", "AA"])
.output();
assert!(result.is_ok());
}
#[test]
fn test_generate_ack_ae() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
let result = cmd
.args(["ack", hl7_file.to_str().unwrap(), "--code", "AE"])
.output();
assert!(result.is_ok());
}
#[test]
fn test_generate_ack_ar() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
let result = cmd
.args(["ack", hl7_file.to_str().unwrap(), "--code", "AR"])
.output();
assert!(result.is_ok());
}
#[test]
fn test_generate_ack_with_mllp_output() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
let result = cmd
.args([
"ack",
hl7_file.to_str().unwrap(),
"--code",
"AA",
"--mllp-out",
])
.output();
assert!(result.is_ok());
}
#[test]
fn test_generate_ack_with_summary() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
let result = cmd
.args([
"ack",
hl7_file.to_str().unwrap(),
"--code",
"AA",
"--summary",
])
.output();
assert!(result.is_ok());
}
#[test]
fn test_generate_ack_original_mode() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
let result = cmd
.args([
"ack",
hl7_file.to_str().unwrap(),
"--mode",
"original",
"--code",
"AA",
])
.output();
assert!(result.is_ok());
}
#[test]
fn test_generate_ack_enhanced_mode() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
let result = cmd
.args([
"ack",
hl7_file.to_str().unwrap(),
"--mode",
"enhanced",
"--code",
"CA",
])
.output();
assert!(result.is_ok());
}
#[test]
fn test_generate_ack_missing_file() {
let mut cmd = cli_command();
cmd.args(["ack", "/nonexistent/file.hl7", "--code", "AA"])
.assert()
.failure();
}
}
mod gen_command {
use super::*;
#[test]
fn test_gen_with_template() {
let dir = create_temp_dir();
let template_file = create_temp_profile(&dir, "template.yaml", simple_template());
let output_dir = dir.path().join("output");
let mut cmd = cli_command();
let result = cmd
.args([
"gen",
"--profile",
template_file.to_str().unwrap(),
"--seed",
"42",
"--count",
"1",
"--out",
output_dir.to_str().unwrap(),
])
.output();
assert!(result.is_ok());
}
#[test]
fn test_gen_with_stats() {
let dir = create_temp_dir();
let template_file = create_temp_profile(&dir, "template.yaml", simple_template());
let output_dir = dir.path().join("output");
let mut cmd = cli_command();
let result = cmd
.args([
"gen",
"--profile",
template_file.to_str().unwrap(),
"--seed",
"42",
"--count",
"1",
"--out",
output_dir.to_str().unwrap(),
"--stats",
])
.output();
assert!(result.is_ok());
}
#[test]
fn test_gen_multiple_messages() {
let dir = create_temp_dir();
let template_file = create_temp_profile(&dir, "template.yaml", simple_template());
let output_dir = dir.path().join("output");
let mut cmd = cli_command();
let result = cmd
.args([
"gen",
"--profile",
template_file.to_str().unwrap(),
"--seed",
"42",
"--count",
"3",
"--out",
output_dir.to_str().unwrap(),
])
.output();
assert!(result.is_ok());
}
#[test]
fn test_gen_missing_template() {
let dir = create_temp_dir();
let output_dir = dir.path().join("output");
let mut cmd = cli_command();
cmd.args([
"gen",
"--profile",
"/nonexistent/template.yaml",
"--seed",
"42",
"--count",
"1",
"--out",
output_dir.to_str().unwrap(),
])
.assert()
.failure();
}
}
mod error_handling {
use super::*;
#[test]
fn test_invalid_command() {
let mut cmd = cli_command();
cmd.args(["invalid-command"]).assert().failure();
}
#[test]
fn test_parse_no_args() {
let mut cmd = cli_command();
cmd.args(["parse"]).assert().failure();
}
#[test]
fn test_val_no_args() {
let mut cmd = cli_command();
cmd.args(["val"]).assert().failure();
}
#[test]
fn test_ack_no_args() {
let mut cmd = cli_command();
cmd.args(["ack"]).assert().failure();
}
#[test]
fn test_gen_no_args() {
let mut cmd = cli_command();
cmd.args(["gen"]).assert().failure();
}
#[test]
fn test_norm_no_args() {
let mut cmd = cli_command();
cmd.args(["norm"]).assert().failure();
}
#[test]
fn test_invalid_ack_code() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
cmd.args(["ack", hl7_file.to_str().unwrap(), "--code", "INVALID"])
.assert()
.failure();
}
#[test]
fn test_invalid_ack_mode() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
cmd.args([
"ack",
hl7_file.to_str().unwrap(),
"--mode",
"INVALID",
"--code",
"AA",
])
.assert()
.failure();
}
}
mod exit_codes {
use super::*;
#[test]
fn test_success_returns_zero() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let mut cmd = cli_command();
cmd.args(["parse", hl7_file.to_str().unwrap()])
.assert()
.code(0);
}
#[test]
fn test_parse_error_returns_nonzero() {
let dir = create_temp_dir();
let invalid_file = create_temp_file(&dir, "invalid.hl7", invalid_hl7_message().as_bytes());
let mut cmd = cli_command();
cmd.args(["parse", invalid_file.to_str().unwrap()])
.assert()
.code(predicate::ne(0));
}
#[test]
fn test_missing_file_returns_nonzero() {
let mut cmd = cli_command();
cmd.args(["parse", "/nonexistent/file.hl7"])
.assert()
.code(predicate::ne(0));
}
}
mod file_io {
use super::*;
#[test]
fn test_read_binary_file() {
let dir = create_temp_dir();
let mut binary_content = vec![0x0B];
binary_content.extend_from_slice(b"MSH|^~\\&|Test\r");
binary_content.push(0x1C);
binary_content.push(0x0D);
let binary_file = create_temp_file(&dir, "binary.hl7", &binary_content);
let content = read_file(&binary_file);
assert_eq!(content, binary_content);
}
#[test]
fn test_write_to_output_file() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let output_file = dir.path().join("output.hl7");
let mut cmd = cli_command();
cmd.args([
"norm",
hl7_file.to_str().unwrap(),
"-o",
output_file.to_str().unwrap(),
])
.assert()
.success();
assert!(output_file.exists());
let content = read_file(&output_file);
assert!(!content.is_empty());
}
#[test]
fn test_output_file_is_valid_hl7() {
let dir = create_temp_dir();
let hl7_file = create_temp_hl7_file(&dir, "test.hl7");
let output_file = dir.path().join("output.hl7");
let mut cmd = cli_command();
cmd.args([
"norm",
hl7_file.to_str().unwrap(),
"-o",
output_file.to_str().unwrap(),
])
.assert()
.success();
let content = read_file(&output_file);
let parse_result = hl7v2::parse(&content);
assert!(parse_result.is_ok());
}
}
mod edge_cases {
use super::*;
#[test]
fn test_empty_file() {
let dir = create_temp_dir();
let empty_file = create_temp_file(&dir, "empty.hl7", b"");
let mut cmd = cli_command();
cmd.args(["parse", empty_file.to_str().unwrap()])
.assert()
.failure();
}
#[test]
fn test_whitespace_only_file() {
let dir = create_temp_dir();
let whitespace_file = create_temp_file(&dir, "whitespace.hl7", b" \n\t ");
let mut cmd = cli_command();
cmd.args(["parse", whitespace_file.to_str().unwrap()])
.assert()
.failure();
}
#[test]
fn test_utf8_file() {
let dir = create_temp_dir();
let utf8_content = "MSH|^~\\&|Тест|Фасил|Recv|Fac|20250101000000||ADT^A01|MSG001|P|2.5.1\rPID|1||123456^^^HOSP^MR||Доу^Джон||19800101|M\r";
let utf8_file = create_temp_hl7_with_content(&dir, "utf8.hl7", utf8_content);
let mut cmd = cli_command();
cmd.args(["parse", utf8_file.to_str().unwrap()])
.assert()
.success();
}
#[test]
fn test_large_message() {
let dir = create_temp_dir();
let mut large_content =
String::from("MSH|^~\\&|App|Fac|Recv|Fac|20250101000000||ADT^A01|MSG001|P|2.5.1\r");
for i in 1..=100 {
large_content.push_str(&format!("NTE|{}|Note segment number {}\r", i, i));
}
let large_file = create_temp_hl7_with_content(&dir, "large.hl7", &large_content);
let mut cmd = cli_command();
cmd.args(["parse", large_file.to_str().unwrap()])
.assert()
.success();
}
#[test]
fn test_message_with_special_characters() {
let dir = create_temp_dir();
let content = "MSH|^~\\&|App|Fac|Recv|Fac|20250101000000||ADT^A01|MSG001|P|2.5.1\rPID|1||123456^^^HOSP^MR||Doe^John^\"Johnny\"||19800101|M\r";
let special_file = create_temp_hl7_with_content(&dir, "special.hl7", content);
let mut cmd = cli_command();
cmd.args(["parse", special_file.to_str().unwrap()])
.assert()
.success();
}
}
mod serve_command {
use super::*;
#[test]
fn test_serve_help() {
let mut cmd = cli_command();
cmd.args(["serve", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("HTTP/gRPC server"))
.stdout(predicate::str::contains("--port"))
.stdout(predicate::str::contains("--host"))
.stdout(predicate::str::contains("--mode"));
}
#[test]
fn test_serve_default_options() {
let mut cmd = cli_command();
cmd.args(["serve", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("default: http"))
.stdout(predicate::str::contains("default: 8080"))
.stdout(predicate::str::contains("default: 0.0.0.0"));
}
#[test]
fn test_serve_mode_options() {
let mut cmd = cli_command();
cmd.args(["serve", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("http"))
.stdout(predicate::str::contains("grpc"));
}
#[test]
fn test_serve_grpc_not_implemented() {
let mut cmd = cli_command();
cmd.args(["serve", "--mode", "grpc", "--port", "50051"])
.assert()
.failure()
.stderr(predicate::str::contains("not yet implemented"));
}
}