use datafold::schema::types::{
DeclarativeSchemaDefinition, JsonTopology, KeyConfig, PrimitiveType, SchemaType, TopologyNode,
};
use serde_json::json;
use std::collections::HashMap;
#[test]
fn test_topology_validation_rejects_invalid_type() {
let mut schema = DeclarativeSchemaDefinition::new(
"TestSchema".to_string(),
SchemaType::Range,
Some(KeyConfig {
hash_field: None,
range_field: Some("id".to_string()),
}),
Some(vec!["id".to_string(), "name".to_string()]),
None,
None,
);
schema.set_field_topology(
"name".to_string(),
JsonTopology::new(TopologyNode::Primitive {
value: PrimitiveType::String,
classifications: None,
}),
);
let result = schema.validate_field_value("name", &json!(42));
assert!(result.is_err(), "Expected validation error");
let err_msg = result.unwrap_err().to_string();
assert!(
err_msg.contains("Topology validation failed") || err_msg.contains("expected"),
"Error message should mention topology validation: {}",
err_msg
);
}
#[test]
fn test_topology_validation_accepts_valid_type() {
let mut schema = DeclarativeSchemaDefinition::new(
"TestSchema".to_string(),
SchemaType::Range,
Some(KeyConfig {
hash_field: None,
range_field: Some("id".to_string()),
}),
Some(vec!["id".to_string(), "name".to_string()]),
None,
None,
);
schema.set_field_topology(
"name".to_string(),
JsonTopology::new(TopologyNode::Primitive {
value: PrimitiveType::String,
classifications: None,
}),
);
let result = schema.validate_field_value("name", &json!("Alice"));
assert!(
result.is_ok(),
"Expected successful validation: {:?}",
result
);
}
#[test]
fn test_topology_nested_object_validation() {
let mut schema = DeclarativeSchemaDefinition::new(
"TestSchema".to_string(),
SchemaType::Range,
Some(KeyConfig {
hash_field: None,
range_field: Some("id".to_string()),
}),
Some(vec!["id".to_string(), "user".to_string()]),
None,
None,
);
let mut user_fields = HashMap::new();
user_fields.insert(
"id".to_string(),
TopologyNode::Primitive {
value: PrimitiveType::Number,
classifications: None,
},
);
user_fields.insert(
"name".to_string(),
TopologyNode::Primitive {
value: PrimitiveType::String,
classifications: None,
},
);
schema.set_field_topology(
"user".to_string(),
JsonTopology::new(TopologyNode::Object { value: user_fields }),
);
let result = schema.validate_field_value("user", &json!({"id": 1, "name": "Alice"}));
assert!(
result.is_ok(),
"Expected successful validation with valid nested object"
);
let result = schema.validate_field_value("user", &json!({"id": "not_a_number", "name": "Bob"}));
assert!(
result.is_err(),
"Expected validation error for invalid nested field"
);
}
#[test]
fn test_topology_array_validation() {
let mut schema = DeclarativeSchemaDefinition::new(
"TestSchema".to_string(),
SchemaType::Range,
Some(KeyConfig {
hash_field: None,
range_field: Some("id".to_string()),
}),
Some(vec!["id".to_string(), "tags".to_string()]),
None,
None,
);
schema.set_field_topology(
"tags".to_string(),
JsonTopology::new(TopologyNode::Array {
value: Box::new(TopologyNode::Primitive {
value: PrimitiveType::String,
classifications: None,
}),
}),
);
let result = schema.validate_field_value("tags", &json!(["rust", "database"]));
assert!(
result.is_ok(),
"Expected successful validation with valid array"
);
let result = schema.validate_field_value("tags", &json!(["rust", 42, "database"]));
assert!(
result.is_err(),
"Expected validation error for invalid array element"
);
}
#[test]
fn test_topology_inference_from_data() {
let sample_data = json!({
"name": "Alice",
"age": 30,
"active": true,
"tags": ["rust", "database"]
});
let topology = JsonTopology::infer_from_value(&sample_data);
assert!(topology.validate(&sample_data).is_ok());
let similar_data = json!({
"name": "Bob",
"age": 25,
"active": false,
"tags": ["python"]
});
assert!(topology.validate(&similar_data).is_ok());
let invalid_data = json!({
"name": "Charlie",
"age": "thirty", "active": false
});
assert!(topology.validate(&invalid_data).is_err());
}
#[test]
fn test_schema_serialization_includes_topology() {
let mut schema = DeclarativeSchemaDefinition::new(
"TestSchema".to_string(),
SchemaType::Single,
None,
Some(vec!["name".to_string(), "age".to_string()]),
None,
None,
);
schema.set_field_topology(
"name".to_string(),
JsonTopology::new(TopologyNode::Primitive {
value: PrimitiveType::String,
classifications: None,
}),
);
schema.set_field_topology(
"age".to_string(),
JsonTopology::new(TopologyNode::Primitive {
value: PrimitiveType::Number,
classifications: None,
}),
);
let serialized = serde_json::to_string(&schema).expect("Failed to serialize");
let deserialized: DeclarativeSchemaDefinition =
serde_json::from_str(&serialized).expect("Failed to deserialize");
assert!(deserialized.field_topologies.contains_key("name"));
assert!(deserialized.field_topologies.contains_key("age"));
let name_topology = deserialized.field_topologies.get("name").unwrap();
assert_eq!(
name_topology.root,
TopologyNode::Primitive {
value: PrimitiveType::String,
classifications: None
}
);
let age_topology = deserialized.field_topologies.get("age").unwrap();
assert_eq!(
age_topology.root,
TopologyNode::Primitive {
value: PrimitiveType::Number,
classifications: None
}
);
}
#[test]
fn test_missing_topology_fails_validation() {
let schema = DeclarativeSchemaDefinition::new(
"TestSchema".to_string(),
SchemaType::Range,
Some(KeyConfig {
hash_field: None,
range_field: Some("id".to_string()),
}),
Some(vec!["id".to_string(), "data".to_string()]),
None,
None,
);
let result = schema.validate_field_value("data", &json!("any value"));
assert!(
result.is_err(),
"Expected validation to fail without topology"
);
let err_msg = result.unwrap_err().to_string();
assert!(
err_msg.contains("No topology defined for field"),
"Error message should mention missing topology: {}",
err_msg
);
}
#[test]
fn test_any_topology_allows_any_value() {
let mut schema = DeclarativeSchemaDefinition::new(
"TestSchema".to_string(),
SchemaType::Range,
Some(KeyConfig {
hash_field: None,
range_field: Some("id".to_string()),
}),
Some(vec!["id".to_string(), "data".to_string()]),
None,
None,
);
schema.set_field_topology("data".to_string(), JsonTopology::new(TopologyNode::Any));
let test_cases = vec![
json!("string"),
json!(42),
json!(true),
json!({"nested": "object"}),
json!(["array", "of", "values"]),
];
for (idx, data) in test_cases.into_iter().enumerate() {
let result = schema.validate_field_value("data", &data);
assert!(
result.is_ok(),
"Expected validation to succeed with Any topology (test case {}): {:?}",
idx,
result
);
}
}