use sz_rust_core::relation::{
belongs_to, belongs_to_many, has_many, has_one, morph, n_plus_one, with,
};
#[test]
fn test_parity_str_snake_simple_class() {
assert_eq!(has_many::class_to_snake_case("User"), "user");
assert_eq!(has_many::class_to_snake_case("Order"), "order");
assert_eq!(has_many::class_to_snake_case("Customer"), "customer");
}
#[test]
fn test_parity_str_snake_multi_word_class() {
assert_eq!(has_many::class_to_snake_case("OrderItem"), "order_item");
assert_eq!(has_many::class_to_snake_case("UserRole"), "user_role");
assert_eq!(
has_many::class_to_snake_case("ContractDetail"),
"contract_detail"
);
}
#[test]
fn test_parity_str_snake_all_lowercase() {
assert_eq!(has_many::class_to_snake_case("user"), "user");
assert_eq!(has_many::class_to_snake_case("order_item"), "order_item");
}
#[test]
fn test_parity_str_snake_all_uppercase() {
assert_eq!(has_many::class_to_snake_case("URL"), "u_r_l");
}
#[test]
fn test_parity_has_many_default_foreign_key() {
assert_eq!(has_many::default_foreign_key("User"), "user_id");
assert_eq!(has_many::default_foreign_key("Order"), "order_id");
assert_eq!(has_many::default_foreign_key("OrderItem"), "order_item_id");
}
#[test]
fn test_parity_belongs_to_default_foreign_key() {
assert_eq!(
belongs_to::default_belongs_to_foreign_key("User"),
"user_id"
);
assert_eq!(
belongs_to::default_belongs_to_foreign_key("Order"),
"order_id"
);
assert_eq!(
belongs_to::default_belongs_to_foreign_key("OrderItem"),
"order_item_id"
);
}
#[test]
fn test_parity_belongs_to_default_fk_with_namespace() {
assert_eq!(
belongs_to::default_belongs_to_foreign_key("app\\model\\User"),
"user_id"
);
assert_eq!(
belongs_to::default_belongs_to_foreign_key("app/model/User"),
"user_id"
);
}
#[test]
fn test_parity_has_many_vs_belongs_to_default_fk_semantics() {
assert_eq!(
has_many::default_foreign_key("User"),
belongs_to::default_belongs_to_foreign_key("User")
);
}
#[test]
fn test_parity_default_junction_table_order_sensitive() {
assert_eq!(
belongs_to_many::default_junction_table("User", "Role"),
"user_role"
);
assert_eq!(
belongs_to_many::default_junction_table("Role", "User"),
"role_user"
);
}
#[test]
fn test_parity_default_junction_table_multi_word() {
assert_eq!(
belongs_to_many::default_junction_table("OrderItem", "Tag"),
"order_item_tag"
);
}
#[test]
fn test_parity_belongs_to_many_default_fk_semantics() {
assert_eq!(belongs_to_many::default_related_fk("Role"), "role_id");
assert_eq!(belongs_to_many::default_current_fk("User"), "user_id");
}
#[test]
fn test_parity_has_many_sql_pattern() {
let sql = has_many::has_many_sql("orders", "user_id", "1");
assert_eq!(sql, "SELECT * FROM orders WHERE user_id = 1");
}
#[test]
fn test_parity_php_has_many_default_config() {
let rel = has_many::php_has_many("User", "orders", None, None);
assert_eq!(rel.foreign_key, "user_id");
assert_eq!(rel.child_pk, "id");
assert_eq!(rel.child_model, "orders");
}
#[test]
fn test_parity_php_has_many_explicit_config() {
let rel = has_many::php_has_many("User", "orders", Some("uid"), Some("pk"));
assert_eq!(rel.foreign_key, "uid");
assert_eq!(rel.child_pk, "pk");
}
#[test]
fn test_parity_belongs_to_sql_pattern() {
let sql = belongs_to::belongs_to_sql("users", "id", "1");
assert_eq!(sql, "SELECT * FROM users WHERE id = 1");
}
#[test]
fn test_parity_php_belongs_to_default_config() {
let rel = belongs_to::php_belongs_to("User", "users", None, None);
assert_eq!(rel.foreign_key, "user_id");
assert_eq!(rel.parent_pk, "id");
assert_eq!(rel.parent_model, "users");
}
#[test]
fn test_parity_php_belongs_to_explicit_config() {
let rel = belongs_to::php_belongs_to("User", "users", Some("uid"), Some("pk"));
assert_eq!(rel.foreign_key, "uid");
assert_eq!(rel.parent_pk, "pk");
}
#[test]
fn test_parity_has_one_sql_pattern() {
let sql = has_one::has_one_sql("profiles", "user_id", "1");
assert_eq!(sql, "SELECT * FROM profiles WHERE user_id = 1");
}
#[test]
fn test_parity_php_has_one_default_config() {
let rel = has_one::php_has_one("User", "profiles", None, None);
assert_eq!(rel.foreign_key, "user_id");
assert_eq!(rel.child_pk, "id");
assert_eq!(rel.child_model, "profiles");
}
#[test]
fn test_parity_belongs_to_many_sql_pattern() {
let sql = belongs_to_many::belongs_to_many_sql(
"roles", "user_role", "id", "role_id", "user_id", "1", );
assert_eq!(
sql,
"SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id = 1"
);
}
#[test]
fn test_parity_php_belongs_to_many_default_config() {
let rel = belongs_to_many::php_belongs_to_many(
"User", "Role", "roles", None, None, None, None, );
assert_eq!(rel.junction_table, "user_role");
assert_eq!(rel.foreign_key, "user_id");
assert_eq!(rel.other_key, "role_id");
assert_eq!(rel.target_pk, "id");
assert_eq!(rel.target_model, "roles");
}
#[test]
fn test_parity_morph_many_sql_pattern() {
let sql = morph::morph_many_sql(
"comments",
"commentable_type",
"Post",
"commentable_id",
"1",
);
assert_eq!(
sql,
"SELECT * FROM comments WHERE commentable_type = 'Post' AND commentable_id = 1"
);
}
#[test]
fn test_parity_morph_many_in_sql_pattern() {
let sql = morph::morph_many_in_sql(
"comments",
"commentable_type",
"Post",
"commentable_id",
&["1", "2", "3"],
);
assert_eq!(
sql,
"SELECT * FROM comments WHERE commentable_id IN (1, 2, 3) AND commentable_type = 'Post'"
);
}
#[test]
fn test_parity_php_morph_many_default_config() {
let rel = morph::php_morph_many("Post", "comments", "commentable", None, None, None);
assert_eq!(rel.morph_type_column, "commentable_type");
assert_eq!(rel.morph_id_column, "commentable_id");
assert_eq!(rel.morph_type_value, "Post");
assert_eq!(rel.child_model, "comments");
}
#[test]
fn test_parity_morph_default_column_names() {
assert_eq!(
morph::default_morph_type_column("commentable"),
"commentable_type"
);
assert_eq!(
morph::default_morph_id_column("commentable"),
"commentable_id"
);
assert_eq!(
morph::default_morph_type_column("imageable"),
"imageable_type"
);
assert_eq!(morph::default_morph_id_column("imageable"), "imageable_id");
}
#[test]
fn test_parity_morph_to_sql_pattern() {
let sql = morph::morph_to_sql("posts", "id", "1");
assert_eq!(sql, "SELECT * FROM posts WHERE id = 1");
}
#[test]
fn test_parity_php_morph_to_default_config() {
let rel = morph::php_morph_to("commentable", None, None);
assert_eq!(rel.morph_type_column, "commentable_type");
assert_eq!(rel.morph_id_column, "commentable_id");
}
#[test]
fn test_parity_group_by_morph_type() {
use serde_json::json;
let rows = vec![
json!({"commentable_type": "Post", "commentable_id": 1}),
json!({"commentable_type": "Post", "commentable_id": 2}),
json!({"commentable_type": "Video", "commentable_id": 1}),
json!({"commentable_type": "Post", "commentable_id": 3}),
];
let grouped = morph::group_by_morph_type(&rows, "commentable_type", "commentable_id");
assert_eq!(grouped.get("Post").unwrap().len(), 3);
assert_eq!(grouped.get("Video").unwrap().len(), 1);
assert_eq!(
grouped.get("Post").unwrap(),
&vec!["1".to_string(), "2".to_string(), "3".to_string()]
);
assert_eq!(grouped.get("Video").unwrap(), &vec!["1".to_string()]);
}
#[test]
fn test_parity_has_many_in_sql_pattern() {
let sql = with::has_many_in_sql("orders", "user_id", &["1", "2", "3"]);
assert_eq!(sql, "SELECT * FROM orders WHERE user_id IN (1, 2, 3)");
}
#[test]
fn test_parity_has_one_in_sql_pattern() {
let sql = with::has_one_in_sql("profiles", "user_id", &["1", "2"]);
assert_eq!(sql, "SELECT * FROM profiles WHERE user_id IN (1, 2)");
}
#[test]
fn test_parity_belongs_to_in_sql_pattern() {
let sql = with::belongs_to_in_sql("users", "id", &["1", "2", "3"]);
assert_eq!(sql, "SELECT * FROM users WHERE id IN (1, 2, 3)");
}
#[test]
fn test_parity_belongs_to_many_in_sql_pattern() {
let sql = with::belongs_to_many_in_sql(
"roles",
"user_role",
"id",
"role_id",
"user_id",
&["1", "2"],
);
assert_eq!(
sql,
"SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id IN (1, 2)"
);
}
#[test]
fn test_parity_with_notation_parse() {
assert_eq!(with::parse_with_notation("category"), ("category", None));
assert_eq!(
with::parse_with_notation("items.product"),
("items", Some("product"))
);
assert_eq!(with::parse_with_notation("a.b.c"), ("a", Some("b.c")));
}
#[test]
fn test_parity_scenario_user_has_many_orders() {
let rel = has_many::php_has_many("User", "orders", None, None);
assert_eq!(rel.foreign_key, "user_id");
assert_eq!(rel.child_pk, "id");
let single_sql = has_many::has_many_sql("orders", "user_id", "1");
assert_eq!(single_sql, "SELECT * FROM orders WHERE user_id = 1");
let batch_sql = with::has_many_in_sql("orders", "user_id", &["1", "2", "3"]);
assert_eq!(batch_sql, "SELECT * FROM orders WHERE user_id IN (1, 2, 3)");
}
#[test]
fn test_parity_scenario_order_belongs_to_user() {
let rel = belongs_to::php_belongs_to("User", "users", None, None);
assert_eq!(rel.foreign_key, "user_id");
assert_eq!(rel.parent_pk, "id");
let single_sql = belongs_to::belongs_to_sql("users", "id", "1");
assert_eq!(single_sql, "SELECT * FROM users WHERE id = 1");
let batch_sql = with::belongs_to_in_sql("users", "id", &["1", "2", "3"]);
assert_eq!(batch_sql, "SELECT * FROM users WHERE id IN (1, 2, 3)");
}
#[test]
fn test_parity_scenario_user_has_one_profile() {
let rel = has_one::php_has_one("User", "profiles", None, None);
assert_eq!(rel.foreign_key, "user_id");
assert_eq!(rel.child_pk, "id");
let single_sql = has_one::has_one_sql("profiles", "user_id", "1");
assert_eq!(single_sql, "SELECT * FROM profiles WHERE user_id = 1");
let batch_sql = with::has_one_in_sql("profiles", "user_id", &["1", "2"]);
assert_eq!(batch_sql, "SELECT * FROM profiles WHERE user_id IN (1, 2)");
}
#[test]
fn test_parity_scenario_user_belongs_to_many_roles() {
let rel = belongs_to_many::php_belongs_to_many("User", "Role", "roles", None, None, None, None);
assert_eq!(rel.junction_table, "user_role");
assert_eq!(rel.foreign_key, "user_id");
assert_eq!(rel.other_key, "role_id");
assert_eq!(rel.target_pk, "id");
let single_sql =
belongs_to_many::belongs_to_many_sql("roles", "user_role", "id", "role_id", "user_id", "1");
assert_eq!(
single_sql,
"SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id = 1"
);
let batch_sql = with::belongs_to_many_in_sql(
"roles",
"user_role",
"id",
"role_id",
"user_id",
&["1", "2"],
);
assert_eq!(
batch_sql,
"SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id IN (1, 2)"
);
}
#[test]
fn test_parity_scenario_post_morph_many_comments() {
let rel = morph::php_morph_many("Post", "comments", "commentable", None, None, None);
assert_eq!(rel.morph_type_column, "commentable_type");
assert_eq!(rel.morph_id_column, "commentable_id");
assert_eq!(rel.morph_type_value, "Post");
let single_sql = morph::morph_many_sql(
"comments",
"commentable_type",
"Post",
"commentable_id",
"1",
);
assert_eq!(
single_sql,
"SELECT * FROM comments WHERE commentable_type = 'Post' AND commentable_id = 1"
);
let batch_sql = morph::morph_many_in_sql(
"comments",
"commentable_type",
"Post",
"commentable_id",
&["1", "2", "3"],
);
assert_eq!(
batch_sql,
"SELECT * FROM comments WHERE commentable_id IN (1, 2, 3) AND commentable_type = 'Post'"
);
}
#[test]
fn test_parity_scenario_comment_morph_to_post_or_video() {
let rel = morph::php_morph_to("commentable", None, None);
assert_eq!(rel.morph_type_column, "commentable_type");
assert_eq!(rel.morph_id_column, "commentable_id");
use serde_json::json;
let comments = vec![
json!({"commentable_type": "Post", "commentable_id": 1}),
json!({"commentable_type": "Video", "commentable_id": 1}),
json!({"commentable_type": "Post", "commentable_id": 2}),
];
let grouped = morph::group_by_morph_type(&comments, "commentable_type", "commentable_id");
assert_eq!(
grouped.get("Post").unwrap(),
&vec!["1".to_string(), "2".to_string()]
);
assert_eq!(grouped.get("Video").unwrap(), &vec!["1".to_string()]);
let post_sql = morph::morph_to_sql("posts", "id", "1");
let video_sql = morph::morph_to_sql("videos", "id", "1");
assert_eq!(post_sql, "SELECT * FROM posts WHERE id = 1");
assert_eq!(video_sql, "SELECT * FROM videos WHERE id = 1");
}
#[test]
fn test_parity_n_plus_one_pattern_detection() {
let mut records = vec![n_plus_one::SqlQueryRecord::new(
"SELECT * FROM users",
"users",
0,
0,
)];
for i in 1..=6u64 {
records.push(n_plus_one::SqlQueryRecord::new(
&format!("SELECT * FROM orders WHERE user_id = {}", i),
"orders",
i * 100,
i,
));
}
let config = n_plus_one::DetectionConfig::new(5, 1000);
let alerts = n_plus_one::detect_n_plus_one(&records, &config);
assert_eq!(alerts.len(), 1);
assert_eq!(alerts[0].table, "orders");
assert_eq!(alerts[0].query_count, 6);
}
#[test]
fn test_parity_with_avoids_n_plus_one() {
let records = vec![
n_plus_one::SqlQueryRecord::new("SELECT * FROM users", "users", 0, 0),
n_plus_one::SqlQueryRecord::new(
"SELECT * FROM orders WHERE user_id IN (1, 2, 3, 4, 5, 6)",
"orders",
100,
1,
),
];
let config = n_plus_one::DetectionConfig::new(5, 1000);
let alerts = n_plus_one::detect_n_plus_one(&records, &config);
assert_eq!(alerts.len(), 0); }
#[test]
fn test_parity_suggest_with_usage_format() {
let suggestion = n_plus_one::suggest_with_usage("orders", 6);
assert!(suggestion.contains("with('orders')"));
assert!(suggestion.contains("6 queries"));
assert!(suggestion.contains("N+1 problem"));
}
#[test]
fn test_parity_n_plus_one_detector_integration() {
let mut detector = n_plus_one::NPlusOneDetector::default();
detector.record("SELECT * FROM users", "users", 0);
for i in 1..=6u64 {
detector.record(
&format!("SELECT * FROM orders WHERE user_id = {}", i),
"orders",
i * 100,
);
}
let alerts = detector.detect();
assert_eq!(alerts.len(), 1);
assert_eq!(alerts[0].table, "orders");
assert_eq!(alerts[0].query_count, 6);
}
#[test]
fn test_parity_n_plus_one_different_query_no_alert() {
let records = vec![
n_plus_one::SqlQueryRecord::new("SELECT * FROM orders WHERE user_id = 1", "orders", 100, 0),
n_plus_one::SqlQueryRecord::new(
"SELECT * FROM orders WHERE user_id = 2 AND status = 1",
"orders",
200,
1,
),
n_plus_one::SqlQueryRecord::new(
"SELECT * FROM orders WHERE user_id = 3 AND status = 2",
"orders",
300,
2,
),
];
let config = n_plus_one::DetectionConfig::new(3, 1000);
let alerts = n_plus_one::detect_n_plus_one(&records, &config);
assert_eq!(alerts.len(), 0); }
#[test]
fn test_parity_collect_pk_values_field_order() {
use serde_json::json;
let rows = vec![
json!({"id": 3, "name": "Charlie"}),
json!({"id": 1, "name": "Alice"}),
json!({"id": 2, "name": "Bob"}),
];
let pks = with::collect_pk_values(&rows, "id");
assert_eq!(pks, vec!["3".to_string(), "1".to_string(), "2".to_string()]);
}
#[test]
fn test_parity_group_by_fk_preserves_insertion_order() {
use serde_json::json;
let rows = vec![
json!({"id": 101, "user_id": 1, "name": "Order A"}),
json!({"id": 102, "user_id": 2, "name": "Order B"}),
json!({"id": 103, "user_id": 1, "name": "Order C"}),
];
let grouped = with::group_by_fk(rows, "user_id");
assert_eq!(grouped.get("1").unwrap().len(), 2);
assert_eq!(grouped.get("2").unwrap().len(), 1);
assert_eq!(grouped.get("1").unwrap()[0]["id"], 101);
assert_eq!(grouped.get("1").unwrap()[1]["id"], 103);
}
#[test]
fn test_parity_sanitize_pk_value_sql_injection_prevention() {
assert_eq!(with::sanitize_pk_value("1"), "1"); assert_eq!(with::sanitize_pk_value("abc"), "'abc'"); assert_eq!(with::sanitize_pk_value("a'b"), "'a''b'"); }
#[test]
fn test_r5_parity_has_many_vs_belongs_to_caller_semantics() {
let has_many_fk = has_many::default_foreign_key("User");
let belongs_to_fk = belongs_to::default_belongs_to_foreign_key("User");
assert_eq!(has_many_fk, belongs_to_fk); assert_eq!(has_many_fk, "user_id");
}
#[test]
fn test_r5_parity_belongs_to_many_junction_table_order_sensitive() {
let user_role = belongs_to_many::default_junction_table("User", "Role");
let role_user = belongs_to_many::default_junction_table("Role", "User");
assert_eq!(user_role, "user_role");
assert_eq!(role_user, "role_user");
assert_ne!(user_role, role_user); }
#[test]
fn test_r5_parity_belongs_to_many_fk_naming_inversion() {
let rel = belongs_to_many::php_belongs_to_many("User", "Role", "roles", None, None, None, None);
assert_eq!(rel.foreign_key, "user_id"); assert_eq!(rel.other_key, "role_id"); assert_eq!(belongs_to_many::default_current_fk("User"), rel.foreign_key);
assert_eq!(belongs_to_many::default_related_fk("Role"), rel.other_key);
}
#[test]
fn test_r5_parity_morph_many_default_value_derivation() {
assert_eq!(
morph::default_morph_type_column("commentable"),
"commentable_type"
);
assert_eq!(
morph::default_morph_id_column("commentable"),
"commentable_id"
);
let rel = morph::php_morph_many("Post", "comments", "commentable", None, None, None);
assert_eq!(rel.morph_type_column, "commentable_type");
assert_eq!(rel.morph_id_column, "commentable_id");
assert_eq!(rel.morph_type_value, "Post"); }
#[test]
fn test_r5_parity_morph_to_no_type_parameter() {
let rel = morph::php_morph_to("commentable", None, None);
assert_eq!(rel.morph_type_column, "commentable_type");
assert_eq!(rel.morph_id_column, "commentable_id");
}
#[test]
fn test_r5_parity_with_eagerly_result_set_in_query() {
assert_eq!(
with::has_many_in_sql("orders", "user_id", &["1", "2", "3"]),
"SELECT * FROM orders WHERE user_id IN (1, 2, 3)"
);
assert_eq!(
with::has_one_in_sql("profiles", "user_id", &["1", "2"]),
"SELECT * FROM profiles WHERE user_id IN (1, 2)"
);
assert_eq!(
with::belongs_to_in_sql("users", "id", &["1", "2", "3"]),
"SELECT * FROM users WHERE id IN (1, 2, 3)"
);
assert_eq!(
with::belongs_to_many_in_sql(
"roles", "user_role", "id", "role_id", "user_id", &["1", "2"]
),
"SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id IN (1, 2)"
);
}
#[test]
fn test_r5_parity_empty_pk_values_returns_in_null() {
assert_eq!(
with::has_many_in_sql("orders", "user_id", &[]),
"SELECT * FROM orders WHERE user_id IN (NULL)"
);
assert_eq!(
with::belongs_to_in_sql("users", "id", &[]),
"SELECT * FROM users WHERE id IN (NULL)"
);
}
#[test]
fn test_r5_parity_php_get_foreign_key_algorithm() {
assert_eq!(has_many::default_foreign_key("User"), "user_id");
assert_eq!(has_many::default_foreign_key("OrderItem"), "order_item_id");
assert_eq!(has_many::default_foreign_key("app\\model\\User"), "user_id");
assert_eq!(has_many::default_foreign_key("app/model/User"), "user_id");
}
#[test]
fn test_r5_parity_php_belongs_to_relation_name() {
assert_eq!(
belongs_to::default_belongs_to_foreign_key("User"),
"user_id"
);
}
#[test]
fn test_r5_parity_php_belongs_to_many_middle_default() {
assert_eq!(
belongs_to_many::default_junction_table("User", "Role"),
"user_role"
);
assert_eq!(
belongs_to_many::default_junction_table("OrderItem", "Tag"),
"order_item_tag"
);
}