#[cfg(test)]
mod tests {
use super::super::types::*;
use super::super::vint::encode_vint;
use crate::schema::{CqlType, UdtRegistry};
use crate::types::{UdtField, UdtTypeDef, UdtValue, Value};
#[allow(dead_code)]
fn create_sample_person_udt() -> UdtValue {
UdtValue {
type_name: "person".to_string(),
keyspace: "test_keyspace".to_string(),
fields: vec![
UdtField {
name: "first_name".to_string(),
value: Some(Value::Text("John".to_string())),
},
UdtField {
name: "last_name".to_string(),
value: Some(Value::Text("Doe".to_string())),
},
UdtField {
name: "age".to_string(),
value: Some(Value::Integer(30)),
},
UdtField {
name: "email".to_string(),
value: Some(Value::Text("john.doe@example.com".to_string())),
},
],
}
}
#[allow(dead_code)]
fn create_sample_address_udt() -> UdtValue {
UdtValue {
type_name: "address".to_string(),
keyspace: "test_keyspace".to_string(),
fields: vec![
UdtField {
name: "street".to_string(),
value: Some(Value::Text("123 Main St".to_string())),
},
UdtField {
name: "city".to_string(),
value: Some(Value::Text("Anytown".to_string())),
},
UdtField {
name: "state".to_string(),
value: Some(Value::Text("CA".to_string())),
},
UdtField {
name: "zip_code".to_string(),
value: Some(Value::Text("12345".to_string())),
},
UdtField {
name: "country".to_string(),
value: Some(Value::Text("USA".to_string())),
},
],
}
}
#[allow(dead_code)]
fn serialize_udt_for_test(udt: &UdtValue) -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(&encode_vint(udt.type_name.len() as i64));
data.extend_from_slice(udt.type_name.as_bytes());
data.extend_from_slice(&encode_vint(udt.fields.len() as i64));
for field in &udt.fields {
data.extend_from_slice(&encode_vint(field.name.len() as i64));
data.extend_from_slice(field.name.as_bytes());
let type_id = match &field.value {
Some(Value::Text(_)) => CqlTypeId::Varchar as u8,
Some(Value::Integer(_)) => CqlTypeId::Int as u8,
Some(Value::BigInt(_)) => CqlTypeId::BigInt as u8,
Some(Value::Boolean(_)) => CqlTypeId::Boolean as u8,
Some(Value::Udt(_)) => CqlTypeId::Udt as u8,
Some(Value::List(_)) => CqlTypeId::List as u8,
Some(Value::Set(_)) => CqlTypeId::Set as u8,
Some(Value::Map(_)) => CqlTypeId::Map as u8,
_ => CqlTypeId::Blob as u8,
};
data.push(type_id);
}
for field in &udt.fields {
match &field.value {
None => {
data.extend_from_slice(&(-1i32).to_be_bytes());
}
Some(value) => {
let value_data = serialize_value_for_test(value);
data.extend_from_slice(&(value_data.len() as i32).to_be_bytes());
data.extend_from_slice(&value_data);
}
}
}
data
}
fn serialize_value_for_test(value: &Value) -> Vec<u8> {
match value {
Value::Text(s) => {
s.as_bytes().to_vec()
}
Value::Integer(i) => i.to_be_bytes().to_vec(),
Value::Boolean(b) => vec![if *b { 1 } else { 0 }],
Value::Udt(udt) => serialize_udt_for_test(udt),
_ => Vec::new(), }
}
#[test]
fn test_udt_registry_creation() {
let registry = UdtRegistry::with_cassandra5_defaults();
assert!(registry.contains_udt("system", "address"));
assert!(registry.contains_udt("system", "person"));
assert!(registry.contains_udt("system", "contact_info"));
assert_eq!(registry.total_udts(), 3);
}
#[test]
fn test_udt_registry_dependency_validation() {
let registry = UdtRegistry::with_cassandra5_defaults();
let person_udt = registry
.resolve_udt_with_dependencies("system", "person")
.unwrap();
assert_eq!(person_udt.name, "person");
let result = registry.resolve_udt_with_dependencies("system", "nonexistent");
assert!(result.is_err());
}
#[test]
fn test_udt_registry_circular_dependency_detection() {
let mut registry = UdtRegistry::new();
let self_referencing_udt = UdtTypeDef::new("test".to_string(), "circular".to_string())
.with_field(
"self_ref".to_string(),
CqlType::Udt("circular".to_string(), vec![]),
true,
);
let result = registry.register_udt_with_validation(self_referencing_udt);
assert!(result.is_err(), "Should detect circular dependency");
}
#[test]
fn test_udt_registry_dependent_udts() {
let registry = UdtRegistry::with_cassandra5_defaults();
let dependents = registry.get_dependent_udts("system", "person");
assert!(!dependents.is_empty());
let contact_info_depends_on_person =
dependents.iter().any(|udt| udt.name == "contact_info");
assert!(contact_info_depends_on_person);
}
#[test]
fn test_udt_registry_export_definitions() {
let registry = UdtRegistry::with_cassandra5_defaults();
let definitions = registry.export_definitions("system");
assert!(!definitions.is_empty());
let address_def = definitions
.iter()
.find(|def| def.contains("CREATE TYPE system.address"))
.expect("Address UDT definition should exist");
assert!(address_def.contains("street text"));
assert!(address_def.contains("city text"));
}
#[test]
fn test_basic_udt_parsing() {
let person_udt = create_sample_person_udt();
let serialized = serialize_udt_for_test(&person_udt);
let (remaining, parsed_value) = parse_udt(&serialized).unwrap();
assert!(remaining.is_empty());
if let Value::Udt(parsed_udt) = parsed_value {
assert_eq!(parsed_udt.type_name, "person");
assert_eq!(parsed_udt.fields.len(), 4);
let first_name = parsed_udt.get_field("first_name").unwrap();
assert_eq!(first_name.as_str(), Some("John"));
let age = parsed_udt.get_field("age").unwrap();
assert_eq!(age.as_i32(), Some(30));
} else {
panic!("Expected UDT value");
}
}
#[test]
fn test_udt_parsing_with_registry() {
let registry = UdtRegistry::with_cassandra5_defaults();
let person_udt = create_sample_person_udt();
let serialized = serialize_udt_for_test(&person_udt);
let (remaining, parsed_value) =
parse_udt_with_registry(&serialized, "person", "system", ®istry).unwrap();
assert!(remaining.is_empty());
if let Value::Udt(parsed_udt) = parsed_value {
assert_eq!(parsed_udt.type_name, "person");
assert_eq!(parsed_udt.keyspace, "system"); } else {
panic!("Expected UDT value");
}
}
#[test]
fn test_nested_udt_parsing() {
let mut registry = UdtRegistry::with_cassandra5_defaults();
let company_udt = UdtTypeDef::new("test".to_string(), "company".to_string())
.with_field("name".to_string(), CqlType::Text, false)
.with_field(
"ceo".to_string(),
CqlType::Udt("person".to_string(), vec![]),
true,
)
.with_field(
"headquarters".to_string(),
CqlType::Udt("address".to_string(), vec![]),
true,
);
registry.register_udt(company_udt);
let resolved_company = registry.resolve_udt_with_dependencies("test", "company");
assert!(resolved_company.is_err());
assert!(registry.contains_udt("test", "company"));
}
#[test]
fn test_udt_with_collections() {
let registry = UdtRegistry::with_cassandra5_defaults();
let person_def = registry.get_udt("system", "person").unwrap();
let phone_numbers_field = person_def.get_field("phone_numbers").unwrap();
assert!(matches!(phone_numbers_field.field_type, CqlType::Set(_)));
let addresses_field = person_def.get_field("addresses").unwrap();
assert!(matches!(addresses_field.field_type, CqlType::List(_)));
let metadata_field = person_def.get_field("metadata").unwrap();
assert!(matches!(metadata_field.field_type, CqlType::Map(_, _)));
}
#[test]
fn test_frozen_udt_parsing() {
let registry = UdtRegistry::with_cassandra5_defaults();
let address_def = registry.get_udt("system", "address").unwrap();
let address_udt = create_sample_address_udt();
let serialized = serialize_udt_for_test(&address_udt);
let (remaining, parsed_value) =
parse_frozen_udt_with_registry(&serialized, address_def, ®istry).unwrap();
assert!(remaining.is_empty());
if let Value::Frozen(boxed_value) = parsed_value {
if let Value::Udt(frozen_udt) = boxed_value.as_ref() {
assert_eq!(frozen_udt.type_name, "address");
assert_eq!(frozen_udt.keyspace, "system");
} else {
panic!("Expected UDT inside Frozen wrapper");
}
} else {
panic!("Expected Frozen value");
}
}
#[test]
fn test_udt_validation() {
let registry = UdtRegistry::with_cassandra5_defaults();
let _person_def = registry.get_udt("system", "person").unwrap();
let valid_person = UdtValue {
type_name: "person".to_string(),
keyspace: "system".to_string(),
fields: vec![
UdtField {
name: "id".to_string(),
value: Some(Value::Uuid([
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
])),
},
UdtField {
name: "first_name".to_string(),
value: Some(Value::Text("John".to_string())),
},
UdtField {
name: "last_name".to_string(),
value: Some(Value::Text("Doe".to_string())),
},
],
};
assert_eq!(valid_person.type_name, "person");
assert_eq!(valid_person.keyspace, "system");
}
#[test]
fn test_list_with_udt_elements() {
let _registry = UdtRegistry::with_cassandra5_defaults();
let addresses = vec![
Value::Udt(create_sample_address_udt()),
Value::Udt({
let mut addr = create_sample_address_udt();
addr.set_field(
"street".to_string(),
Some(Value::Text("456 Oak Ave".to_string())),
);
addr.set_field(
"city".to_string(),
Some(Value::Text("Other City".to_string())),
);
addr
}),
];
let list_value = Value::List(addresses);
if let Value::List(list) = list_value {
assert_eq!(list.len(), 2);
for item in list {
if let Value::Udt(udt) = item {
assert_eq!(udt.type_name, "address");
assert_eq!(udt.field_count(), 5);
} else {
panic!("Expected UDT in list");
}
}
}
}
#[test]
fn test_map_with_udt_values() {
let _registry = UdtRegistry::with_cassandra5_defaults();
let people_map = vec![
(
Value::Text("employee1".to_string()),
Value::Udt(create_sample_person_udt()),
),
(
Value::Text("employee2".to_string()),
Value::Udt({
let mut person = create_sample_person_udt();
person.set_field(
"first_name".to_string(),
Some(Value::Text("Jane".to_string())),
);
person.set_field(
"last_name".to_string(),
Some(Value::Text("Smith".to_string())),
);
person
}),
),
];
let map_value = Value::Map(people_map);
if let Value::Map(map) = map_value {
assert_eq!(map.len(), 2);
for (key, value) in map {
assert!(matches!(key, Value::Text(_)));
if let Value::Udt(udt) = value {
assert_eq!(udt.type_name, "person");
} else {
panic!("Expected UDT value in map");
}
}
}
}
#[test]
fn test_empty_value_creation_for_cql_types() {
let empty_text = Value::Text(String::new());
assert_eq!(empty_text, Value::Text(String::new()));
let empty_int = Value::Integer(0);
assert_eq!(empty_int, Value::Integer(0));
let empty_list = Value::List(Vec::new());
assert_eq!(empty_list, Value::List(Vec::new()));
let empty_udt = Value::Udt(UdtValue {
type_name: "test".to_string(),
keyspace: "test_ks".to_string(),
fields: vec![],
});
if let Value::Udt(udt) = &empty_udt {
assert_eq!(udt.type_name, "test");
assert_eq!(udt.fields.len(), 0);
} else {
panic!("Expected UDT value");
}
}
#[test]
fn test_udt_enhanced_parsing_fallback() {
let person_udt = create_sample_person_udt();
let serialized = serialize_udt_for_test(&person_udt);
let (remaining, parsed_value) = parse_udt_enhanced(&serialized).unwrap();
assert!(remaining.is_empty());
if let Value::Udt(parsed_udt) = parsed_value {
assert_eq!(parsed_udt.type_name, "person");
} else {
panic!("Expected UDT value");
}
}
#[test]
fn test_complex_nested_structure() {
let registry = UdtRegistry::with_cassandra5_defaults();
let contact_info_def = registry.get_udt("system", "contact_info").unwrap();
let person_field = contact_info_def.get_field("person").unwrap();
assert!(matches!(person_field.field_type, CqlType::Udt(_, _)));
let address_field = contact_info_def.get_field("primary_address").unwrap();
assert!(matches!(address_field.field_type, CqlType::Udt(_, _)));
let emergency_contacts_field = contact_info_def.get_field("emergency_contacts").unwrap();
assert!(matches!(
emergency_contacts_field.field_type,
CqlType::List(_)
));
}
}