use reddb_server::{RedDBOptions, RedDBRuntime};
use std::time::{Duration, Instant};
fn runtime() -> RedDBRuntime {
RedDBRuntime::with_options(RedDBOptions::in_memory()).expect("runtime boots")
}
fn exec(rt: &RedDBRuntime, sql: &str) {
rt.execute_query(sql)
.unwrap_or_else(|err| panic!("{sql}: {err:?}"));
}
#[test]
fn wait_above_default_cap_is_rejected_with_explicit_message() {
let rt = runtime();
exec(&rt, "CREATE QUEUE qwait_cap_default");
exec(&rt, "QUEUE GROUP CREATE qwait_cap_default workers");
let started = Instant::now();
let err = rt
.execute_query("QUEUE READ qwait_cap_default GROUP workers CONSUMER c1 COUNT 1 WAIT 999h")
.expect_err("WAIT above cap should reject");
let elapsed = started.elapsed();
let msg = format!("{err}");
assert!(
msg.contains("red.config.queue.max_wait_ms"),
"error should name the cap key, got: {msg:?}"
);
assert!(
msg.contains("60000"),
"error should name the active cap value, got: {msg:?}"
);
assert!(
elapsed < Duration::from_millis(500),
"rejection should be immediate, elapsed={elapsed:?}"
);
}
#[test]
fn wait_above_operator_set_cap_is_rejected_with_active_cap_value() {
let rt = runtime();
exec(&rt, "SET CONFIG red.config.queue.max_wait_ms = 250");
exec(&rt, "CREATE QUEUE qwait_cap_tight");
exec(&rt, "QUEUE GROUP CREATE qwait_cap_tight workers");
let err = rt
.execute_query("QUEUE READ qwait_cap_tight GROUP workers CONSUMER c1 COUNT 1 WAIT 1s")
.expect_err("WAIT above tightened cap should reject");
let msg = format!("{err}");
assert!(
msg.contains("250"),
"error should reflect the operator-set cap (250), got: {msg:?}"
);
assert!(
msg.contains("red.config.queue.max_wait_ms"),
"error should name the cap key, got: {msg:?}"
);
let read = rt
.execute_query("QUEUE READ qwait_cap_tight GROUP workers CONSUMER c1 COUNT 1 WAIT 250ms")
.expect("WAIT at the cap should be accepted");
assert!(read.result.records.is_empty());
}
#[test]
fn wait_inside_explicit_transaction_is_rejected_as_autocommit_only() {
let rt = runtime();
exec(&rt, "CREATE QUEUE qwait_txn");
exec(&rt, "QUEUE GROUP CREATE qwait_txn workers");
exec(&rt, "BEGIN");
let started = Instant::now();
let err = rt
.execute_query("QUEUE READ qwait_txn GROUP workers CONSUMER c1 COUNT 1 WAIT 5s")
.expect_err("WAIT inside BEGIN/COMMIT should reject");
let elapsed = started.elapsed();
let msg = format!("{err}");
assert!(
msg.to_lowercase().contains("autocommit")
|| msg.to_lowercase().contains("explicit transaction"),
"error should explain WAIT is autocommit-only, got: {msg:?}"
);
assert!(
elapsed < Duration::from_millis(500),
"rejection should be immediate, no parking, elapsed={elapsed:?}"
);
exec(&rt, "ROLLBACK");
let read = rt
.execute_query("QUEUE READ qwait_txn GROUP workers CONSUMER c1 COUNT 1 WAIT 100ms")
.expect("autocommit WAIT after rollback should be accepted");
assert!(read.result.records.is_empty());
}
#[test]
fn wait_with_no_clause_is_unaffected_by_cap_and_txn_checks() {
let rt = runtime();
exec(&rt, "CREATE QUEUE qwait_none");
exec(&rt, "QUEUE GROUP CREATE qwait_none workers");
exec(&rt, "BEGIN");
let read = rt
.execute_query("QUEUE READ qwait_none GROUP workers CONSUMER c1 COUNT 1")
.expect("non-WAIT read inside a txn is still accepted");
assert!(read.result.records.is_empty());
exec(&rt, "COMMIT");
}