#![allow(unused_imports)]
pub use crate::utils::operators::{OPERATOR_REGISTRY, get_operators_by_category};
mod casing_tests {
use super::super::*;
#[allow(unused_imports)]
use super::*;
#[test]
fn test_simple_camel_to_snake() {
assert_eq!(to_snake_case("userId"), "user_id");
assert_eq!(to_snake_case("userName"), "user_name");
assert_eq!(to_snake_case("firstName"), "first_name");
}
#[test]
fn test_pascal_to_snake() {
assert_eq!(to_snake_case("UserId"), "user_id");
assert_eq!(to_snake_case("FirstName"), "first_name");
}
#[test]
fn test_consecutive_capitals() {
assert_eq!(to_snake_case("HTTPResponse"), "http_response");
assert_eq!(to_snake_case("XMLParser"), "xml_parser");
assert_eq!(to_snake_case("IOError"), "io_error");
}
#[test]
fn test_already_snake_case() {
assert_eq!(to_snake_case("user_id"), "user_id");
assert_eq!(to_snake_case("first_name"), "first_name");
assert_eq!(to_snake_case("http_response"), "http_response");
}
#[test]
fn test_mixed_formats() {
assert_eq!(to_snake_case("user_Id"), "user_id"); assert_eq!(to_snake_case("HTTPStatus_Code"), "http_status_code");
}
#[test]
fn test_single_char() {
assert_eq!(to_snake_case("a"), "a");
assert_eq!(to_snake_case("A"), "a");
}
#[test]
fn test_empty_string() {
assert_eq!(to_snake_case(""), "");
}
#[test]
fn test_numbers() {
assert_eq!(to_snake_case("phone1"), "phone_1");
assert_eq!(to_snake_case("dns1Id"), "dns_1_id");
assert_eq!(to_snake_case("user2FA"), "user_2_fa");
assert_eq!(to_snake_case("level99Boss"), "level_99_boss");
}
#[test]
fn test_simple_snake_to_camel() {
assert_eq!(to_camel_case("user_id"), "userId");
assert_eq!(to_camel_case("first_name"), "firstName");
assert_eq!(to_camel_case("http_response"), "httpResponse");
}
#[test]
fn test_already_camel_case() {
assert_eq!(to_camel_case("userId"), "userId");
assert_eq!(to_camel_case("firstName"), "firstName");
}
#[test]
fn test_camel_case_collapses_digits() {
assert_eq!(to_camel_case("phone_1"), "phone1");
assert_eq!(to_camel_case("phone_2"), "phone2");
assert_eq!(to_camel_case("dns_1_id"), "dns1Id");
assert_eq!(to_camel_case("line_2_content"), "line2Content");
}
#[test]
fn test_snake_camel_round_trip() {
for snake in [
"id",
"user_id",
"first_name",
"created_at",
"http_response",
"phone_1",
"phone_2",
"address_2",
"dns_1_id",
"line_2_content",
] {
assert_eq!(
to_snake_case(&to_camel_case(snake)),
snake,
"round trip failed for {snake}"
);
}
}
#[test]
fn test_multiple_underscores() {
assert_eq!(to_camel_case("user__id"), "userId");
assert_eq!(to_camel_case("http___response"), "httpResponse");
}
#[test]
fn test_trailing_underscore() {
assert_eq!(to_camel_case("user_id_"), "userId");
assert_eq!(to_camel_case("first_name_"), "firstName");
}
#[test]
fn test_normalize_field_path_simple() {
assert_eq!(normalize_field_path("userId"), "user_id");
assert_eq!(normalize_field_path("createdAt"), "created_at");
}
#[test]
fn test_normalize_field_path_nested() {
assert_eq!(normalize_field_path("user.createdAt"), "user.created_at");
assert_eq!(
normalize_field_path("device.sensorData.currentValue"),
"device.sensor_data.current_value"
);
}
#[test]
fn test_normalize_field_path_already_snake() {
assert_eq!(normalize_field_path("user_id"), "user_id");
assert_eq!(normalize_field_path("user.created_at"), "user.created_at");
}
#[test]
fn test_roundtrip_conversion() {
let original = "userId";
let snake = to_snake_case(original);
let back = to_camel_case(&snake);
assert_eq!(back, original);
let original2 = "HTTPResponse";
let snake2 = to_snake_case(original2);
assert_eq!(snake2, "http_response");
let back2 = to_camel_case(&snake2);
assert_eq!(back2, "httpResponse"); }
#[test]
fn test_real_world_examples() {
assert_eq!(to_snake_case("createdAt"), "created_at");
assert_eq!(to_snake_case("updatedAt"), "updated_at");
assert_eq!(to_snake_case("deletedAt"), "deleted_at");
assert_eq!(to_snake_case("isActive"), "is_active");
assert_eq!(to_snake_case("isDeleted"), "is_deleted");
assert_eq!(to_snake_case("machineId"), "machine_id");
assert_eq!(to_snake_case("deviceType"), "device_type");
}
}
mod opaque_id_tests {
use super::super::*;
#[allow(unused_imports)]
use super::*;
#[test]
fn test_opaque_id_creation() {
let opaque = OpaqueId::new("12345");
assert!(!opaque.as_str().is_empty());
assert!(!opaque.as_str().contains("12345"));
}
#[test]
fn test_opaque_id_decode() {
let db_id = "user_42";
let opaque = OpaqueId::new(db_id);
let decoded = opaque.decode();
assert_eq!(decoded, Some(db_id.to_string()));
}
#[test]
fn test_opaque_id_with_signature() {
let db_id = "12345";
let secret = b"secret_key";
let opaque = OpaqueId::with_signature(db_id, secret);
assert!(opaque.verify_signature(secret));
assert!(!opaque.verify_signature(b"wrong_secret"));
}
#[test]
fn test_opaque_id_signature_tampering() {
let db_id = "sensitive_id_789";
let secret = b"super_secret";
let mut opaque = OpaqueId::with_signature(db_id, secret);
assert!(opaque.verify_signature(secret));
opaque.id = opaque.id.chars().rev().collect();
assert!(!opaque.verify_signature(secret));
}
#[test]
fn test_opaque_id_equality() {
let opaque1 = OpaqueId::new("same_id");
let opaque2 = OpaqueId::new("same_id");
assert_eq!(opaque1, opaque2);
let opaque3 = OpaqueId::new("different_id");
assert_ne!(opaque1, opaque3);
}
#[test]
fn test_opaque_id_prevents_enumeration() {
let ids: Vec<String> = (1..=5).map(|i| i.to_string()).collect();
let opaque_ids: Vec<OpaqueId> = ids.iter().map(OpaqueId::new).collect();
for i in 1..opaque_ids.len() {
assert_ne!(opaque_ids[i].as_str(), opaque_ids[i - 1].as_str());
}
for i in 0..opaque_ids.len() {
let original = ids[i].as_str();
let opaque = opaque_ids[i].as_str();
assert!(!opaque.contains(original));
}
}
}
mod operators_tests {
#![allow(clippy::unwrap_used)]
use super::{super::*, *};
#[test]
fn test_operator_registry_initialized() {
assert!(OPERATOR_REGISTRY.len() >= 40);
}
#[test]
fn test_comparison_operators() {
let operators = ["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"];
for op_name in &operators {
let op = get_operator_info(op_name);
assert!(op.is_some(), "Operator {op_name} should exist");
let op = op.unwrap();
assert_eq!(op.category, OperatorCategory::Comparison);
assert!(!op.jsonb_operator);
}
}
#[test]
fn test_string_operators() {
let operators = [
"like", "ilike", "nlike", "nilike", "regex", "iregex", "nregex", "niregex",
];
for op_name in &operators {
let op = get_operator_info(op_name);
assert!(op.is_some(), "String operator {op_name} should exist");
let op = op.unwrap();
assert_eq!(op.category, OperatorCategory::String);
}
}
#[test]
fn test_null_operators() {
let op1 = get_operator_info("is_null").unwrap();
assert_eq!(op1.sql_op, "IS NULL");
assert_eq!(op1.category, OperatorCategory::Null);
let op2 = get_operator_info("is_not_null").unwrap();
assert_eq!(op2.sql_op, "IS NOT NULL");
assert_eq!(op2.category, OperatorCategory::Null);
}
#[test]
fn test_containment_operators() {
let contains = get_operator_info("contains").expect("contains is registered");
assert_eq!(contains.category, OperatorCategory::String);
assert_eq!(contains.sql_op, "LIKE");
let strict = get_operator_info("strictly_contains").expect("strictly_contains");
assert_eq!(strict.category, OperatorCategory::Containment);
assert!(strict.jsonb_operator);
for alias in ["array_contained_by", "array_contained_in", "contained_in"] {
let op = get_operator_info(alias);
assert!(op.is_some(), "{alias} is registered");
assert_eq!(
op.map(|o| o.sql_op),
Some("<@"),
"{alias} must be the containment operator"
);
}
}
#[test]
fn test_array_operators() {
let operators = ["array_contains", "array_contained_in", "array_overlaps"];
for op_name in &operators {
let op = get_operator_info(op_name);
assert!(op.is_some(), "Array operator {op_name} should exist");
let op = op.unwrap();
assert_eq!(op.category, OperatorCategory::Array);
}
}
#[test]
fn test_vector_operators() {
let operators = [
("cosine_distance", "<=>"),
("l2_distance", "<->"),
("inner_product", "<#>"),
("l1_distance", "<+>"),
("hamming_distance", "<~>"),
("jaccard_distance", "<%>"),
];
for (op_name, expected_sql) in &operators {
let op = get_operator_info(op_name);
assert!(op.is_some(), "Vector operator {op_name} should exist");
let op = op.unwrap();
assert_eq!(op.category, OperatorCategory::Vector);
assert_eq!(op.sql_op, *expected_sql);
}
}
#[test]
fn test_fulltext_operators() {
let operators = [
"search",
"plainto_tsquery",
"phraseto_tsquery",
"websearch_to_tsquery",
];
for op_name in &operators {
let op = get_operator_info(op_name);
assert!(op.is_some(), "Fulltext operator {op_name} should exist");
let op = op.unwrap();
assert_eq!(op.category, OperatorCategory::Fulltext);
assert_eq!(op.sql_op, "@@");
}
}
#[test]
fn test_is_operator() {
assert!(is_operator("eq"));
assert!(is_operator("contains"));
assert!(is_operator("cosine_distance"));
assert!(!is_operator("invalid_operator"));
assert!(!is_operator(""));
}
#[test]
fn test_get_operators_by_category() {
let comparison_ops = get_operators_by_category(OperatorCategory::Comparison);
assert!(comparison_ops.len() >= 8);
let vector_ops = get_operators_by_category(OperatorCategory::Vector);
assert!(vector_ops.len() >= 6);
let fulltext_ops = get_operators_by_category(OperatorCategory::Fulltext);
assert!(fulltext_ops.len() >= 4);
}
#[test]
fn test_requires_array_flag() {
assert!(get_operator_info("in").unwrap().requires_array);
assert!(get_operator_info("nin").unwrap().requires_array);
assert!(!get_operator_info("eq").unwrap().requires_array);
assert!(!get_operator_info("like").unwrap().requires_array);
}
#[test]
fn test_jsonb_operator_flag() {
assert!(get_operator_info("strictly_contains").unwrap().jsonb_operator);
assert!(get_operator_info("len_eq").unwrap().jsonb_operator);
assert!(!get_operator_info("eq").unwrap().jsonb_operator);
assert!(!get_operator_info("like").unwrap().jsonb_operator);
assert!(!get_operator_info("contains").unwrap().jsonb_operator);
}
}