use serde_json::json;
use helios_persistence::core::{ResourceStorage, SearchProvider};
use helios_persistence::tenant::{TenantContext, TenantId, TenantPermissions};
use helios_persistence::types::{
Pagination, SearchParamType, SearchParameter, SearchQuery, SearchValue,
};
#[cfg(feature = "sqlite")]
use helios_persistence::backends::sqlite::SqliteBackend;
#[cfg(feature = "sqlite")]
fn create_sqlite_backend() -> SqliteBackend {
let backend = SqliteBackend::in_memory().expect("Failed to create SQLite backend");
backend.init_schema().expect("Failed to initialize schema");
backend
}
fn create_tenant() -> TenantContext {
TenantContext::new(TenantId::new("test-tenant"), TenantPermissions::full_access())
}
#[cfg(feature = "sqlite")]
async fn seed_test_data(backend: &SqliteBackend, tenant: &TenantContext) {
let patient1 = json!({"resourceType": "Patient", "id": "patient-1", "name": [{"family": "Smith"}]});
let patient2 = json!({"resourceType": "Patient", "id": "patient-2", "name": [{"family": "Jones"}]});
backend.create_or_update(tenant, "Patient", "patient-1", patient1).await.unwrap();
backend.create_or_update(tenant, "Patient", "patient-2", patient2).await.unwrap();
let obs1 = json!({
"resourceType": "Observation",
"status": "final",
"subject": {"reference": "Patient/patient-1"},
"code": {"coding": [{"code": "test1"}]}
});
let obs2 = json!({
"resourceType": "Observation",
"status": "final",
"subject": {"reference": "Patient/patient-1"},
"code": {"coding": [{"code": "test2"}]}
});
let obs3 = json!({
"resourceType": "Observation",
"status": "final",
"subject": {"reference": "Patient/patient-2"},
"code": {"coding": [{"code": "test3"}]}
});
backend.create(tenant, "Observation", obs1).await.unwrap();
backend.create(tenant, "Observation", obs2).await.unwrap();
backend.create(tenant, "Observation", obs3).await.unwrap();
}
#[cfg(feature = "sqlite")]
#[tokio::test]
async fn test_reference_search_relative() {
let backend = create_sqlite_backend();
let tenant = create_tenant();
seed_test_data(&backend, &tenant).await;
let query = SearchQuery::new("Observation").with_parameter(SearchParameter {
name: "subject".to_string(),
param_type: SearchParamType::Reference,
modifier: None,
values: vec![SearchValue::reference("Patient/patient-1")],
chain: vec![],
components: vec![],
});
let result = backend
.search(&tenant, &query, Pagination::new(100))
.await
.unwrap();
assert_eq!(result.resources.len(), 2);
for resource in &result.resources {
assert_eq!(
resource.content()["subject"]["reference"],
"Patient/patient-1"
);
}
}
#[cfg(feature = "sqlite")]
#[tokio::test]
async fn test_reference_search_id_only() {
let backend = create_sqlite_backend();
let tenant = create_tenant();
seed_test_data(&backend, &tenant).await;
let query = SearchQuery::new("Observation").with_parameter(SearchParameter {
name: "subject".to_string(),
param_type: SearchParamType::Reference,
modifier: None,
values: vec![SearchValue::reference("patient-1")],
chain: vec![],
components: vec![],
});
let result = backend
.search(&tenant, &query, Pagination::new(100))
.await
.unwrap();
assert!(!result.resources.is_empty());
}
#[cfg(feature = "sqlite")]
#[tokio::test]
async fn test_reference_search_type_modifier() {
let backend = create_sqlite_backend();
let tenant = create_tenant();
seed_test_data(&backend, &tenant).await;
let query = SearchQuery::new("Observation").with_parameter(SearchParameter {
name: "subject:Patient".to_string(),
param_type: SearchParamType::Reference,
modifier: None,
values: vec![SearchValue::reference("patient-1")],
chain: vec![],
components: vec![],
});
let result = backend
.search(&tenant, &query, Pagination::new(100))
.await
.unwrap();
assert!(!result.resources.is_empty());
}
#[cfg(feature = "sqlite")]
#[tokio::test]
async fn test_reference_search_no_results() {
let backend = create_sqlite_backend();
let tenant = create_tenant();
seed_test_data(&backend, &tenant).await;
let query = SearchQuery::new("Observation").with_parameter(SearchParameter {
name: "subject".to_string(),
param_type: SearchParamType::Reference,
modifier: None,
values: vec![SearchValue::reference("Patient/nonexistent")],
chain: vec![],
components: vec![],
});
let result = backend
.search(&tenant, &query, Pagination::new(100))
.await
.unwrap();
assert!(result.resources.is_empty());
}