use ents::{
DatabaseError, Edge, EdgeCursor, EdgeQuery, EdgeValue, Id, QueryEdge,
Transactional,
};
use ents_sqlite::Txn;
use r2d2_sqlite::rusqlite::Connection;
fn setup_db() -> Connection {
let conn = Connection::open_in_memory().unwrap();
conn.execute(
"CREATE TABLE entities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL,
data TEXT NOT NULL
)",
[],
)
.unwrap();
conn.execute(
"CREATE TABLE edges (
source INTEGER NOT NULL,
type BLOB NOT NULL,
dest INTEGER NOT NULL,
PRIMARY KEY (source, type, dest)
)",
[],
)
.unwrap();
conn
}
fn insert_edges(
txn: &Txn,
edges: &[(Id, &[u8], Id)],
) -> Result<(), DatabaseError> {
for (source, sort_key, dest) in edges {
txn.create_edge(EdgeValue {
source: *source,
sort_key: sort_key.to_vec(),
dest: *dest,
})?;
}
Ok(())
}
#[test]
fn test_find_edges_asc_no_filter() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
insert_edges(
&txn,
&[
(1, b"follows", 10),
(1, b"follows", 20),
(1, b"likes", 5),
(1, b"likes", 15),
(1, b"blocks", 30),
],
)
.unwrap();
let query = EdgeQuery::asc(&[]);
let result = txn.find_edges(1, query).unwrap();
assert_eq!(result.edges.len(), 5);
assert_eq!(result.edges[0], Edge::new(1, b"blocks".to_vec(), 30));
assert_eq!(result.edges[1], Edge::new(1, b"follows".to_vec(), 10));
assert_eq!(result.edges[2], Edge::new(1, b"follows".to_vec(), 20));
assert_eq!(result.edges[3], Edge::new(1, b"likes".to_vec(), 5));
assert_eq!(result.edges[4], Edge::new(1, b"likes".to_vec(), 15));
}
#[test]
fn test_find_edges_desc_no_filter() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
insert_edges(
&txn,
&[
(1, b"follows", 10),
(1, b"follows", 20),
(1, b"likes", 5),
(1, b"likes", 15),
(1, b"blocks", 30),
],
)
.unwrap();
let query = EdgeQuery::desc(&[]);
let result = txn.find_edges(1, query).unwrap();
assert_eq!(result.edges.len(), 5);
assert_eq!(result.edges[0], Edge::new(1, b"likes".to_vec(), 15));
assert_eq!(result.edges[1], Edge::new(1, b"likes".to_vec(), 5));
assert_eq!(result.edges[2], Edge::new(1, b"follows".to_vec(), 20));
assert_eq!(result.edges[3], Edge::new(1, b"follows".to_vec(), 10));
assert_eq!(result.edges[4], Edge::new(1, b"blocks".to_vec(), 30));
}
#[test]
fn test_find_edges_with_single_name_filter() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
insert_edges(
&txn,
&[
(1, b"follows", 10),
(1, b"follows", 20),
(1, b"likes", 5),
(1, b"likes", 15),
],
)
.unwrap();
let query = EdgeQuery::asc(&[b"follows"]);
let result = txn.find_edges(1, query).unwrap();
assert_eq!(result.edges.len(), 2);
assert_eq!(result.edges[0], Edge::new(1, b"follows".to_vec(), 10));
assert_eq!(result.edges[1], Edge::new(1, b"follows".to_vec(), 20));
}
#[test]
fn test_find_edges_with_multiple_name_filters() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
insert_edges(
&txn,
&[
(1, b"follows", 10),
(1, b"follows", 20),
(1, b"likes", 5),
(1, b"likes", 15),
(1, b"blocks", 30),
],
)
.unwrap();
let query = EdgeQuery::asc(&[b"follows" as &[u8], b"likes" as &[u8]]);
let result = txn.find_edges(1, query).unwrap();
assert_eq!(result.edges.len(), 4);
assert_eq!(result.edges[0], Edge::new(1, b"follows".to_vec(), 10));
assert_eq!(result.edges[1], Edge::new(1, b"follows".to_vec(), 20));
assert_eq!(result.edges[2], Edge::new(1, b"likes".to_vec(), 5));
assert_eq!(result.edges[3], Edge::new(1, b"likes".to_vec(), 15));
}
#[test]
fn test_find_edges_asc_with_cursor() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
insert_edges(
&txn,
&[
(1, b"follows", 10),
(1, b"follows", 20),
(1, b"follows", 30),
(1, b"likes", 5),
(1, b"likes", 15),
],
)
.unwrap();
let cursor = EdgeCursor::new(b"follows", 10);
let query = EdgeQuery::asc(&[]).with_cursor(cursor);
let result = txn.find_edges(1, query).unwrap();
assert_eq!(result.edges.len(), 4);
assert_eq!(result.edges[0], Edge::new(1, b"follows".to_vec(), 20));
assert_eq!(result.edges[1], Edge::new(1, b"follows".to_vec(), 30));
assert_eq!(result.edges[2], Edge::new(1, b"likes".to_vec(), 5));
assert_eq!(result.edges[3], Edge::new(1, b"likes".to_vec(), 15));
}
#[test]
fn test_find_edges_desc_with_cursor() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
insert_edges(
&txn,
&[
(1, b"follows", 10),
(1, b"follows", 20),
(1, b"follows", 30),
(1, b"likes", 5),
(1, b"likes", 15),
],
)
.unwrap();
let cursor = EdgeCursor::new(b"likes", 5);
let query = EdgeQuery::desc(&[]).with_cursor(cursor);
let result = txn.find_edges(1, query).unwrap();
assert_eq!(result.edges.len(), 3);
assert_eq!(result.edges[0], Edge::new(1, b"follows".to_vec(), 30));
assert_eq!(result.edges[1], Edge::new(1, b"follows".to_vec(), 20));
assert_eq!(result.edges[2], Edge::new(1, b"follows".to_vec(), 10));
}
#[test]
fn test_find_edges_asc_cursor_with_filter() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
insert_edges(
&txn,
&[
(1, b"follows", 10),
(1, b"follows", 20),
(1, b"follows", 30),
(1, b"likes", 5),
(1, b"likes", 15),
],
)
.unwrap();
let cursor = EdgeCursor::new(b"follows", 10);
let query = EdgeQuery::asc(&[b"follows"]).with_cursor(cursor);
let result = txn.find_edges(1, query).unwrap();
assert_eq!(result.edges.len(), 2);
assert_eq!(result.edges[0], Edge::new(1, b"follows".to_vec(), 20));
assert_eq!(result.edges[1], Edge::new(1, b"follows".to_vec(), 30));
}
#[test]
fn test_find_edges_empty_result() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
insert_edges(&txn, &[(1, b"follows", 10), (1, b"follows", 20)]).unwrap();
let query = EdgeQuery::asc(&[b"blocks"]);
let result = txn.find_edges(1, query).unwrap();
assert_eq!(result.edges.len(), 0);
}
#[test]
fn test_find_edges_no_edges_for_source() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
insert_edges(&txn, &[(1, b"follows", 10)]).unwrap();
let query = EdgeQuery::asc(&[]);
let result = txn.find_edges(999, query).unwrap();
assert_eq!(result.edges.len(), 0);
}
#[test]
fn test_find_edges_pagination_asc() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
let mut edges = Vec::new();
for i in 1..=10 {
edges.push((1, b"item" as &[u8], i * 10));
}
insert_edges(&txn, &edges).unwrap();
let query = EdgeQuery::asc(&[b"item"]);
let page1 = txn.find_edges(1, query).unwrap();
assert_eq!(page1.edges.len(), 10);
let last_edge = page1.edges.last().unwrap();
let cursor = EdgeCursor::new(&last_edge.sort_key, last_edge.dest);
let query = EdgeQuery::asc(&[b"item"]).with_cursor(cursor);
let page2 = txn.find_edges(1, query).unwrap();
assert_eq!(page2.edges.len(), 0);
}
#[test]
fn test_find_edges_pagination_desc() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
let mut edges = Vec::new();
for i in 1..=10 {
edges.push((1, b"item" as &[u8], i * 10));
}
insert_edges(&txn, &edges).unwrap();
let query = EdgeQuery::desc(&[b"item"]);
let page1 = txn.find_edges(1, query).unwrap();
assert_eq!(page1.edges.len(), 10);
assert_eq!(page1.edges[0].dest, 100);
let last_edge = page1.edges.last().unwrap();
let cursor = EdgeCursor::new(&last_edge.sort_key, last_edge.dest);
let query = EdgeQuery::desc(&[b"item"]).with_cursor(cursor);
let page2 = txn.find_edges(1, query).unwrap();
assert_eq!(page2.edges.len(), 0);
}
#[test]
fn test_find_edges_limit_100() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
let mut edges = Vec::new();
for i in 1..=150 {
edges.push((1, b"item" as &[u8], i));
}
insert_edges(&txn, &edges).unwrap();
let query = EdgeQuery::asc(&[b"item"]);
let result = txn.find_edges(1, query).unwrap();
assert_eq!(result.edges.len(), 100);
assert_eq!(result.edges[0].dest, 1);
assert_eq!(result.edges[99].dest, 100);
}
#[test]
fn test_find_edges_cursor_boundary() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
insert_edges(
&txn,
&[(1, b"a", 1), (1, b"a", 2), (1, b"b", 1), (1, b"b", 2)],
)
.unwrap();
let cursor = EdgeCursor::new(b"a", 2);
let query = EdgeQuery::asc(&[]).with_cursor(cursor);
let result = txn.find_edges(1, query).unwrap();
assert_eq!(result.edges.len(), 2);
assert_eq!(result.edges[0], Edge::new(1, b"b".to_vec(), 1));
assert_eq!(result.edges[1], Edge::new(1, b"b".to_vec(), 2));
}
#[test]
fn test_find_edges_multiple_sources() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
insert_edges(
&txn,
&[
(1, b"follows", 10),
(1, b"follows", 20),
(2, b"follows", 30),
(2, b"follows", 40),
],
)
.unwrap();
let query = EdgeQuery::asc(&[b"follows"]);
let result1 = txn.find_edges(1, query).unwrap();
assert_eq!(result1.edges.len(), 2);
assert_eq!(result1.edges[0].dest, 10);
assert_eq!(result1.edges[1].dest, 20);
let query = EdgeQuery::asc(&[b"follows"]);
let result2 = txn.find_edges(2, query).unwrap();
assert_eq!(result2.edges.len(), 2);
assert_eq!(result2.edges[0].dest, 30);
assert_eq!(result2.edges[1].dest, 40);
}
#[test]
fn test_find_edges_binary_edge_types() {
let conn = setup_db();
let tx = conn.unchecked_transaction().unwrap();
let txn = Txn::new(tx);
insert_edges(
&txn,
&[
(1, &[0x00, 0x01, 0x02], 10),
(1, &[0x00, 0x01, 0x03], 20),
(1, &[0xFF, 0xFE, 0xFD], 30),
],
)
.unwrap();
let query = EdgeQuery::asc(&[&[0x00, 0x01, 0x02]]);
let result = txn.find_edges(1, query).unwrap();
assert_eq!(result.edges.len(), 1);
assert_eq!(result.edges[0].sort_key, vec![0x00, 0x01, 0x02]);
assert_eq!(result.edges[0].dest, 10);
}