use std::fs;
use std::path::PathBuf;
use std::process::Command;
use tempfile::TempDir;
fn get_cli_binary_path() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_sof-cli"))
}
fn create_test_view_definition() -> String {
r#"{
"resourceType": "ViewDefinition",
"status": "active",
"name": "test_view",
"resource": "Patient",
"select": [
{
"column": [
{
"name": "id",
"path": "id"
},
{
"name": "family",
"path": "name.family"
}
]
}
]
}"#
.to_string()
}
fn create_test_bundle() -> String {
r#"{
"resourceType": "Bundle",
"type": "collection",
"entry": [
{
"resource": {
"resourceType": "Patient",
"id": "patient-1",
"name": [{
"family": "Smith",
"given": ["John"]
}]
}
},
{
"resource": {
"resourceType": "Patient",
"id": "patient-2",
"name": [{
"family": "Jones",
"given": ["Jane"]
}]
}
}
]
}"#
.to_string()
}
fn create_test_patient() -> String {
r#"{
"resourceType": "Patient",
"id": "single-patient",
"name": [{
"family": "Doe",
"given": ["John"]
}]
}"#
.to_string()
}
fn create_test_patient_array() -> String {
r#"[
{
"resourceType": "Patient",
"id": "array-patient-1",
"name": [{
"family": "Array1",
"given": ["Patient"]
}]
},
{
"resourceType": "Patient",
"id": "array-patient-2",
"name": [{
"family": "Array2",
"given": ["Patient"]
}]
}
]"#
.to_string()
}
#[test]
fn test_cli_with_file_source_bundle() {
let cli_path = get_cli_binary_path();
if !cli_path.exists() {
panic!(
"CLI binary not found at {:?}. Run 'cargo build' first.",
cli_path
);
}
let temp_dir = TempDir::new().unwrap();
let view_path = temp_dir.path().join("view.json");
let bundle_path = temp_dir.path().join("bundle.json");
fs::write(&view_path, create_test_view_definition()).unwrap();
fs::write(&bundle_path, create_test_bundle()).unwrap();
let bundle_url = format!("file://{}", bundle_path.to_string_lossy());
let output = Command::new(&cli_path)
.arg("-v")
.arg(&view_path)
.arg("-s")
.arg(&bundle_url)
.arg("-f")
.arg("csv")
.output()
.expect("Failed to execute CLI");
if !output.status.success() {
eprintln!("CLI output: {}", String::from_utf8_lossy(&output.stdout));
eprintln!("CLI error: {}", String::from_utf8_lossy(&output.stderr));
panic!("CLI command failed with status: {}", output.status);
}
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("id"),
"Output should contain 'id' column header"
);
assert!(
stdout.contains("family"),
"Output should contain 'family' column header"
);
assert!(
stdout.contains("patient-1") || stdout.contains("Smith"),
"Output should contain patient-1 data"
);
assert!(
stdout.contains("patient-2") || stdout.contains("Jones"),
"Output should contain patient-2 data"
);
}
#[test]
fn test_cli_with_file_source_single_resource() {
let cli_path = get_cli_binary_path();
if !cli_path.exists() {
panic!(
"CLI binary not found at {:?}. Run 'cargo build' first.",
cli_path
);
}
let temp_dir = TempDir::new().unwrap();
let view_path = temp_dir.path().join("view.json");
let patient_path = temp_dir.path().join("patient.json");
fs::write(&view_path, create_test_view_definition()).unwrap();
fs::write(&patient_path, create_test_patient()).unwrap();
let patient_url = format!("file://{}", patient_path.to_string_lossy());
let output = Command::new(&cli_path)
.arg("-v")
.arg(&view_path)
.arg("-s")
.arg(&patient_url)
.arg("-f")
.arg("csv")
.output()
.expect("Failed to execute CLI");
if !output.status.success() {
eprintln!("CLI error: {}", String::from_utf8_lossy(&output.stderr));
panic!("CLI command failed");
}
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("id"));
assert!(stdout.contains("single-patient") || stdout.contains("Doe"));
}
#[test]
fn test_cli_with_file_source_resource_array() {
let cli_path = get_cli_binary_path();
if !cli_path.exists() {
panic!(
"CLI binary not found at {:?}. Run 'cargo build' first.",
cli_path
);
}
let temp_dir = TempDir::new().unwrap();
let view_path = temp_dir.path().join("view.json");
let array_path = temp_dir.path().join("array.json");
fs::write(&view_path, create_test_view_definition()).unwrap();
fs::write(&array_path, create_test_patient_array()).unwrap();
let array_url = format!("file://{}", array_path.to_string_lossy());
let output = Command::new(&cli_path)
.arg("-v")
.arg(&view_path)
.arg("-s")
.arg(&array_url)
.arg("-f")
.arg("csv")
.output()
.expect("Failed to execute CLI");
if !output.status.success() {
eprintln!("CLI error: {}", String::from_utf8_lossy(&output.stderr));
panic!("CLI command failed");
}
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("array-patient-1") || stdout.contains("Array1"));
assert!(stdout.contains("array-patient-2") || stdout.contains("Array2"));
}
#[test]
fn test_cli_with_file_source_and_bundle() {
let cli_path = get_cli_binary_path();
if !cli_path.exists() {
panic!(
"CLI binary not found at {:?}. Run 'cargo build' first.",
cli_path
);
}
let temp_dir = TempDir::new().unwrap();
let view_path = temp_dir.path().join("view.json");
let source_path = temp_dir.path().join("source.json");
let bundle_path = temp_dir.path().join("bundle.json");
fs::write(&view_path, create_test_view_definition()).unwrap();
fs::write(&source_path, create_test_patient()).unwrap();
fs::write(&bundle_path, create_test_bundle()).unwrap();
let source_url = format!("file://{}", source_path.to_string_lossy());
let output = Command::new(&cli_path)
.arg("-v")
.arg(&view_path)
.arg("-s")
.arg(&source_url)
.arg("-b")
.arg(&bundle_path)
.arg("-f")
.arg("csv")
.output()
.expect("Failed to execute CLI");
if !output.status.success() {
eprintln!("CLI error: {}", String::from_utf8_lossy(&output.stderr));
panic!("CLI command failed");
}
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("single-patient") || stdout.contains("Doe"),
"Should contain patient from source file"
);
assert!(
stdout.contains("patient-1") || stdout.contains("Smith"),
"Should contain patient-1 from bundle"
);
assert!(
stdout.contains("patient-2") || stdout.contains("Jones"),
"Should contain patient-2 from bundle"
);
}
#[test]
fn test_cli_with_file_source_not_found() {
let cli_path = get_cli_binary_path();
if !cli_path.exists() {
panic!(
"CLI binary not found at {:?}. Run 'cargo build' first.",
cli_path
);
}
let temp_dir = TempDir::new().unwrap();
let view_path = temp_dir.path().join("view.json");
fs::write(&view_path, create_test_view_definition()).unwrap();
let nonexistent_path = if cfg!(windows) {
"file:///C:/nonexistent/path/to/file.json"
} else {
"file:///nonexistent/path/to/file.json"
};
let output = Command::new(&cli_path)
.arg("-v")
.arg(&view_path)
.arg("-s")
.arg(nonexistent_path)
.arg("-f")
.arg("csv")
.output()
.expect("Failed to execute CLI");
assert!(
!output.status.success(),
"CLI should fail with non-existent file"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("File not found")
|| stderr.contains("not found")
|| stderr.contains("Invalid"),
"Error message should indicate file not found or invalid path. Actual: {}",
stderr
);
}
#[test]
fn test_cli_with_file_source_invalid_json() {
let cli_path = get_cli_binary_path();
if !cli_path.exists() {
panic!(
"CLI binary not found at {:?}. Run 'cargo build' first.",
cli_path
);
}
let temp_dir = TempDir::new().unwrap();
let view_path = temp_dir.path().join("view.json");
let invalid_path = temp_dir.path().join("invalid.json");
fs::write(&view_path, create_test_view_definition()).unwrap();
fs::write(&invalid_path, "{ this is not valid json }").unwrap();
let invalid_url = format!("file://{}", invalid_path.to_string_lossy());
let output = Command::new(&cli_path)
.arg("-v")
.arg(&view_path)
.arg("-s")
.arg(&invalid_url)
.arg("-f")
.arg("csv")
.output()
.expect("Failed to execute CLI");
assert!(
!output.status.success(),
"CLI should fail with invalid JSON"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("parse") || stderr.contains("JSON") || stderr.contains("error"),
"Error message should indicate JSON parsing error"
);
}
#[test]
fn test_cli_with_file_source_json_output() {
let cli_path = get_cli_binary_path();
if !cli_path.exists() {
panic!(
"CLI binary not found at {:?}. Run 'cargo build' first.",
cli_path
);
}
let temp_dir = TempDir::new().unwrap();
let view_path = temp_dir.path().join("view.json");
let bundle_path = temp_dir.path().join("bundle.json");
fs::write(&view_path, create_test_view_definition()).unwrap();
fs::write(&bundle_path, create_test_bundle()).unwrap();
let bundle_url = format!("file://{}", bundle_path.to_string_lossy());
let output = Command::new(&cli_path)
.arg("-v")
.arg(&view_path)
.arg("-s")
.arg(&bundle_url)
.arg("-f")
.arg("json")
.output()
.expect("Failed to execute CLI");
if !output.status.success() {
eprintln!("CLI error: {}", String::from_utf8_lossy(&output.stderr));
panic!("CLI command failed");
}
let stdout = String::from_utf8_lossy(&output.stdout);
let parsed: Result<serde_json::Value, _> = serde_json::from_str(&stdout);
assert!(parsed.is_ok(), "Output should be valid JSON");
let json_value = parsed.unwrap();
assert!(json_value.is_array(), "JSON output should be an array");
}
#[test]
fn test_cli_with_file_source_output_to_file() {
let cli_path = get_cli_binary_path();
if !cli_path.exists() {
panic!(
"CLI binary not found at {:?}. Run 'cargo build' first.",
cli_path
);
}
let temp_dir = TempDir::new().unwrap();
let view_path = temp_dir.path().join("view.json");
let bundle_path = temp_dir.path().join("bundle.json");
let output_path = temp_dir.path().join("output.csv");
fs::write(&view_path, create_test_view_definition()).unwrap();
fs::write(&bundle_path, create_test_bundle()).unwrap();
let bundle_url = format!("file://{}", bundle_path.to_string_lossy());
let output = Command::new(&cli_path)
.arg("-v")
.arg(&view_path)
.arg("-s")
.arg(&bundle_url)
.arg("-f")
.arg("csv")
.arg("-o")
.arg(&output_path)
.output()
.expect("Failed to execute CLI");
if !output.status.success() {
eprintln!("CLI error: {}", String::from_utf8_lossy(&output.stderr));
panic!("CLI command failed");
}
assert!(output_path.exists(), "Output file should be created");
let content = fs::read_to_string(&output_path).unwrap();
assert!(content.contains("id"));
assert!(content.contains("patient-1") || content.contains("Smith"));
}
#[test]
fn test_cli_with_file_source_no_headers() {
let cli_path = get_cli_binary_path();
if !cli_path.exists() {
panic!(
"CLI binary not found at {:?}. Run 'cargo build' first.",
cli_path
);
}
let temp_dir = TempDir::new().unwrap();
let view_path = temp_dir.path().join("view.json");
let bundle_path = temp_dir.path().join("bundle.json");
fs::write(&view_path, create_test_view_definition()).unwrap();
fs::write(&bundle_path, create_test_bundle()).unwrap();
let bundle_url = format!("file://{}", bundle_path.to_string_lossy());
let output = Command::new(&cli_path)
.arg("-v")
.arg(&view_path)
.arg("-s")
.arg(&bundle_url)
.arg("-f")
.arg("csv")
.arg("--no-headers")
.output()
.expect("Failed to execute CLI");
if !output.status.success() {
eprintln!("CLI error: {}", String::from_utf8_lossy(&output.stderr));
panic!("CLI command failed");
}
let stdout = String::from_utf8_lossy(&output.stdout);
let first_line = stdout.lines().next().unwrap_or("");
assert!(
!first_line.contains("id,family"),
"First line should not be headers with --no-headers flag"
);
assert!(stdout.contains("patient-1") || stdout.contains("Smith"));
}
fn create_test_ndjson() -> String {
r#"{"resourceType": "Patient", "id": "ndjson-p1", "name": [{"family": "NdFirst", "given": ["Test"]}]}
{"resourceType": "Patient", "id": "ndjson-p2", "name": [{"family": "NdSecond", "given": ["Test"]}]}
{"resourceType": "Patient", "id": "ndjson-p3", "name": [{"family": "NdThird", "given": ["Test"]}]}"#.to_string()
}
#[test]
fn test_cli_with_ndjson_file_via_source() {
let cli_path = get_cli_binary_path();
if !cli_path.exists() {
panic!(
"CLI binary not found at {:?}. Run 'cargo build' first.",
cli_path
);
}
let temp_dir = TempDir::new().unwrap();
let view_path = temp_dir.path().join("view.json");
let ndjson_path = temp_dir.path().join("data.ndjson");
fs::write(&view_path, create_test_view_definition()).unwrap();
fs::write(&ndjson_path, create_test_ndjson()).unwrap();
let ndjson_url = format!("file://{}", ndjson_path.to_string_lossy());
let output = Command::new(&cli_path)
.arg("-v")
.arg(&view_path)
.arg("-s")
.arg(&ndjson_url)
.arg("-f")
.arg("csv")
.output()
.expect("Failed to execute CLI");
if !output.status.success() {
eprintln!("CLI error: {}", String::from_utf8_lossy(&output.stderr));
panic!("CLI command failed");
}
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("ndjson-p1") || stdout.contains("NdFirst"),
"Should contain first NDJSON patient"
);
assert!(
stdout.contains("ndjson-p2") || stdout.contains("NdSecond"),
"Should contain second NDJSON patient"
);
assert!(
stdout.contains("ndjson-p3") || stdout.contains("NdThird"),
"Should contain third NDJSON patient"
);
}
#[test]
fn test_cli_with_ndjson_file_via_bundle() {
let cli_path = get_cli_binary_path();
if !cli_path.exists() {
panic!(
"CLI binary not found at {:?}. Run 'cargo build' first.",
cli_path
);
}
let temp_dir = TempDir::new().unwrap();
let view_path = temp_dir.path().join("view.json");
let ndjson_path = temp_dir.path().join("data.ndjson");
fs::write(&view_path, create_test_view_definition()).unwrap();
fs::write(&ndjson_path, create_test_ndjson()).unwrap();
let output = Command::new(&cli_path)
.arg("-v")
.arg(&view_path)
.arg("-b")
.arg(&ndjson_path)
.arg("-f")
.arg("csv")
.output()
.expect("Failed to execute CLI");
if !output.status.success() {
eprintln!("CLI error: {}", String::from_utf8_lossy(&output.stderr));
panic!("CLI command failed");
}
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("ndjson-p1") || stdout.contains("NdFirst"));
assert!(stdout.contains("ndjson-p2") || stdout.contains("NdSecond"));
assert!(stdout.contains("ndjson-p3") || stdout.contains("NdThird"));
}
#[test]
fn test_cli_with_ndjson_content_fallback() {
let cli_path = get_cli_binary_path();
if !cli_path.exists() {
panic!(
"CLI binary not found at {:?}. Run 'cargo build' first.",
cli_path
);
}
let temp_dir = TempDir::new().unwrap();
let view_path = temp_dir.path().join("view.json");
let data_path = temp_dir.path().join("data.json");
fs::write(&view_path, create_test_view_definition()).unwrap();
fs::write(&data_path, create_test_ndjson()).unwrap();
let data_url = format!("file://{}", data_path.to_string_lossy());
let output = Command::new(&cli_path)
.arg("-v")
.arg(&view_path)
.arg("-s")
.arg(&data_url)
.arg("-f")
.arg("csv")
.output()
.expect("Failed to execute CLI");
if !output.status.success() {
eprintln!("CLI error: {}", String::from_utf8_lossy(&output.stderr));
panic!("CLI command failed - content-based NDJSON detection should work");
}
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("ndjson-p1") || stdout.contains("NdFirst"));
}
#[test]
fn test_cli_with_ndjson_and_bundle_mixed() {
let cli_path = get_cli_binary_path();
if !cli_path.exists() {
panic!(
"CLI binary not found at {:?}. Run 'cargo build' first.",
cli_path
);
}
let temp_dir = TempDir::new().unwrap();
let view_path = temp_dir.path().join("view.json");
let ndjson_path = temp_dir.path().join("data.ndjson");
let bundle_path = temp_dir.path().join("bundle.json");
fs::write(&view_path, create_test_view_definition()).unwrap();
fs::write(&ndjson_path, create_test_ndjson()).unwrap();
fs::write(&bundle_path, create_test_bundle()).unwrap();
let ndjson_url = format!("file://{}", ndjson_path.to_string_lossy());
let output = Command::new(&cli_path)
.arg("-v")
.arg(&view_path)
.arg("-s")
.arg(&ndjson_url)
.arg("-b")
.arg(&bundle_path)
.arg("-f")
.arg("csv")
.output()
.expect("Failed to execute CLI");
if !output.status.success() {
eprintln!("CLI error: {}", String::from_utf8_lossy(&output.stderr));
panic!("CLI command failed");
}
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("ndjson-p1") || stdout.contains("NdFirst"),
"Should contain NDJSON patients"
);
assert!(
stdout.contains("patient-1") || stdout.contains("Smith"),
"Should contain Bundle patients"
);
}
#[test]
fn test_cli_with_ndjson_json_output() {
let cli_path = get_cli_binary_path();
if !cli_path.exists() {
panic!(
"CLI binary not found at {:?}. Run 'cargo build' first.",
cli_path
);
}
let temp_dir = TempDir::new().unwrap();
let view_path = temp_dir.path().join("view.json");
let ndjson_path = temp_dir.path().join("data.ndjson");
fs::write(&view_path, create_test_view_definition()).unwrap();
fs::write(&ndjson_path, create_test_ndjson()).unwrap();
let ndjson_url = format!("file://{}", ndjson_path.to_string_lossy());
let output = Command::new(&cli_path)
.arg("-v")
.arg(&view_path)
.arg("-s")
.arg(&ndjson_url)
.arg("-f")
.arg("json")
.output()
.expect("Failed to execute CLI");
if !output.status.success() {
eprintln!("CLI error: {}", String::from_utf8_lossy(&output.stderr));
panic!("CLI command failed");
}
let stdout = String::from_utf8_lossy(&output.stdout);
let parsed: Result<serde_json::Value, _> = serde_json::from_str(&stdout);
assert!(parsed.is_ok(), "Output should be valid JSON");
let json_value = parsed.unwrap();
assert!(json_value.is_array(), "JSON output should be an array");
assert_eq!(
json_value.as_array().unwrap().len(),
3,
"Should have 3 patients from NDJSON"
);
}