use super::*;
use amaters_core::storage::MemoryStorage;
use amaters_core::types::{CipherBlob, Key};
#[tokio::test]
async fn test_service_creation() {
let storage = Arc::new(MemoryStorage::new());
let service = AqlServiceImpl::new(storage);
assert!(service.start_time.elapsed().as_secs() < 1);
}
#[tokio::test]
async fn test_get_query_execution() {
let storage = Arc::new(MemoryStorage::new());
let key = Key::from_str("test_key");
let value = CipherBlob::new(vec![1, 2, 3, 4, 5]);
storage.put(&key, &value).await.expect("Failed to put");
let service = AqlServiceImpl::new(storage);
let query = Query::Get {
collection: "test".to_string(),
key: key.clone(),
};
let result = service.execute_query_internal(query).await;
assert!(result.is_ok());
let query_result = result.expect("Query failed");
match query_result.result {
Some(query::query_result::Result::Single(single)) => {
assert!(single.value.is_some());
}
_ => panic!("Expected single result"),
}
}
#[tokio::test]
async fn test_set_query_execution() {
let storage = Arc::new(MemoryStorage::new());
let service = AqlServiceImpl::new(storage.clone());
let key = Key::from_str("test_key");
let value = CipherBlob::new(vec![1, 2, 3, 4, 5]);
let query = Query::Set {
collection: "test".to_string(),
key: key.clone(),
value: value.clone(),
};
let result = service.execute_query_internal(query).await;
assert!(result.is_ok());
let stored = storage.get(&key).await.expect("Failed to get");
assert!(stored.is_some());
assert_eq!(stored.expect("No value"), value);
}
#[tokio::test]
async fn test_delete_query_execution() {
let storage = Arc::new(MemoryStorage::new());
let key = Key::from_str("test_key");
let value = CipherBlob::new(vec![1, 2, 3, 4, 5]);
storage.put(&key, &value).await.expect("Failed to put");
let service = AqlServiceImpl::new(storage.clone());
let query = Query::Delete {
collection: "test".to_string(),
key: key.clone(),
};
let result = service.execute_query_internal(query).await;
assert!(result.is_ok());
let stored = storage.get(&key).await.expect("Failed to get");
assert!(stored.is_none());
}
#[tokio::test]
async fn test_range_query_execution() {
let storage = Arc::new(MemoryStorage::new());
for i in 0..10 {
let key = Key::from_str(&format!("key_{:02}", i));
let value = CipherBlob::new(vec![i as u8]);
storage.put(&key, &value).await.expect("Failed to put");
}
let service = AqlServiceImpl::new(storage);
let query = Query::Range {
collection: "test".to_string(),
start: Key::from_str("key_03"),
end: Key::from_str("key_07"),
};
let result = service.execute_query_internal(query).await;
assert!(result.is_ok());
let query_result = result.expect("Query failed");
match query_result.result {
Some(query::query_result::Result::Multi(multi)) => {
assert!(!multi.values.is_empty());
}
_ => panic!("Expected multi result"),
}
}
#[tokio::test]
async fn test_get_nonexistent_key() {
let storage = Arc::new(MemoryStorage::new());
let service = AqlServiceImpl::new(storage);
let query = Query::Get {
collection: "test".to_string(),
key: Key::from_str("nonexistent"),
};
let result = service.execute_query_internal(query).await;
assert!(result.is_ok());
let query_result = result.expect("Query failed");
match query_result.result {
Some(query::query_result::Result::Single(single)) => {
assert!(single.value.is_none());
}
_ => panic!("Expected single result"),
}
}
#[tokio::test]
async fn test_health_check() {
let storage = Arc::new(MemoryStorage::new());
let service = AqlServiceImpl::new(storage);
let request = aql::HealthCheckRequest { service: None };
let response = service.health_check(request).await;
assert_eq!(response.status, aql::HealthStatus::HealthServing as i32);
}
#[tokio::test]
async fn test_server_info() {
let storage = Arc::new(MemoryStorage::new());
let service = AqlServiceImpl::new(storage);
let request = aql::ServerInfoRequest {};
let response = service.get_server_info(request).await;
assert!(response.version.is_some());
assert!(!response.capabilities.is_empty());
assert!(response.capabilities.contains(&"query.get".to_string()));
}
#[cfg(feature = "compute")]
#[tokio::test]
async fn test_server_info_advertises_filter() {
let storage = Arc::new(MemoryStorage::new());
let service = AqlServiceImpl::new(storage);
let request = aql::ServerInfoRequest {};
let response = service.get_server_info(request).await;
assert!(
response.capabilities.contains(&"query.filter".to_string()),
"capabilities should advertise query.filter when compute feature is enabled"
);
}
#[cfg(feature = "compute")]
#[tokio::test]
async fn test_filter_query_execution() {
use amaters_core::{ColumnRef, Predicate};
let storage = Arc::new(MemoryStorage::new());
for i in 0u8..5 {
let key = Key::from_str(&format!("row_{:02}", i));
let value = CipherBlob::new(vec![i]);
storage
.put(&key, &value)
.await
.expect("Failed to insert test data");
}
let service = AqlServiceImpl::new(storage);
let rhs_blob = CipherBlob::new(vec![2]);
let predicate = Predicate::Gt(ColumnRef::new("value".to_string()), rhs_blob);
let filter_query = Query::Filter {
collection: "test".to_string(),
predicate,
};
let result = service
.execute_query_internal(filter_query)
.await
.expect("plaintext filter query should succeed");
match result.result {
Some(query::query_result::Result::Multi(multi)) => {
assert_eq!(
multi.values.len(),
2,
"expected 2 matching rows (values 3 and 4)"
);
for kv in &multi.values {
assert!(
kv.encrypted_predicate_result.is_none(),
"plaintext filter results should not carry encrypted_predicate_result"
);
}
}
other => panic!("Expected Multi result from filter query, got {:?}", other),
}
}
#[cfg(not(feature = "compute"))]
#[tokio::test]
async fn test_filter_query_requires_compute_feature() {
use amaters_core::{ColumnRef, Predicate};
let storage = Arc::new(MemoryStorage::new());
let service = AqlServiceImpl::new(storage);
let rhs_blob = CipherBlob::new(vec![1]);
let predicate = Predicate::Gt(ColumnRef::new("value".to_string()), rhs_blob);
let filter_query = Query::Filter {
collection: "test".to_string(),
predicate,
};
let result = service.execute_query_internal(filter_query).await;
assert!(
result.is_err(),
"Filter should fail without compute feature"
);
let err_msg = result
.as_ref()
.err()
.map(|e| e.to_string())
.unwrap_or_default();
assert!(
err_msg.contains("compute feature"),
"Error should mention compute feature: {}",
err_msg
);
}
#[cfg(not(feature = "compute"))]
fn dummy_predicate() -> amaters_core::Predicate {
amaters_core::Predicate::Eq(
amaters_core::ColumnRef::new("col"),
CipherBlob::new(vec![0]),
)
}
#[cfg(not(feature = "compute"))]
#[tokio::test]
async fn test_update_set_single_key() {
let storage = Arc::new(MemoryStorage::new());
let key = Key::from_str("row_00");
let original = CipherBlob::new(vec![10, 20, 30]);
storage.put(&key, &original).await.expect("Failed to put");
let service = AqlServiceImpl::new(storage.clone());
let new_blob = CipherBlob::new(vec![99, 88, 77]);
let query = Query::Update {
collection: "test".to_string(),
predicate: dummy_predicate(),
updates: vec![amaters_core::Update::Set(
amaters_core::ColumnRef::new("val"),
new_blob.clone(),
)],
};
let result = service
.execute_query_internal(query)
.await
.expect("Update failed");
match result.result {
Some(query::query_result::Result::Success(s)) => {
assert_eq!(s.affected_rows, 1);
}
other => panic!("Expected Success, got {:?}", other),
}
let stored = storage
.get(&key)
.await
.expect("Failed to get")
.expect("Key missing after update");
assert_eq!(stored, new_blob);
}
#[cfg(not(feature = "compute"))]
#[tokio::test]
async fn test_update_set_multiple_keys() {
let storage = Arc::new(MemoryStorage::new());
for i in 0u8..5 {
let key = Key::from_str(&format!("row_{:02}", i));
let value = CipherBlob::new(vec![i]);
storage.put(&key, &value).await.expect("Failed to put");
}
let service = AqlServiceImpl::new(storage.clone());
let replacement = CipherBlob::new(vec![255]);
let query = Query::Update {
collection: "data".to_string(),
predicate: dummy_predicate(),
updates: vec![amaters_core::Update::Set(
amaters_core::ColumnRef::new("v"),
replacement.clone(),
)],
};
let result = service
.execute_query_internal(query)
.await
.expect("Update failed");
match result.result {
Some(query::query_result::Result::Success(s)) => {
assert_eq!(s.affected_rows, 5);
}
other => panic!("Expected Success, got {:?}", other),
}
for i in 0u8..5 {
let key = Key::from_str(&format!("row_{:02}", i));
let stored = storage
.get(&key)
.await
.expect("Failed to get")
.expect("Key missing");
assert_eq!(stored, replacement);
}
}
#[cfg(not(feature = "compute"))]
#[tokio::test]
async fn test_update_nonexistent_collection() {
let storage = Arc::new(MemoryStorage::new());
let service = AqlServiceImpl::new(storage);
let query = Query::Update {
collection: "ghost".to_string(),
predicate: dummy_predicate(),
updates: vec![amaters_core::Update::Set(
amaters_core::ColumnRef::new("x"),
CipherBlob::new(vec![1]),
)],
};
let result = service
.execute_query_internal(query)
.await
.expect("Update on empty storage should not error");
match result.result {
Some(query::query_result::Result::Success(s)) => {
assert_eq!(s.affected_rows, 0);
}
other => panic!("Expected Success with 0 rows, got {:?}", other),
}
}
#[cfg(not(feature = "compute"))]
#[tokio::test]
async fn test_update_add_operation() {
let storage = Arc::new(MemoryStorage::new());
let key = Key::from_str("counter");
let original = CipherBlob::new(vec![10, 20]);
storage.put(&key, &original).await.expect("Failed to put");
let service = AqlServiceImpl::new(storage.clone());
let addend = CipherBlob::new(vec![5, 3]);
let query = Query::Update {
collection: "c".to_string(),
predicate: dummy_predicate(),
updates: vec![amaters_core::Update::Add(
amaters_core::ColumnRef::new("v"),
addend,
)],
};
service
.execute_query_internal(query)
.await
.expect("Update failed");
let stored = storage
.get(&key)
.await
.expect("Failed to get")
.expect("Key missing");
assert_eq!(stored.as_bytes(), &[15, 23]);
}
#[cfg(not(feature = "compute"))]
#[tokio::test]
async fn test_update_mul_operation() {
let storage = Arc::new(MemoryStorage::new());
let key = Key::from_str("product");
let original = CipherBlob::new(vec![3, 4]);
storage.put(&key, &original).await.expect("Failed to put");
let service = AqlServiceImpl::new(storage.clone());
let factor = CipherBlob::new(vec![2, 5]);
let query = Query::Update {
collection: "c".to_string(),
predicate: dummy_predicate(),
updates: vec![amaters_core::Update::Mul(
amaters_core::ColumnRef::new("v"),
factor,
)],
};
service
.execute_query_internal(query)
.await
.expect("Update failed");
let stored = storage
.get(&key)
.await
.expect("Failed to get")
.expect("Key missing");
assert_eq!(stored.as_bytes(), &[6, 20]);
}
#[cfg(not(feature = "compute"))]
#[tokio::test]
async fn test_update_multiple_operations_per_key() {
let storage = Arc::new(MemoryStorage::new());
let key = Key::from_str("multi_op");
let original = CipherBlob::new(vec![2]);
storage.put(&key, &original).await.expect("Failed to put");
let service = AqlServiceImpl::new(storage.clone());
let query = Query::Update {
collection: "c".to_string(),
predicate: dummy_predicate(),
updates: vec![
amaters_core::Update::Add(amaters_core::ColumnRef::new("v"), CipherBlob::new(vec![3])),
amaters_core::Update::Mul(amaters_core::ColumnRef::new("v"), CipherBlob::new(vec![10])),
],
};
service
.execute_query_internal(query)
.await
.expect("Update failed");
let stored = storage
.get(&key)
.await
.expect("Failed to get")
.expect("Key missing");
assert_eq!(stored.as_bytes(), &[50]);
}
#[cfg(not(feature = "compute"))]
#[tokio::test]
async fn test_update_returns_affected_count() {
let storage = Arc::new(MemoryStorage::new());
for i in 0u8..7 {
let key = Key::from_str(&format!("k{}", i));
storage
.put(&key, &CipherBlob::new(vec![i]))
.await
.expect("Failed to put");
}
let service = AqlServiceImpl::new(storage);
let query = Query::Update {
collection: "c".to_string(),
predicate: dummy_predicate(),
updates: vec![amaters_core::Update::Set(
amaters_core::ColumnRef::new("v"),
CipherBlob::new(vec![0]),
)],
};
let result = service
.execute_query_internal(query)
.await
.expect("Update failed");
match result.result {
Some(query::query_result::Result::Success(s)) => {
assert_eq!(s.affected_rows, 7);
}
other => panic!("Expected Success with 7 rows, got {:?}", other),
}
}
#[cfg(not(feature = "compute"))]
#[tokio::test]
async fn test_update_preserves_other_collections() {
let storage = Arc::new(MemoryStorage::new());
let key_a = Key::from_str("collA_row1");
let key_b = Key::from_str("collB_row1");
let val_a = CipherBlob::new(vec![1, 2, 3]);
let val_b = CipherBlob::new(vec![4, 5, 6]);
storage.put(&key_a, &val_a).await.expect("Failed to put A");
storage.put(&key_b, &val_b).await.expect("Failed to put B");
let service = AqlServiceImpl::new(storage.clone());
let query = Query::Update {
collection: "collA".to_string(),
predicate: dummy_predicate(),
updates: vec![amaters_core::Update::Set(
amaters_core::ColumnRef::new("v"),
CipherBlob::new(vec![99]),
)],
};
service
.execute_query_internal(query)
.await
.expect("Update failed");
let stored_a = storage.get(&key_a).await.expect("Failed to get A");
assert!(stored_a.is_some(), "key_a should still exist");
let stored_b = storage.get(&key_b).await.expect("Failed to get B");
assert!(stored_b.is_some(), "key_b should still exist");
}
#[cfg(not(feature = "compute"))]
#[tokio::test]
async fn test_update_empty_updates_vec() {
let storage = Arc::new(MemoryStorage::new());
let key = Key::from_str("keep_me");
let original = CipherBlob::new(vec![42]);
storage.put(&key, &original).await.expect("Failed to put");
let service = AqlServiceImpl::new(storage.clone());
let query = Query::Update {
collection: "c".to_string(),
predicate: dummy_predicate(),
updates: vec![], };
let result = service
.execute_query_internal(query)
.await
.expect("Update with empty ops should succeed");
match result.result {
Some(query::query_result::Result::Success(s)) => {
assert_eq!(s.affected_rows, 1);
}
other => panic!("Expected Success, got {:?}", other),
}
let stored = storage
.get(&key)
.await
.expect("Failed to get")
.expect("Key missing");
assert_eq!(stored, original);
}
#[cfg(not(feature = "compute"))]
#[tokio::test]
async fn test_update_then_select_verifies_changes() {
let storage = Arc::new(MemoryStorage::new());
for i in 0u8..3 {
let key = Key::from_str(&format!("sel_{:02}", i));
let value = CipherBlob::new(vec![i, i, i]);
storage.put(&key, &value).await.expect("Failed to put");
}
let service = AqlServiceImpl::new(storage.clone());
let update_query = Query::Update {
collection: "c".to_string(),
predicate: dummy_predicate(),
updates: vec![amaters_core::Update::Add(
amaters_core::ColumnRef::new("v"),
CipherBlob::new(vec![1, 1, 1]),
)],
};
service
.execute_query_internal(update_query)
.await
.expect("Update failed");
for i in 0u8..3 {
let key = Key::from_str(&format!("sel_{:02}", i));
let get_query = Query::Get {
collection: "c".to_string(),
key: key.clone(),
};
let result = service
.execute_query_internal(get_query)
.await
.expect("Get failed");
match result.result {
Some(query::query_result::Result::Single(single)) => {
let proto_val = single.value.expect("Expected value from get");
let expected = vec![i + 1, i + 1, i + 1];
assert_eq!(
proto_val.data, expected,
"Row sel_{:02} should have been updated",
i
);
}
other => panic!("Expected Single result, got {:?}", other),
}
}
}
#[cfg(feature = "compute")]
#[tokio::test]
async fn test_update_with_compute_feature() {
use amaters_core::{ColumnRef, Predicate};
let storage = Arc::new(MemoryStorage::new());
for i in 0u8..3 {
let key = Key::from_str(&format!("row_{:02}", i));
let value = CipherBlob::new(vec![i]);
storage
.put(&key, &value)
.await
.expect("Failed to insert test data");
}
let service = AqlServiceImpl::new(storage);
let rhs_blob = CipherBlob::new(vec![1]);
let predicate = Predicate::Eq(ColumnRef::new("value"), rhs_blob);
let update_query = Query::Update {
collection: "test".to_string(),
predicate,
updates: vec![amaters_core::Update::Set(
ColumnRef::new("v"),
CipherBlob::new(vec![99]),
)],
};
let result = service.execute_query_internal(update_query).await;
match result {
Ok(query_result) => {
match query_result.result {
Some(query::query_result::Result::Success(s)) => {
assert!(s.affected_rows <= 3);
}
other => panic!("Expected Success result from update, got {:?}", other),
}
}
Err(e) => {
let msg = e.to_string();
assert!(
msg.contains("FHE")
|| msg.contains("fhe")
|| msg.contains("Predicate compilation")
|| msg.contains("compilation failed")
|| msg.contains("execution")
|| msg.contains("RHS"),
"Unexpected error from update query: {}",
msg
);
}
}
}
include!("server_rollback_tests.rs");
#[tokio::test]
async fn test_compression_feature_gate_disabled() {
let storage = Arc::new(MemoryStorage::new());
let builder = AqlServerBuilder::new(storage);
let _server = builder.build_grpc_service();
}
include!("net_integration_tests.rs");