#![cfg(test)]
use sz_orm_core::dialect::get_dialect;
use sz_orm_core::entity_graph::EntityGraph;
use sz_orm_core::find_with_related::WithRelation;
use sz_orm_core::DbType;
#[test]
fn test_l3_1_dag_no_cycle() {
let mut graph = EntityGraph::new();
graph.add_edge("user", "posts");
graph.add_edge("posts", "comments");
assert!(graph.detect_cycles().is_ok(), "线性链不应检测到循环");
}
#[test]
fn test_l3_2_direct_cycle() {
let mut graph = EntityGraph::new();
graph.add_edge_with_graph("user", "posts", {
let mut sub = EntityGraph::new();
sub.add_edge("posts", "user");
sub
});
let result = graph.detect_cycles();
assert!(result.is_err(), "直接循环应被检测到");
let cycle = result.unwrap_err();
assert!(
cycle.contains(&"user".to_string()) && cycle.contains(&"posts".to_string()),
"循环路径应包含 user 和 posts: {:?}",
cycle
);
}
#[test]
fn test_l3_3_indirect_cycle() {
let mut graph = EntityGraph::new();
graph.add_edge_with_graph("user", "posts", {
let mut sub = EntityGraph::new();
sub.add_edge_with_graph("posts", "comments", {
let mut sub2 = EntityGraph::new();
sub2.add_edge("comments", "user");
sub2
});
sub
});
let result = graph.detect_cycles();
assert!(result.is_err(), "间接循环应被检测到");
let cycle = result.unwrap_err();
assert!(cycle.len() >= 3, "间接循环路径应至少 3 个节点: {:?}", cycle);
assert!(
cycle.contains(&"user".to_string())
&& cycle.contains(&"posts".to_string())
&& cycle.contains(&"comments".to_string()),
"循环路径应包含 user、posts、comments: {:?}",
cycle
);
assert_eq!(
cycle.first(),
cycle.last(),
"循环路径首尾应相同: {:?}",
cycle
);
}
#[test]
fn test_l3_4_self_loop() {
let mut graph = EntityGraph::new();
graph.add_edge("user", "user");
let result = graph.detect_cycles();
assert!(result.is_err(), "自环应被检测到");
let cycle = result.unwrap_err();
assert_eq!(
cycle.first(),
Some(&"user".to_string()),
"自环节点应为 user: {:?}",
cycle
);
}
#[test]
fn test_l3_5_multi_branch_dag() {
let mut graph = EntityGraph::new();
graph.add_edge("user", "posts");
graph.add_edge("user", "profile");
graph.add_edge("posts", "comments");
assert!(graph.detect_cycles().is_ok(), "多分支 DAG 不应检测到循环");
}
#[test]
fn test_l3_6_cycle_path_in_error() {
let mut graph = EntityGraph::new();
graph.add_edge_with_graph("user", "posts", {
let mut sub = EntityGraph::new();
sub.add_edge("posts", "user");
sub
});
let cycle = graph.detect_cycles().unwrap_err();
assert_eq!(
cycle.first(),
cycle.last(),
"循环路径首尾应相同: {:?}",
cycle
);
assert!(
cycle.contains(&"user".to_string()) && cycle.contains(&"posts".to_string()),
"路径应包含 user 和 posts: {:?}",
cycle
);
assert!(cycle.len() >= 2, "循环路径应至少 2 个节点: {:?}", cycle);
}
#[test]
fn test_l3_7_duplicate_edges() {
let mut graph = EntityGraph::new();
graph.add_edge("user", "posts");
graph.add_edge("user", "posts");
let result = graph.detect_duplicate_edges();
assert!(result.is_err(), "重复边应被检测到");
let duplicates = result.unwrap_err();
assert_eq!(duplicates.len(), 1, "应有 1 个重复边: {:?}", duplicates);
assert_eq!(
duplicates[0],
("user".to_string(), "posts".to_string()),
"重复边应为 (user, posts): {:?}",
duplicates
);
}
#[test]
fn test_l3_8_no_duplicate_edges() {
let mut graph = EntityGraph::new();
graph.add_edge("user", "posts");
graph.add_edge("user", "profile");
graph.add_edge("posts", "comments");
assert!(graph.detect_duplicate_edges().is_ok(), "无重复边不应报错");
}
#[test]
fn test_l3_9_validate_clean_graph() {
let mut graph = EntityGraph::new();
graph.add_edge("user", "posts");
graph.add_edge("posts", "comments");
assert!(graph.validate().is_ok(), "无环无重复的图应通过校验");
}
#[test]
fn test_l3_10_validate_cycle() {
let mut graph = EntityGraph::new();
graph.add_edge_with_graph("user", "posts", {
let mut sub = EntityGraph::new();
sub.add_edge("posts", "user");
sub
});
let result = graph.validate();
assert!(result.is_err(), "有循环的图应校验失败");
let err_msg = result.unwrap_err();
assert!(
err_msg.contains("循环引用"),
"错误信息应包含'循环引用': {}",
err_msg
);
}
#[test]
fn test_l3_11_validate_duplicate() {
let mut graph = EntityGraph::new();
graph.add_edge("user", "posts");
graph.add_edge("user", "posts");
let result = graph.validate();
assert!(result.is_err(), "有重复边的图应校验失败");
let err_msg = result.unwrap_err();
assert!(
err_msg.contains("重复边"),
"错误信息应包含'重复边': {}",
err_msg
);
}
#[test]
fn test_l3_12_empty_graph() {
let graph = EntityGraph::new();
assert!(graph.detect_cycles().is_ok(), "空图不应检测到循环");
assert!(graph.detect_duplicate_edges().is_ok(), "空图不应有重复边");
assert!(graph.validate().is_ok(), "空图应通过校验");
}
#[test]
fn test_l3_13_complex_dag() {
let mut graph = EntityGraph::new();
graph.add_edge("user", "posts");
graph.add_edge("user", "profile");
graph.add_edge_with_graph("posts", "comments", {
let mut sub = EntityGraph::new();
sub.add_edge("comments", "tags");
sub
});
assert!(graph.detect_cycles().is_ok(), "复杂 DAG 不应检测到循环");
assert!(graph.validate().is_ok(), "复杂 DAG 应通过校验");
}
#[test]
fn test_l3_14_deep_nested_cycle() {
let mut graph = EntityGraph::new();
graph.add_edge_with_graph("a", "b", {
let mut sub1 = EntityGraph::new();
sub1.add_edge_with_graph("b", "c", {
let mut sub2 = EntityGraph::new();
sub2.add_edge_with_graph("c", "d", {
let mut sub3 = EntityGraph::new();
sub3.add_edge("d", "a");
sub3
});
sub2
});
sub1
});
let result = graph.detect_cycles();
assert!(result.is_err(), "4 层嵌套循环应被检测到");
let cycle = result.unwrap_err();
assert!(cycle.len() >= 4, "4 层循环路径应至少 4 个节点: {:?}", cycle);
}
#[test]
fn test_l3_15_subgraph_cycle() {
let mut graph = EntityGraph::new();
graph.add_edge_with_graph("user", "posts", {
let mut sub = EntityGraph::new();
sub.add_edge_with_graph("x", "y", {
let mut inner = EntityGraph::new();
inner.add_edge("y", "x");
inner
});
sub
});
let result = graph.detect_cycles();
assert!(result.is_err(), "子图内部循环应被检测到");
}
#[test]
fn test_l3_16_with_relation_duplicate_eager() {
let dialect = get_dialect(DbType::MySQL).unwrap();
let loader = WithRelation::new(&*dialect, "users")
.unwrap()
.with_has_many("orders", "user_id", "id")
.unwrap()
.with_has_many("orders", "user_id", "id")
.unwrap();
let result = loader.load_eager(Some("users.id > 0"));
assert!(result.is_err(), "重复关联应返回 Err");
let err_msg = match result {
Err(e) => format!("{}", e),
Ok(_) => panic!("期望 Err,得到 Ok"),
};
assert!(
err_msg.contains("重复关联检测失败"),
"错误消息应包含'重复关联检测失败': {}",
err_msg
);
}
#[test]
fn test_l3_17_with_relation_duplicate_join() {
let dialect = get_dialect(DbType::MySQL).unwrap();
let loader = WithRelation::new(&*dialect, "users")
.unwrap()
.with_has_one("profiles", "user_id", "id")
.unwrap()
.with_has_one("profiles", "user_id", "id")
.unwrap();
let result = loader.load_join(Some("users.id > 0"));
assert!(result.is_err(), "重复关联应返回 Err");
let err_msg = match result {
Err(e) => format!("{}", e),
Ok(_) => panic!("期望 Err,得到 Ok"),
};
assert!(
err_msg.contains("重复关联检测失败"),
"错误消息应包含'重复关联检测失败': {}",
err_msg
);
}
#[test]
fn test_l3_18_with_relation_different_names_ok() {
let dialect = get_dialect(DbType::MySQL).unwrap();
let loader = WithRelation::new(&*dialect, "users")
.unwrap()
.with_has_many("orders", "user_id", "id")
.unwrap()
.with_has_one("profiles", "user_id", "id")
.unwrap();
let loaded = loader.load_eager(Some("users.id > 0"));
assert!(loaded.is_ok(), "不同关联名不应报错: {:?}", loaded.err());
let loaded = loaded.unwrap();
assert!(!loaded.main_sql().is_empty(), "主表 SQL 不应为空");
}
#[test]
fn test_l3_19_with_relation_same_name_different_type() {
let dialect = get_dialect(DbType::MySQL).unwrap();
let loader = WithRelation::new(&*dialect, "users")
.unwrap()
.with_has_many("orders", "user_id", "id")
.unwrap()
.with_belongs_to("orders", "order_id", "id")
.unwrap();
let result = loader.load_eager(None);
assert!(result.is_err(), "同名不同类型应返回 Err");
let err_msg = match result {
Err(e) => format!("{}", e),
Ok(_) => panic!("期望 Err,得到 Ok"),
};
assert!(
err_msg.contains("重复关联检测失败"),
"错误消息应包含'重复关联检测失败': {}",
err_msg
);
}
#[test]
fn test_l3_20_legitimate_self_reference_no_cycle() {
let mut graph = EntityGraph::new();
graph.add_edge("category", "children");
assert!(
graph.detect_cycles().is_ok(),
"合法自引用关系不应被误报为循环"
);
}