#[cfg(test)]
mod parser_tests {
use crate::{
schema::{
parser::SchemaParser, registry::ParsingContext, ClusteringColumn, ClusteringOrder,
Column, KeyColumn, TableSchema,
},
types::{ComparatorType, Value},
};
use std::collections::HashMap;
use uuid::Uuid;
fn create_test_schema() -> TableSchema {
TableSchema {
keyspace: "test_ks".to_string(),
table: "test_table".to_string(),
partition_keys: vec![KeyColumn {
name: "id".to_string(),
data_type: "int".to_string(),
position: 0,
}],
clustering_keys: vec![ClusteringColumn {
name: "timestamp".to_string(),
data_type: "bigint".to_string(),
position: 0,
order: ClusteringOrder::Asc,
}],
columns: vec![
Column {
name: "id".to_string(),
data_type: "int".to_string(),
nullable: false,
default: None,
is_static: false,
},
Column {
name: "timestamp".to_string(),
data_type: "bigint".to_string(),
nullable: false,
default: None,
is_static: false,
},
Column {
name: "name".to_string(),
data_type: "text".to_string(),
nullable: true,
default: None,
is_static: false,
},
Column {
name: "data".to_string(),
data_type: "blob".to_string(),
nullable: true,
default: None,
is_static: false,
},
Column {
name: "tags".to_string(),
data_type: "list<text>".to_string(),
nullable: true,
default: None,
is_static: false,
},
],
comments: HashMap::new(),
}
}
fn create_test_context() -> ParsingContext {
let schema = create_test_schema();
let mut column_comparators = HashMap::new();
column_comparators.insert("id".to_string(), ComparatorType::Int);
column_comparators.insert("timestamp".to_string(), ComparatorType::BigInt);
column_comparators.insert("name".to_string(), ComparatorType::Text);
column_comparators.insert("data".to_string(), ComparatorType::Blob);
column_comparators.insert(
"tags".to_string(),
ComparatorType::List(Box::new(ComparatorType::Text)),
);
ParsingContext {
schema,
partition_comparators: vec![ComparatorType::Int],
clustering_comparators: vec![ComparatorType::BigInt],
column_comparators,
}
}
#[test]
fn test_schema_parser_creation() {
let context = create_test_context();
let parser = SchemaParser::new(context);
assert!(parser.is_ok());
}
#[test]
fn test_parse_partition_key() {
let context = create_test_context();
let parser = SchemaParser::new(context).unwrap();
let data = vec![0, 0, 0, 42];
let result = parser.parse_partition_key(&data);
assert!(result.is_ok());
let values = result.unwrap();
assert_eq!(values.len(), 1);
assert_eq!(values[0], Value::Integer(42));
}
#[test]
fn test_parse_clustering_keys() {
let context = create_test_context();
let parser = SchemaParser::new(context).unwrap();
let data = vec![0, 0, 0, 0, 73, 150, 2, 210];
let result = parser.parse_clustering_keys(&data);
assert!(result.is_ok());
let values = result.unwrap();
assert_eq!(values.len(), 1);
assert_eq!(values[0], Value::BigInt(1234567890));
}
#[test]
fn test_parse_text_column() {
let context = create_test_context();
let parser = SchemaParser::new(context).unwrap();
let data = vec![0, 0, 0, 5, b'h', b'e', b'l', b'l', b'o'];
let result = parser.parse_column_value("name", &data);
assert!(result.is_ok());
let (value, consumed) = result.unwrap();
assert_eq!(value, Value::Text("hello".to_string()));
assert_eq!(consumed, 9); }
#[test]
fn test_parse_blob_column() {
let context = create_test_context();
let parser = SchemaParser::new(context).unwrap();
let data = vec![0, 0, 0, 3, 1, 2, 3];
let result = parser.parse_column_value("data", &data);
assert!(result.is_ok());
let (value, consumed) = result.unwrap();
assert_eq!(value, Value::Blob(vec![1, 2, 3]));
assert_eq!(consumed, 7); }
#[test]
fn test_parse_list_column() {
let context = create_test_context();
let parser = SchemaParser::new(context).unwrap();
let mut data = vec![0, 0, 0, 2]; data.extend_from_slice(&[0, 0, 0, 5]); data.extend_from_slice(b"hello");
data.extend_from_slice(&[0, 0, 0, 5]); data.extend_from_slice(b"world");
let result = parser.parse_column_value("tags", &data);
assert!(result.is_ok());
let (value, _consumed) = result.unwrap();
match value {
Value::List(elements) => {
assert_eq!(elements.len(), 2);
assert_eq!(elements[0], Value::Text("hello".to_string()));
assert_eq!(elements[1], Value::Text("world".to_string()));
}
_ => panic!("Expected List value"),
}
}
#[test]
fn test_multi_component_partition_key() {
let mut schema = create_test_schema();
schema.partition_keys.push(KeyColumn {
name: "region".to_string(),
data_type: "text".to_string(),
position: 1,
});
let mut column_comparators = HashMap::new();
column_comparators.insert("id".to_string(), ComparatorType::Int);
column_comparators.insert("region".to_string(), ComparatorType::Text);
let context = ParsingContext {
schema,
partition_comparators: vec![ComparatorType::Int, ComparatorType::Text],
clustering_comparators: vec![],
column_comparators,
};
let parser = SchemaParser::new(context).unwrap();
let mut data = vec![0, 0, 0, 42]; data.extend_from_slice(&[0, 0, 0, 2]); data.extend_from_slice(b"US");
let result = parser.parse_partition_key(&data);
assert!(result.is_ok());
let values = result.unwrap();
assert_eq!(values.len(), 2);
assert_eq!(values[0], Value::Integer(42));
assert_eq!(values[1], Value::Text("US".to_string()));
}
#[test]
fn test_parse_uuid_type() {
let mut schema = create_test_schema();
schema.columns.push(Column {
name: "user_id".to_string(),
data_type: "uuid".to_string(),
nullable: false,
default: None,
is_static: false,
});
let mut column_comparators = HashMap::new();
column_comparators.insert("user_id".to_string(), ComparatorType::Uuid);
let context = ParsingContext {
schema,
partition_comparators: vec![],
clustering_comparators: vec![],
column_comparators,
};
let parser = SchemaParser::new(context).unwrap();
let uuid = Uuid::new_v4();
let data = uuid.as_bytes().to_vec();
let result = parser.parse_column_value("user_id", &data);
assert!(result.is_ok());
let (value, consumed) = result.unwrap();
assert!(matches!(value, Value::Uuid(_)));
assert_eq!(consumed, 16); }
#[test]
fn test_parse_nested_collection() {
let mut schema = create_test_schema();
schema.columns.push(Column {
name: "nested".to_string(),
data_type: "map<text,list<int>>".to_string(),
nullable: true,
default: None,
is_static: false,
});
let map_comparator = ComparatorType::Map(
Box::new(ComparatorType::Text),
Box::new(ComparatorType::List(Box::new(ComparatorType::Int))),
);
let mut column_comparators = HashMap::new();
column_comparators.insert("nested".to_string(), map_comparator);
let context = ParsingContext {
schema,
partition_comparators: vec![],
clustering_comparators: vec![],
column_comparators,
};
let parser = SchemaParser::new(context).unwrap();
let mut data = vec![0, 0, 0, 1];
data.extend_from_slice(&[0, 0, 0, 3]); data.extend_from_slice(b"key");
data.extend_from_slice(&[0, 0, 0, 2]); data.extend_from_slice(&[0, 0, 0, 1]); data.extend_from_slice(&[0, 0, 0, 2]);
let result = parser.parse_column_value("nested", &data);
assert!(result.is_ok());
let (_value, _consumed) = result.unwrap();
}
#[test]
fn test_parse_frozen_type() {
let mut schema = create_test_schema();
schema.columns.push(Column {
name: "frozen_set".to_string(),
data_type: "frozen<set<int>>".to_string(),
nullable: true,
default: None,
is_static: false,
});
let frozen_comparator =
ComparatorType::Frozen(Box::new(ComparatorType::Set(Box::new(ComparatorType::Int))));
let mut column_comparators = HashMap::new();
column_comparators.insert("frozen_set".to_string(), frozen_comparator);
let context = ParsingContext {
schema,
partition_comparators: vec![],
clustering_comparators: vec![],
column_comparators,
};
let parser = SchemaParser::new(context).unwrap();
let mut data = vec![0, 0, 0, 3]; data.extend_from_slice(&[0, 0, 0, 10]); data.extend_from_slice(&[0, 0, 0, 20]); data.extend_from_slice(&[0, 0, 0, 30]);
let result = parser.parse_column_value("frozen_set", &data);
assert!(result.is_ok());
let (value, _consumed) = result.unwrap();
assert!(matches!(value, Value::Frozen(_)));
}
#[test]
fn test_error_on_missing_column() {
let context = create_test_context();
let parser = SchemaParser::new(context).unwrap();
let data = vec![0, 0, 0, 42];
let result = parser.parse_column_value("nonexistent", &data);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("not found in schema"));
}
#[test]
fn test_error_on_insufficient_data() {
let context = create_test_context();
let parser = SchemaParser::new(context).unwrap();
let data = vec![0, 0];
let result = parser.parse_column_value("id", &data);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("Insufficient data"));
}
#[test]
fn test_incomplete_context_rejected() {
let context = ParsingContext {
schema: TableSchema {
keyspace: "test".to_string(),
table: "test".to_string(),
partition_keys: vec![],
clustering_keys: vec![],
columns: vec![],
comments: HashMap::new(),
},
partition_comparators: vec![],
clustering_comparators: vec![],
column_comparators: HashMap::new(),
};
let result = SchemaParser::new(context);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Incomplete parsing context"));
}
#[test]
fn test_multi_component_partition_and_clustering_keys() {
let schema = TableSchema {
keyspace: "test_ks".to_string(),
table: "multi_key_table".to_string(),
partition_keys: vec![
KeyColumn {
name: "region".to_string(),
data_type: "text".to_string(),
position: 0,
},
KeyColumn {
name: "bucket".to_string(),
data_type: "int".to_string(),
position: 1,
},
],
clustering_keys: vec![
ClusteringColumn {
name: "timestamp".to_string(),
data_type: "timestamp".to_string(),
position: 0,
order: ClusteringOrder::Asc,
},
ClusteringColumn {
name: "sequence".to_string(),
data_type: "bigint".to_string(),
position: 1,
order: ClusteringOrder::Asc,
},
],
columns: vec![
Column {
name: "region".to_string(),
data_type: "text".to_string(),
nullable: false,
default: None,
is_static: false,
},
Column {
name: "bucket".to_string(),
data_type: "int".to_string(),
nullable: false,
default: None,
is_static: false,
},
Column {
name: "timestamp".to_string(),
data_type: "timestamp".to_string(),
nullable: false,
default: None,
is_static: false,
},
Column {
name: "sequence".to_string(),
data_type: "bigint".to_string(),
nullable: false,
default: None,
is_static: false,
},
Column {
name: "value".to_string(),
data_type: "double".to_string(),
nullable: true,
default: None,
is_static: false,
},
],
comments: HashMap::new(),
};
let mut column_comparators = HashMap::new();
column_comparators.insert("region".to_string(), ComparatorType::Text);
column_comparators.insert("bucket".to_string(), ComparatorType::Int);
column_comparators.insert("timestamp".to_string(), ComparatorType::Timestamp);
column_comparators.insert("sequence".to_string(), ComparatorType::BigInt);
column_comparators.insert("value".to_string(), ComparatorType::Float);
let context = ParsingContext {
schema,
partition_comparators: vec![ComparatorType::Text, ComparatorType::Int],
clustering_comparators: vec![ComparatorType::Timestamp, ComparatorType::BigInt],
column_comparators,
};
let parser = SchemaParser::new(context).unwrap();
let mut partition_data = vec![0, 0, 0, 7]; partition_data.extend_from_slice(b"US-WEST");
partition_data.extend_from_slice(&[0, 0, 0, 42]);
let result = parser.parse_partition_key(&partition_data);
assert!(result.is_ok());
let values = result.unwrap();
assert_eq!(values.len(), 2);
assert_eq!(values[0], Value::Text("US-WEST".to_string()));
assert_eq!(values[1], Value::Integer(42));
let mut clustering_data = vec![0, 0, 1, 126, 45, 67, 89, 0]; clustering_data.extend_from_slice(&[0, 0, 0, 0, 7, 91, 205, 21]);
let result = parser.parse_clustering_keys(&clustering_data);
assert!(result.is_ok());
let values = result.unwrap();
assert_eq!(values.len(), 2);
assert!(matches!(values[0], Value::Timestamp(_)));
assert_eq!(values[1], Value::BigInt(123456789));
}
#[test]
fn test_nested_udt_in_frozen_collection() {
let mut schema = create_test_schema();
schema.columns.push(Column {
name: "user_profiles".to_string(),
data_type: "frozen<list<frozen<user_profile>>>".to_string(),
nullable: true,
default: None,
is_static: false,
});
let udt_comparator = ComparatorType::Udt {
type_name: "user_profile".to_string(),
keyspace: Some("test_ks".to_string()),
field_comparators: vec![
("name".to_string(), ComparatorType::Text),
("age".to_string(), ComparatorType::Int),
("email".to_string(), ComparatorType::Text),
],
};
let frozen_udt_comparator = ComparatorType::Frozen(Box::new(udt_comparator));
let list_comparator = ComparatorType::List(Box::new(frozen_udt_comparator));
let frozen_list_comparator = ComparatorType::Frozen(Box::new(list_comparator));
let mut column_comparators = HashMap::new();
column_comparators.insert("id".to_string(), ComparatorType::Int);
column_comparators.insert("timestamp".to_string(), ComparatorType::BigInt);
column_comparators.insert("name".to_string(), ComparatorType::Text);
column_comparators.insert("data".to_string(), ComparatorType::Blob);
column_comparators.insert(
"tags".to_string(),
ComparatorType::List(Box::new(ComparatorType::Text)),
);
column_comparators.insert("user_profiles".to_string(), frozen_list_comparator);
let context = ParsingContext {
schema,
partition_comparators: vec![ComparatorType::Int],
clustering_comparators: vec![ComparatorType::BigInt],
column_comparators,
};
let parser = SchemaParser::new(context).unwrap();
let mut data = vec![0, 0, 0, 1];
data.extend_from_slice(&[0, 0, 0, 4]); data.extend_from_slice(b"John");
data.extend_from_slice(&[0, 0, 0, 30]);
data.extend_from_slice(&[0, 0, 0, 16]); data.extend_from_slice(b"john@example.com");
let result = parser.parse_column_value("user_profiles", &data);
if let Err(e) = &result {
eprintln!("Error parsing UDT: {:?}", e);
}
assert!(result.is_ok());
let (value, _consumed) = result.unwrap();
assert!(matches!(value, Value::Frozen(_)));
if let Value::Frozen(inner) = value {
assert!(matches!(*inner, Value::List(_)));
}
}
#[test]
fn test_ordering_equivalence_byte_comparable_vs_typed() {
let context = create_test_context();
let parser = SchemaParser::new(context).unwrap();
let values = ["apple", "banana", "cherry", "date"];
let mut serialized_values = Vec::new();
for value_str in &values {
let mut data = vec![0, 0, 0, value_str.len() as u8]; data.extend_from_slice(value_str.as_bytes());
serialized_values.push(data);
}
let mut parsed_values = Vec::new();
for data in &serialized_values {
let result = parser.parse_column_value("name", data);
assert!(result.is_ok());
let (value, _consumed) = result.unwrap();
parsed_values.push(value);
}
for i in 0..parsed_values.len() - 1 {
let current = &parsed_values[i];
let next = &parsed_values[i + 1];
let comparator = ComparatorType::Text;
let comparison = comparator.compare(current, next).unwrap();
assert_eq!(
comparison,
std::cmp::Ordering::Less,
"Value '{}' should be less than '{}'",
values[i],
values[i + 1]
);
}
let int_values = [10, 20, 30, 40];
let mut int_data_values = Vec::new();
for &value in &int_values {
let data = vec![
(value >> 24) as u8,
(value >> 16) as u8,
(value >> 8) as u8,
value as u8,
];
int_data_values.push(data);
}
let mut parsed_int_values = Vec::new();
for data in &int_data_values {
let result = parser.parse_column_value("id", data);
assert!(result.is_ok());
let (value, _consumed) = result.unwrap();
parsed_int_values.push(value);
}
for i in 0..parsed_int_values.len() - 1 {
let current = &parsed_int_values[i];
let next = &parsed_int_values[i + 1];
let comparator = ComparatorType::Int;
let comparison = comparator.compare(current, next).unwrap();
assert_eq!(
comparison,
std::cmp::Ordering::Less,
"Value {} should be less than {}",
int_values[i],
int_values[i + 1]
);
}
for i in 0..int_data_values.len() - 1 {
let current_bytes = &int_data_values[i];
let next_bytes = &int_data_values[i + 1];
assert!(
current_bytes < next_bytes,
"Byte ordering should match typed ordering for integers"
);
}
}
}