use std::collections::BTreeSet;
use reifydb_test_harness::engine::TestEngine;
use reifydb_value::value::{Value, datetime::DateTime, frame::frame::Frame, value_type::ValueType};
fn engine_with_queue(declaration: &str) -> TestEngine {
let t = TestEngine::new();
t.admin("CREATE NAMESPACE test");
t.admin(declaration);
t
}
fn column(frames: &[Frame], name: &str) -> Vec<Value> {
let frame = frames.first().expect("a query must always return a frame");
let column = frame.columns.iter().find(|c| c.name == name).unwrap_or_else(|| {
panic!(
"result has no column {name}; got {:?}",
frame.columns.iter().map(|c| &c.name).collect::<Vec<_>>()
)
});
(0..frame.row_count()).map(|i| column.data.get_value(i)).collect()
}
fn uint8s(frames: &[Frame], name: &str) -> Vec<u64> {
column(frames, name)
.into_iter()
.map(|v| match v {
Value::Uint8(n) => n,
other => panic!("column {name} must be Uint8, got {other:?}"),
})
.collect()
}
fn only_uint8(frames: &[Frame], name: &str) -> u64 {
let values = uint8s(frames, name);
assert_eq!(values.len(), 1, "expected exactly one row");
values[0]
}
fn queue_row(t: &TestEngine, name: &str) -> Vec<Frame> {
t.query(&format!(r#"FROM system::queues filter {{ name == "{name}" }}"#))
}
fn partition_rows(t: &TestEngine, queue_id: u64) -> Vec<Frame> {
t.query(&format!("FROM system::queue_partitions filter {{ queue_id == {queue_id} }}"))
}
fn queue_id(t: &TestEngine, name: &str) -> u64 {
only_uint8(&queue_row(t, name), "id")
}
fn claim(t: &TestEngine, worker: &str, max_n: u32) -> Vec<Frame> {
t.command(&format!(r#"CALL queue::claim("{worker}", "test::jobs", {max_n}, duration::seconds(30))"#))
}
fn tokens(frames: &[Frame]) -> Vec<String> {
column(frames, "token")
.into_iter()
.map(|v| match v {
Value::Utf8(t) => t,
other => panic!("token must be Utf8, got {other:?}"),
})
.collect()
}
#[test]
fn test_depth_and_in_flight_follow_every_transition() {
let t = engine_with_queue("CREATE QUEUE test::jobs { id: int4 } WITH { fifo: { partitions: 1 } }");
t.command("INSERT test::jobs [{ id: 1 }, { id: 2 }, { id: 3 }]");
let row = queue_row(&t, "jobs");
assert_eq!(only_uint8(&row, "depth"), 3, "every enqueued item must be counted as waiting");
assert_eq!(only_uint8(&row, "in_flight"), 0, "nothing is claimed yet");
let claimed = claim(&t, "w1", 2);
let tokens = tokens(&claimed);
assert_eq!(tokens.len(), 2);
let row = queue_row(&t, "jobs");
assert_eq!(only_uint8(&row, "depth"), 1, "a claim must move items out of the waiting count");
assert_eq!(only_uint8(&row, "in_flight"), 2, "a claim must move them into the in-flight count");
t.command(&format!(r#"CALL queue::ack("{}")"#, tokens[0]));
let row = queue_row(&t, "jobs");
assert_eq!(only_uint8(&row, "depth"), 1, "an ack must not touch the waiting count");
assert_eq!(only_uint8(&row, "in_flight"), 1, "a completed item must leave the in-flight count");
}
#[test]
fn test_blocked_keys_counts_parked_siblings_only_on_ordered_queues() {
let t = engine_with_queue(
"CREATE QUEUE test::jobs { id: int4, tenant: utf8 } WITH { fifo: { partitions: 1, ordered_by: tenant } }",
);
t.command(
r#"INSERT test::jobs [{ id: 1, tenant: "a" }, { id: 2, tenant: "a" }, { id: 3, tenant: "a" }, { id: 4, tenant: "b" }]"#,
);
let row = queue_row(&t, "jobs");
assert_eq!(only_uint8(&row, "depth"), 4, "parked siblings are still waiting work");
assert_eq!(only_uint8(&row, "blocked_keys"), 1, "tenant a is blocked, tenant b is not");
let t = engine_with_queue("CREATE QUEUE test::plain { id: int4 } WITH { fifo: { partitions: 1 } }");
t.command("INSERT test::plain [{ id: 1 }, { id: 2 }, { id: 3 }]");
let row = queue_row(&t, "plain");
assert_eq!(only_uint8(&row, "depth"), 3);
assert_eq!(only_uint8(&row, "blocked_keys"), 0, "an unkeyed queue can never block a key");
}
#[test]
fn test_partition_rows_sum_to_the_queue_row_and_cover_every_partition() {
let t = engine_with_queue("CREATE QUEUE test::jobs { id: int4 } WITH { fifo: { partitions: 8 } }");
t.command("INSERT test::jobs [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]");
claim(&t, "w1", 2);
let id = queue_id(&t, "jobs");
let rows = partition_rows(&t, id);
let partitions: BTreeSet<u64> = column(&rows, "partition")
.into_iter()
.map(|v| match v {
Value::Uint2(n) => u64::from(n),
other => panic!("partition must be Uint2, got {other:?}"),
})
.collect();
assert_eq!(partitions, (0..8).collect::<BTreeSet<_>>(), "every declared partition must appear exactly once");
let queue = queue_row(&t, "jobs");
assert_eq!(uint8s(&rows, "depth").iter().sum::<u64>(), only_uint8(&queue, "depth"));
assert_eq!(uint8s(&rows, "in_flight").iter().sum::<u64>(), only_uint8(&queue, "in_flight"));
assert_eq!(uint8s(&rows, "blocked_keys").iter().sum::<u64>(), only_uint8(&queue, "blocked_keys"));
assert_eq!(only_uint8(&queue, "in_flight"), 2, "the claim must be visible in the aggregate");
}
#[test]
fn test_oldest_due_at_reports_the_earliest_instant_not_the_latest() {
let t = engine_with_queue("CREATE QUEUE test::jobs { id: int4 } WITH { fifo: { partitions: 1 } }");
t.mock_clock().set_millis(1_000);
t.command(r#"INSERT test::jobs [{ id: 1 }] WITH { not_before: datetime::from_epoch_millis(50000) }"#);
t.command(r#"INSERT test::jobs [{ id: 2 }] WITH { not_before: datetime::from_epoch_millis(20000) }"#);
t.command(r#"INSERT test::jobs [{ id: 3 }] WITH { not_before: datetime::from_epoch_millis(90000) }"#);
let expected_nanos = |millis: u64| Value::DateTime(DateTime::from_nanos(millis * 1_000_000));
assert_eq!(
column(&queue_row(&t, "jobs"), "oldest_due_at")[0],
expected_nanos(20_000),
"oldest_due_at must be the minimum due instant, not the maximum"
);
t.mock_clock().set_millis(20_000);
assert_eq!(TestEngine::row_count(&claim(&t, "w1", 10)), 1, "only the 20s item is due");
assert_eq!(
column(&queue_row(&t, "jobs"), "oldest_due_at")[0],
expected_nanos(50_000),
"claiming the head must advance oldest_due_at to the next pending instant"
);
}
#[test]
fn test_an_empty_queue_reports_zero_counters_and_no_oldest_due_at() {
let t = engine_with_queue("CREATE QUEUE test::jobs { id: int4 } WITH { fifo: { partitions: 4 } }");
let row = queue_row(&t, "jobs");
assert_eq!(only_uint8(&row, "depth"), 0);
assert_eq!(only_uint8(&row, "in_flight"), 0);
assert_eq!(only_uint8(&row, "blocked_keys"), 0);
assert_eq!(
column(&row, "oldest_due_at")[0],
Value::none_of(ValueType::DateTime),
"an empty queue has no oldest due instant"
);
let rows = partition_rows(&t, queue_id(&t, "jobs"));
assert_eq!(uint8s(&rows, "depth").len(), 4, "an untouched partition still gets a row");
assert!(uint8s(&rows, "depth").iter().all(|d| *d == 0));
}
#[test]
fn test_a_dropped_queue_leaves_no_partition_rows_behind() {
let t = engine_with_queue("CREATE QUEUE test::jobs { id: int4 } WITH { fifo: { partitions: 2 } }");
t.command("INSERT test::jobs [{ id: 1 }]");
let id = queue_id(&t, "jobs");
assert_eq!(uint8s(&partition_rows(&t, id), "depth").len(), 2);
t.admin("DROP QUEUE test::jobs");
assert!(uint8s(&partition_rows(&t, id), "depth").is_empty(), "partition rows must not outlive the queue");
}