//! Collection catalog value-object tests. use context_core::{ CollectionName, Error, ProfileName, QualifiedTableName, SourceKey, SqlIdentifier, VectorDimensions, VectorName, }; #[test] fn collection_name_accepts_ascii_identifier_names() { assert_eq!( CollectionName::new("_tenant_01").map(CollectionName::into_string), Ok("_tenant_01".to_owned()) ); } #[test] fn collection_name_rejects_empty_names() { assert_invalid_collection_name("", "must not be empty"); } #[test] fn collection_name_rejects_names_that_start_with_digits() { assert_invalid_collection_name("1tenant", "must start with an ASCII letter or underscore"); } #[test] fn collection_name_rejects_names_with_unsupported_characters() { assert_invalid_collection_name( "tenant-events", "must contain only ASCII letters, digits, and underscores", ); } #[test] fn collection_name_rejects_names_longer_than_postgres_identifier_limit() { let name = "a".repeat(64); assert_invalid_collection_name(&name, "exceeds 63 bytes"); } #[test] fn vector_name_accepts_sql_identifier_names() { assert_eq!( VectorName::new("embedding_v1").map(|name| name.to_string()), Ok("embedding_v1".to_owned()) ); } #[test] fn sql_identifier_rejects_quoted_or_dotted_names() { assert_eq!( SqlIdentifier::new("docs.embedding"), Err(Error::InvalidIdentifier { kind: "SQL identifier", value: "docs.embedding".to_owned(), reason: "must contain only ASCII letters, digits, and underscores", }) ); } #[test] fn qualified_table_name_accepts_schema_table_form() { let table = QualifiedTableName::new("public.documents") .map(|table| (table.schema().to_string(), table.table().to_string())); assert_eq!(table, Ok(("public".to_owned(), "documents".to_owned()))); } #[test] fn qualified_table_name_rejects_unqualified_names() { assert_eq!( QualifiedTableName::new("documents"), Err(Error::InvalidIdentifier { kind: "qualified table name", value: "documents".to_owned(), reason: "must use schema.table form", }) ); } #[test] fn vector_dimensions_rejects_zero_and_values_above_policy_limit() { assert_eq!( VectorDimensions::new(0), Err(Error::InvalidVectorDimensions(0)) ); assert_eq!( VectorDimensions::new(16_001), Err(Error::InvalidVectorDimensions(16_001)) ); assert_eq!( VectorDimensions::new(1536).map(VectorDimensions::get), Ok(1536) ); } #[test] fn source_key_accepts_non_empty_keys() { assert_eq!( SourceKey::new("tenant-1/doc-42").map(SourceKey::into_string), Ok("tenant-1/doc-42".to_owned()) ); } #[test] fn source_key_rejects_empty_and_oversized_keys() { assert_eq!( SourceKey::new(""), Err(Error::InvalidSourceKey(String::new())) ); let oversized = "x".repeat(1025); assert_eq!( SourceKey::new(oversized.clone()), Err(Error::InvalidSourceKey(oversized)) ); } #[test] fn profile_name_uses_one_shared_bounded_control_free_identity() { assert_eq!( ProfileName::new("legacy v1").map(|name| name.as_str().to_owned()), Ok("legacy v1".to_owned()) ); for (name, reason) in [ (" ".to_owned(), "must not be blank"), ( "bad\nname".to_owned(), "must not contain control characters", ), ("x".repeat(129), "exceeds 128 bytes"), (" ".repeat(129), "exceeds 128 bytes"), ] { assert_eq!(ProfileName::validate(&name), Err(reason)); assert_eq!( ProfileName::new(name.clone()), Err(Error::InvalidIdentifier { kind: "profile name", value: name, reason, }) ); } assert_eq!(ProfileName::validate("legacy v1"), Ok(())); } fn assert_invalid_collection_name(name: &str, expected_reason: &'static str) { assert_eq!( CollectionName::new(name), Err(Error::InvalidIdentifier { kind: "collection name", value: name.to_owned(), reason: expected_reason, }) ); }