use helios_sof::SofBundle;
use helios_sof::data_source::{DataSource, UniversalDataSource};
use std::io::Write;
use tempfile::NamedTempFile;
#[tokio::test]
async fn test_ndjson_multiple_patients() {
let data_source = UniversalDataSource::new();
let ndjson_content = r#"{"resourceType": "Patient", "id": "patient-1", "gender": "male"}
{"resourceType": "Patient", "id": "patient-2", "gender": "female"}
{"resourceType": "Patient", "id": "patient-3", "gender": "other"}"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file
.as_file_mut()
.write_all(ndjson_content.as_bytes())
.unwrap();
temp_file.flush().unwrap();
let temp_path = temp_file.path().to_path_buf();
let ndjson_path = temp_path.with_extension("ndjson");
std::fs::copy(&temp_path, &ndjson_path).unwrap();
let file_url = format!("file://{}", ndjson_path.to_string_lossy());
let result = data_source.load(&file_url).await;
assert!(result.is_ok(), "Failed to load NDJSON file: {:?}", result);
#[cfg(feature = "R4")]
match result.unwrap() {
SofBundle::R4(bundle) => {
let entries = bundle.entry.as_ref().unwrap();
assert_eq!(entries.len(), 3, "Expected 3 resources in bundle");
}
#[cfg(feature = "R4B")]
SofBundle::R4B(_) => panic!("Expected R4 bundle"),
#[cfg(feature = "R5")]
SofBundle::R5(_) => panic!("Expected R4 bundle"),
#[cfg(feature = "R6")]
SofBundle::R6(_) => panic!("Expected R4 bundle"),
}
let _ = std::fs::remove_file(&ndjson_path);
}
#[tokio::test]
async fn test_ndjson_mixed_resources() {
let data_source = UniversalDataSource::new();
let ndjson_content = r#"{"resourceType": "Patient", "id": "p1"}
{"resourceType": "Observation", "id": "obs1", "status": "final", "code": {"text": "Test"}}
{"resourceType": "Condition", "id": "cond1", "clinicalStatus": {"text": "active"}, "code": {"text": "Test"}}"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file
.as_file_mut()
.write_all(ndjson_content.as_bytes())
.unwrap();
temp_file.flush().unwrap();
let temp_path = temp_file.path().to_path_buf();
let ndjson_path = temp_path.with_extension("ndjson");
std::fs::copy(&temp_path, &ndjson_path).unwrap();
let file_url = format!("file://{}", ndjson_path.to_string_lossy());
let result = data_source.load(&file_url).await;
assert!(result.is_ok(), "Failed to load mixed NDJSON: {:?}", result);
#[cfg(feature = "R4")]
match result.unwrap() {
SofBundle::R4(bundle) => {
assert_eq!(bundle.entry.as_ref().unwrap().len(), 3);
}
#[cfg(feature = "R4B")]
SofBundle::R4B(_) => panic!("Expected R4 bundle"),
#[cfg(feature = "R5")]
SofBundle::R5(_) => panic!("Expected R4 bundle"),
#[cfg(feature = "R6")]
SofBundle::R6(_) => panic!("Expected R4 bundle"),
}
let _ = std::fs::remove_file(&ndjson_path);
}
#[tokio::test]
async fn test_ndjson_with_empty_lines() {
let data_source = UniversalDataSource::new();
let ndjson_content = r#"{"resourceType": "Patient", "id": "p1"}
{"resourceType": "Patient", "id": "p2"}
{"resourceType": "Patient", "id": "p3"}
"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file
.as_file_mut()
.write_all(ndjson_content.as_bytes())
.unwrap();
temp_file.flush().unwrap();
let temp_path = temp_file.path().to_path_buf();
let ndjson_path = temp_path.with_extension("ndjson");
std::fs::copy(&temp_path, &ndjson_path).unwrap();
let file_url = format!("file://{}", ndjson_path.to_string_lossy());
let result = data_source.load(&file_url).await;
assert!(result.is_ok());
#[cfg(feature = "R4")]
match result.unwrap() {
SofBundle::R4(bundle) => {
assert_eq!(bundle.entry.as_ref().unwrap().len(), 3);
}
#[cfg(feature = "R4B")]
SofBundle::R4B(_) => panic!("Expected R4 bundle"),
#[cfg(feature = "R5")]
SofBundle::R5(_) => panic!("Expected R4 bundle"),
#[cfg(feature = "R6")]
SofBundle::R6(_) => panic!("Expected R4 bundle"),
}
let _ = std::fs::remove_file(&ndjson_path);
}
#[tokio::test]
async fn test_ndjson_content_detection() {
let data_source = UniversalDataSource::new();
let ndjson_content = r#"{"resourceType": "Patient", "id": "p1"}
{"resourceType": "Patient", "id": "p2"}"#;
let mut temp_file = NamedTempFile::with_suffix(".json").unwrap();
temp_file
.as_file_mut()
.write_all(ndjson_content.as_bytes())
.unwrap();
temp_file.flush().unwrap();
let file_url = format!("file://{}", temp_file.path().to_string_lossy());
let result = data_source.load(&file_url).await;
assert!(result.is_ok(), "Content-based NDJSON detection failed");
#[cfg(feature = "R4")]
match result.unwrap() {
SofBundle::R4(bundle) => {
assert_eq!(bundle.entry.as_ref().unwrap().len(), 2);
}
#[cfg(feature = "R4B")]
SofBundle::R4B(_) => panic!("Expected R4 bundle"),
#[cfg(feature = "R5")]
SofBundle::R5(_) => panic!("Expected R4 bundle"),
#[cfg(feature = "R6")]
SofBundle::R6(_) => panic!("Expected R4 bundle"),
}
}
#[tokio::test]
async fn test_ndjson_partial_invalid() {
let data_source = UniversalDataSource::new();
let ndjson_content = r#"{"resourceType": "Patient", "id": "p1"}
{invalid json line}
{"resourceType": "Patient", "id": "p2"}
{"missing": "resourceType"}
{"resourceType": "Patient", "id": "p3"}"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file
.as_file_mut()
.write_all(ndjson_content.as_bytes())
.unwrap();
temp_file.flush().unwrap();
let temp_path = temp_file.path().to_path_buf();
let ndjson_path = temp_path.with_extension("ndjson");
std::fs::copy(&temp_path, &ndjson_path).unwrap();
let file_url = format!("file://{}", ndjson_path.to_string_lossy());
let result = data_source.load(&file_url).await;
assert!(result.is_ok());
#[cfg(feature = "R4")]
match result.unwrap() {
SofBundle::R4(bundle) => {
assert_eq!(bundle.entry.as_ref().unwrap().len(), 3);
}
#[cfg(feature = "R4B")]
SofBundle::R4B(_) => panic!("Expected R4 bundle"),
#[cfg(feature = "R5")]
SofBundle::R5(_) => panic!("Expected R4 bundle"),
#[cfg(feature = "R6")]
SofBundle::R6(_) => panic!("Expected R4 bundle"),
}
let _ = std::fs::remove_file(&ndjson_path);
}
#[tokio::test]
async fn test_ndjson_all_invalid() {
let data_source = UniversalDataSource::new();
let ndjson_content = r#"{invalid json}
{also: invalid}
not even json"#;
let mut temp_file = NamedTempFile::new().unwrap();
temp_file
.as_file_mut()
.write_all(ndjson_content.as_bytes())
.unwrap();
temp_file.flush().unwrap();
let temp_path = temp_file.path().to_path_buf();
let ndjson_path = temp_path.with_extension("ndjson");
std::fs::copy(&temp_path, &ndjson_path).unwrap();
let file_url = format!("file://{}", ndjson_path.to_string_lossy());
let result = data_source.load(&file_url).await;
assert!(result.is_err());
let _ = std::fs::remove_file(&ndjson_path);
}
#[tokio::test]
async fn test_ndjson_empty_file() {
let data_source = UniversalDataSource::new();
let ndjson_content = "";
let mut temp_file = NamedTempFile::new().unwrap();
temp_file
.as_file_mut()
.write_all(ndjson_content.as_bytes())
.unwrap();
temp_file.flush().unwrap();
let temp_path = temp_file.path().to_path_buf();
let ndjson_path = temp_path.with_extension("ndjson");
std::fs::copy(&temp_path, &ndjson_path).unwrap();
let file_url = format!("file://{}", ndjson_path.to_string_lossy());
let result = data_source.load(&file_url).await;
assert!(result.is_err());
let _ = std::fs::remove_file(&ndjson_path);
}
#[tokio::test]
async fn test_ndjson_only_whitespace() {
let data_source = UniversalDataSource::new();
let ndjson_content = " \n \n \n";
let mut temp_file = NamedTempFile::new().unwrap();
temp_file
.as_file_mut()
.write_all(ndjson_content.as_bytes())
.unwrap();
temp_file.flush().unwrap();
let temp_path = temp_file.path().to_path_buf();
let ndjson_path = temp_path.with_extension("ndjson");
std::fs::copy(&temp_path, &ndjson_path).unwrap();
let file_url = format!("file://{}", ndjson_path.to_string_lossy());
let result = data_source.load(&file_url).await;
assert!(result.is_err());
let _ = std::fs::remove_file(&ndjson_path);
}
#[tokio::test]
async fn test_json_bundle_still_works() {
let data_source = UniversalDataSource::new();
let bundle_json = r#"{
"resourceType": "Bundle",
"type": "collection",
"entry": [{
"resource": {
"resourceType": "Patient",
"id": "test-patient"
}
}]
}"#;
let mut temp_file = NamedTempFile::with_suffix(".json").unwrap();
temp_file
.as_file_mut()
.write_all(bundle_json.as_bytes())
.unwrap();
temp_file.flush().unwrap();
let file_url = format!("file://{}", temp_file.path().to_string_lossy());
let result = data_source.load(&file_url).await;
assert!(result.is_ok(), "Regular JSON Bundle loading broken");
#[cfg(feature = "R4")]
match result.unwrap() {
SofBundle::R4(bundle) => {
assert_eq!(bundle.entry.as_ref().unwrap().len(), 1);
}
#[cfg(feature = "R4B")]
SofBundle::R4B(_) => panic!("Expected R4 bundle"),
#[cfg(feature = "R5")]
SofBundle::R5(_) => panic!("Expected R4 bundle"),
#[cfg(feature = "R6")]
SofBundle::R6(_) => panic!("Expected R4 bundle"),
}
}