use anyhow::Result;
use uni_db::{DataType, Uni};
#[tokio::test]
async fn test_create_node_return_id() -> Result<()> {
let db = Uni::in_memory().build().await?;
let tx = db.session().tx().await?;
tx.execute("CREATE LABEL Person (name STRING)").await?;
tx.commit().await?;
let tx = db.session().tx().await?;
let result = tx
.query("CREATE (n:Person {name: 'Alice'}) RETURN id(n) AS vid")
.await?;
tx.commit().await?;
assert_eq!(result.len(), 1, "Should return exactly 1 row");
let vid = result.rows()[0].get::<i64>("vid")?;
assert!(vid >= 0, "VID should be a non-negative integer, got {vid}");
Ok(())
}
#[tokio::test]
async fn test_create_edge_return_id() -> Result<()> {
let db = Uni::in_memory().build().await?;
let tx = db.session().tx().await?;
tx.execute("CREATE LABEL Person (name STRING)").await?;
tx.execute("CREATE EDGE TYPE KNOWS FROM Person TO Person")
.await?;
tx.commit().await?;
let tx = db.session().tx().await?;
let result = tx
.query(
"CREATE (a:Person {name: 'Alice'})-[r:KNOWS]->(b:Person {name: 'Bob'}) \
RETURN id(r) AS eid",
)
.await?;
tx.commit().await?;
assert_eq!(result.len(), 1, "Should return exactly 1 row");
let eid = result.rows()[0].get::<i64>("eid")?;
assert!(eid >= 0, "EID should be a non-negative integer, got {eid}");
Ok(())
}
#[tokio::test]
async fn test_create_node_return_id_and_properties() -> Result<()> {
let db = Uni::in_memory().build().await?;
let tx = db.session().tx().await?;
tx.execute("CREATE LABEL Person (name STRING)").await?;
tx.commit().await?;
let tx = db.session().tx().await?;
let result = tx
.query("CREATE (n:Person {name: 'Alice'}) RETURN id(n) AS vid, n.name AS name")
.await?;
tx.commit().await?;
assert_eq!(result.len(), 1, "Should return exactly 1 row");
let vid = result.rows()[0].get::<i64>("vid")?;
assert!(vid >= 0, "VID should be a non-negative integer, got {vid}");
assert_eq!(result.rows()[0].get::<String>("name")?, "Alice");
Ok(())
}
#[tokio::test]
async fn test_execute_create_returns_affected_rows() -> Result<()> {
let db = Uni::in_memory().build().await?;
let tx = db.session().tx().await?;
tx.execute("CREATE LABEL Person (name STRING)").await?;
tx.commit().await?;
let tx = db.session().tx().await?;
let result = tx.execute("CREATE (:Person {name: 'Alice'})").await?;
tx.commit().await?;
assert!(
result.affected_rows() >= 1,
"CREATE of 1 node should report affected_rows >= 1, got {}",
result.affected_rows(),
);
Ok(())
}
#[tokio::test]
async fn test_execute_create_multiple_returns_affected_rows() -> Result<()> {
let db = Uni::in_memory().build().await?;
let tx = db.session().tx().await?;
tx.execute("CREATE LABEL Person (name STRING)").await?;
tx.commit().await?;
let tx = db.session().tx().await?;
let result = tx
.execute("CREATE (:Person {name: 'Alice'}), (:Person {name: 'Bob'})")
.await?;
tx.commit().await?;
assert!(
result.affected_rows() >= 2,
"CREATE of 2 nodes should report affected_rows >= 2, got {}",
result.affected_rows(),
);
Ok(())
}
#[tokio::test]
async fn test_execute_delete_returns_affected_rows() -> Result<()> {
let db = Uni::in_memory().build().await?;
let tx = db.session().tx().await?;
tx.execute("CREATE LABEL Person (name STRING)").await?;
tx.execute("CREATE (:Person {name: 'Alice'})").await?;
tx.execute("CREATE (:Person {name: 'Bob'})").await?;
tx.commit().await?;
db.flush().await?;
let tx = db.session().tx().await?;
let result = tx.execute("MATCH (n:Person) DELETE n").await?;
tx.commit().await?;
assert!(
result.affected_rows() >= 1,
"DELETE should report affected_rows >= 1, got {}",
result.affected_rows(),
);
Ok(())
}
#[tokio::test]
async fn test_execute_set_returns_affected_rows() -> Result<()> {
let db = Uni::in_memory().build().await?;
let tx = db.session().tx().await?;
tx.execute("CREATE LABEL Person (name STRING, age INT)")
.await?;
tx.execute("CREATE (:Person {name: 'Alice', age: 25})")
.await?;
tx.commit().await?;
db.flush().await?;
let tx = db.session().tx().await?;
let result = tx
.execute("MATCH (n:Person {name: 'Alice'}) SET n.age = 30")
.await?;
tx.commit().await?;
assert!(
result.affected_rows() >= 1,
"SET should report affected_rows >= 1, got {}",
result.affected_rows(),
);
Ok(())
}
#[tokio::test]
async fn test_list_labels_includes_schema_labels() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.apply()
.await?;
let labels = db.list_labels().await?;
assert!(
labels.contains(&"Person".to_string()),
"list_labels() should include schema-registered label 'Person', got {labels:?}",
);
Ok(())
}
#[tokio::test]
async fn test_list_labels_after_schema_then_create() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.apply()
.await?;
let labels = db.list_labels().await?;
assert!(
labels.contains(&"Person".to_string()),
"After schema registration, 'Person' should appear in list_labels(), got {labels:?}",
);
let tx = db.session().tx().await?;
tx.execute("CREATE (:Person {name: 'Alice'})").await?;
tx.commit().await?;
db.flush().await?;
let labels = db.list_labels().await?;
assert!(
labels.contains(&"Person".to_string()),
"After creating a vertex, 'Person' should appear in list_labels(), got {labels:?}",
);
Ok(())
}
#[tokio::test]
async fn test_list_edge_types_vs_list_labels_consistency() -> Result<()> {
let db = Uni::in_memory().build().await?;
db.schema()
.label("Person")
.property("name", DataType::String)
.edge_type("KNOWS", &["Person"], &["Person"])
.apply()
.await?;
let edge_types = db.list_edge_types().await?;
assert!(
edge_types.contains(&"KNOWS".to_string()),
"list_edge_types() should include 'KNOWS', got {edge_types:?}",
);
let labels = db.list_labels().await?;
assert!(
labels.contains(&"Person".to_string()),
"list_labels() should be consistent with list_edge_types() and include schema-registered \
labels, got {labels:?}",
);
Ok(())
}