mod test_helpers;
use std::collections::BTreeMap;
use std::time::{Duration, Instant};
use lora_database::{CancellableDeadline, LoraErrorCode};
use test_helpers::TestDb;
const N: usize = 20_000;
fn seeded() -> TestDb {
let db = TestDb::new();
db.run(&format!("UNWIND range(1, {N}) AS i CREATE (:A {{i: i}})"));
db.run(&format!("UNWIND range(1, {N}) AS i CREATE (:B {{i: i}})"));
db
}
fn timed_out(db: &TestDb, query: &str, timeout: Duration) -> Duration {
let start = Instant::now();
let err = db
.service
.execute_with_params_timeout(query, None, BTreeMap::new(), timeout)
.expect_err("query should time out");
assert_eq!(err.code(), LoraErrorCode::Timeout, "{err}");
start.elapsed()
}
#[test]
fn streaming_write_honours_its_deadline_and_leaves_nothing() {
let db = seeded();
let took = timed_out(
&db,
"MATCH (a:A), (b:B) CREATE (:Pair)",
Duration::from_millis(50),
);
assert!(took < Duration::from_secs(2), "took {took:?}");
db.assert_count("MATCH (p:Pair) RETURN p", 0);
db.run("CREATE (:After)");
db.assert_count("MATCH (x:After) RETURN x", 1);
}
#[test]
fn filter_that_never_yields_still_times_out() {
let db = seeded();
let took = timed_out(
&db,
"MATCH (a:A), (b:B) WHERE a.i + b.i = -1 SET a.hit = true",
Duration::from_millis(50),
);
assert!(took < Duration::from_secs(2), "took {took:?}");
}
#[test]
fn cancellation_stops_a_running_query() {
let db = std::sync::Arc::new(seeded());
let mut handle = CancellableDeadline::new(None);
let deadline = handle.deadline();
let shared = db.clone();
let worker = std::thread::spawn(move || {
let service = &shared.service;
service.execute_with_params_deadline(
"MATCH (a:A), (b:B) RETURN count(*) AS c",
None,
BTreeMap::new(),
deadline,
)
});
std::thread::sleep(Duration::from_millis(50));
let start = Instant::now();
handle.cancel();
let err = worker
.join()
.unwrap()
.expect_err("cancelled query must fail");
assert_eq!(err.code(), LoraErrorCode::Timeout);
assert!(start.elapsed() < Duration::from_secs(2));
}