use super::*; use crate::schema::ast::{EdgeDecl, NodeDecl}; use crate::schema::parser::parse_schema; use crate::types::PropType; fn test_schema() -> &'static str { r#" node Person { name: String age: I32? } node Company { name: String } edge Knows: Person -> Person { since: Date? } edge WorksAt: Person -> Company { title: String? } "# } #[test] fn test_build_catalog() { let schema = parse_schema(test_schema()).unwrap(); let catalog = build_catalog(&schema).unwrap(); assert_eq!(catalog.node_types.len(), 2); assert_eq!(catalog.edge_types.len(), 2); assert!(catalog.node_types.contains_key("Person")); assert!(catalog.node_types.contains_key("Company")); } #[test] fn test_edge_lookup() { let schema = parse_schema(test_schema()).unwrap(); let catalog = build_catalog(&schema).unwrap(); let edge = catalog.lookup_edge_by_name("knows").unwrap(); assert_eq!(edge.from_type, "Person"); assert_eq!(edge.to_type, "Person"); let upper = catalog.lookup_edge_by_name("KNOWS").unwrap(); assert_eq!(upper.name, "Knows"); } #[test] fn test_node_arrow_schema() { let schema = parse_schema(test_schema()).unwrap(); let catalog = build_catalog(&schema).unwrap(); let person = &catalog.node_types["Person"]; assert_eq!(person.arrow_schema.fields().len(), 3); // id, name, age } #[test] fn test_duplicate_node_error() { let input = r#" node Person { name: String } node Person { age: I32 } "#; let schema = parse_schema(input).unwrap(); assert!(build_catalog(&schema).is_err()); } #[test] fn test_bad_edge_endpoint() { let input = r#" node Person { name: String } edge Knows: Person -> Alien "#; let schema = parse_schema(input).unwrap(); assert!(build_catalog(&schema).is_err()); } #[test] fn test_id_fields_are_utf8() { let schema = parse_schema(test_schema()).unwrap(); let catalog = build_catalog(&schema).unwrap(); let person = &catalog.node_types["Person"]; assert_eq!( person .arrow_schema .field_with_name("id") .unwrap() .data_type(), &DataType::Utf8 ); let knows = &catalog.edge_types["Knows"]; assert_eq!( knows .arrow_schema .field_with_name("id") .unwrap() .data_type(), &DataType::Utf8 ); assert_eq!( knows .arrow_schema .field_with_name("src") .unwrap() .data_type(), &DataType::Utf8 ); assert_eq!( knows .arrow_schema .field_with_name("dst") .unwrap() .data_type(), &DataType::Utf8 ); } #[test] fn test_key_property_tracking() { let input = r#" node Signal { slug: String @key title: String } node Person { name: String } edge Emits: Person -> Signal "#; let schema = parse_schema(input).unwrap(); let catalog = build_catalog(&schema).unwrap(); assert_eq!(catalog.node_types["Signal"].key_property(), Some("slug")); assert_eq!(catalog.node_types["Person"].key_property(), None); } #[test] fn test_edge_lookup_handles_non_ascii_leading_character() { let schema = SchemaFile { declarations: vec![ SchemaDecl::Node(NodeDecl { name: "Person".to_string(), annotations: vec![], implements: vec![], properties: vec![crate::schema::ast::PropDecl { name: "name".to_string(), prop_type: PropType::scalar(ScalarType::String, false), annotations: vec![], }], constraints: vec![], }), SchemaDecl::Edge(EdgeDecl { name: "Édges".to_string(), from_type: "Person".to_string(), to_type: "Person".to_string(), cardinality: Default::default(), annotations: vec![], properties: vec![], constraints: vec![], }), ], }; let catalog = build_catalog(&schema).unwrap(); assert!(catalog.lookup_edge_by_name("édges").is_some()); } #[test] fn test_edge_lookup_rejects_case_fold_collisions() { let input = r#" node Person { name: String } edge Knows: Person -> Person edge KNOWS: Person -> Person "#; let schema = parse_schema(input).unwrap(); let err = build_catalog(&schema).unwrap_err(); assert!(err.to_string().contains("case folding")); } #[test] fn test_catalog_composite_unique() { let input = r#" node Person { first: String last: String @unique(first, last) } "#; let schema = parse_schema(input).unwrap(); let catalog = build_catalog(&schema).unwrap(); let person = &catalog.node_types["Person"]; assert!( person .unique_constraints .contains(&vec!["first".to_string(), "last".to_string()]) ); } #[test] fn test_catalog_composite_index() { let input = r#" node Event { category: String date: Date @index(category, date) } "#; let schema = parse_schema(input).unwrap(); let catalog = build_catalog(&schema).unwrap(); let event = &catalog.node_types["Event"]; assert!( event .indices .contains(&vec!["category".to_string(), "date".to_string()]) ); } #[test] fn test_catalog_edge_cardinality() { let input = r#" node Person { name: String } node Company { name: String } edge WorksAt: Person -> Company @card(0..1) "#; let schema = parse_schema(input).unwrap(); let catalog = build_catalog(&schema).unwrap(); let edge = &catalog.edge_types["WorksAt"]; assert_eq!(edge.cardinality.min, 0); assert_eq!(edge.cardinality.max, Some(1)); } #[test] fn test_catalog_interfaces_stored() { let input = r#" interface Named { name: String } node Person implements Named { age: I32? } "#; let schema = parse_schema(input).unwrap(); let catalog = build_catalog(&schema).unwrap(); assert!(catalog.interfaces.contains_key("Named")); assert!(catalog.interfaces["Named"].properties.contains_key("name")); } #[test] fn test_catalog_node_implements() { let input = r#" interface Named { name: String } node Person implements Named { age: I32? } "#; let schema = parse_schema(input).unwrap(); let catalog = build_catalog(&schema).unwrap(); assert_eq!(catalog.node_types["Person"].implements, vec!["Named"]); } #[test] fn test_key_implies_index() { let input = r#" node Signal { slug: String @key title: String } "#; let schema = parse_schema(input).unwrap(); let catalog = build_catalog(&schema).unwrap(); let signal = &catalog.node_types["Signal"]; assert!(signal.indices.contains(&vec!["slug".to_string()])); }