#![allow(non_snake_case)]
use triviumdb::Database;
const DIM: usize = 4;
fn tmp_db(name: &str) -> String {
let dir = std::env::temp_dir().join("triviumdb_test");
std::fs::create_dir_all(&dir).ok();
dir.join(format!("rev_{}", name))
.to_string_lossy()
.to_string()
}
fn cleanup(path: &str) {
for ext in &["", ".wal", ".vec", ".lock", ".flush_ok"] {
std::fs::remove_file(format!("{}{}", path, ext)).ok();
}
}
fn build_test_db(path: &str) -> Database<f32> {
cleanup(path);
let mut db = Database::<f32>::open(path, DIM).unwrap();
let alice_id = db
.insert(
&[1.0, 0.0, 0.0, 0.0],
serde_json::json!({"name": "Alice", "age": 30}),
)
.unwrap();
let bob_id = db
.insert(
&[0.0, 1.0, 0.0, 0.0],
serde_json::json!({"name": "Bob", "age": 25}),
)
.unwrap();
let carol_id = db
.insert(
&[0.0, 0.0, 1.0, 0.0],
serde_json::json!({"name": "Carol", "age": 35}),
)
.unwrap();
let acme_id = db
.insert(
&[0.0, 0.0, 0.0, 1.0],
serde_json::json!({"name": "Acme", "type": "company"}),
)
.unwrap();
let dave_id = db
.insert(
&[0.5, 0.5, 0.0, 0.0],
serde_json::json!({"name": "Dave", "age": 28}),
)
.unwrap();
db.link(alice_id, bob_id, "knows", 1.0).unwrap(); db.link(bob_id, carol_id, "knows", 1.0).unwrap(); db.link(alice_id, acme_id, "works_at", 1.0).unwrap(); db.link(dave_id, bob_id, "knows", 1.0).unwrap();
db
}
#[test]
fn 测试_反向单跳_谁认识Bob() {
let path = tmp_db("rev_single");
let db = build_test_db(&path);
let results = db
.tql(r#"MATCH (a {name: "Bob"})<-[:knows]-(b) RETURN b"#)
.unwrap();
assert_eq!(results.len(), 2, "Alice 和 Dave 都 knows Bob");
let names: Vec<&str> = results
.iter()
.map(|r| r["b"].payload.get("name").unwrap().as_str().unwrap())
.collect();
assert!(names.contains(&"Alice"));
assert!(names.contains(&"Dave"));
drop(db);
cleanup(&path);
}
#[test]
fn 测试_反向单跳_无标签() {
let path = tmp_db("rev_any");
let db = build_test_db(&path);
let results = db
.tql(r#"MATCH (a {name: "Bob"})<-[]-(b) RETURN b"#)
.unwrap();
assert_eq!(results.len(), 2, "Alice 和 Dave 都指向 Bob");
drop(db);
cleanup(&path);
}
#[test]
fn 测试_反向单跳_Carol() {
let path = tmp_db("rev_carol");
let db = build_test_db(&path);
let results = db
.tql(r#"MATCH (a {name: "Carol"})<-[:knows]-(b) RETURN b"#)
.unwrap();
assert_eq!(results.len(), 1);
assert_eq!(
results[0]["b"]
.payload
.get("name")
.unwrap()
.as_str()
.unwrap(),
"Bob"
);
drop(db);
cleanup(&path);
}
#[test]
fn 测试_反向可变长_溯源() {
let path = tmp_db("rev_varlen");
let db = build_test_db(&path);
let results = db
.tql(r#"MATCH (a {name: "Carol"})<-[:knows*1..2]-(b) RETURN b"#)
.unwrap();
assert!(
results.len() >= 3,
"应至少找到 Bob, Alice, Dave; got {}",
results.len()
);
let names: Vec<&str> = results
.iter()
.map(|r| r["b"].payload.get("name").unwrap().as_str().unwrap())
.collect();
assert!(names.contains(&"Bob"), "1跳: Bob");
assert!(names.contains(&"Alice"), "2跳: Alice");
assert!(names.contains(&"Dave"), "2跳: Dave");
drop(db);
cleanup(&path);
}
#[test]
fn 测试_双向遍历_Bob的所有关联() {
let path = tmp_db("bidir");
let db = build_test_db(&path);
let results = db
.tql(r#"MATCH (a {name: "Bob"})-[:knows]-(b) RETURN b"#)
.unwrap();
assert_eq!(results.len(), 3, "正向Carol + 反向Alice,Dave");
let names: Vec<&str> = results
.iter()
.map(|r| r["b"].payload.get("name").unwrap().as_str().unwrap())
.collect();
assert!(names.contains(&"Carol"), "正向: Bob->Carol");
assert!(names.contains(&"Alice"), "反向: Alice->Bob");
assert!(names.contains(&"Dave"), "反向: Dave->Bob");
drop(db);
cleanup(&path);
}
#[test]
fn 测试_双向遍历_无标签() {
let path = tmp_db("bidir_any");
let db = build_test_db(&path);
let results = db
.tql(r#"MATCH (a {name: "Alice"})-[]-(b) RETURN b"#)
.unwrap();
assert_eq!(results.len(), 2, "Alice 正向: Bob, Acme; 无反向入边");
drop(db);
cleanup(&path);
}
#[test]
fn 测试_入度统计_反向COUNT() {
let path = tmp_db("in_degree");
let db = build_test_db(&path);
let results = db
.tql(r#"MATCH (a {name: "Bob"})<-[:knows]-(b) RETURN count(b) AS in_degree"#)
.unwrap();
assert_eq!(results.len(), 1);
let count = results[0]["in_degree"]
.payload
.get("in_degree")
.unwrap()
.as_i64()
.unwrap();
assert_eq!(count, 2, "Bob 的 knows 入度应为 2 (Alice + Dave)");
drop(db);
cleanup(&path);
}
#[test]
fn 测试_EXPLAIN_反向模式() {
let path = tmp_db("explain_rev");
let db = build_test_db(&path);
let results = db
.tql(r#"EXPLAIN MATCH (a {name: "Bob"})<-[:knows]-(b) RETURN b"#)
.unwrap();
let plan = &results[0]["plan"].payload;
let detail = plan.get("detail").unwrap().as_str().unwrap();
assert!(detail.contains("knows"), "EXPLAIN 应展示边标签");
drop(db);
cleanup(&path);
}