use std::panic::{AssertUnwindSafe, catch_unwind};
use reifydb_core::execution::ExecutionResult;
use reifydb_test_harness::engine::TestEngine;
use reifydb_value::{error::Diagnostic, params::Params};
const WINDOW: &str = "FROM test::t | window rolling { total: math::sum(v) } with { duration: 1h } by { g }";
fn setup() -> TestEngine {
let t = TestEngine::new();
t.admin("CREATE NAMESPACE test");
t.admin("CREATE TABLE test::t { g: int4, v: float8, ts: datetime } with { time: event(ts) }");
t.command("INSERT test::t [{ g: 1, v: 1.0, ts: \"2026-01-01T00:00:00Z\" }]");
t
}
fn without_panic(rql: &str, run: impl FnOnce() -> ExecutionResult) -> ExecutionResult {
catch_unwind(AssertUnwindSafe(run)).unwrap_or_else(|payload| {
let message = payload
.downcast_ref::<String>()
.cloned()
.or_else(|| payload.downcast_ref::<&str>().map(|s| s.to_string()))
.unwrap_or_default();
panic!("{rql} panicked instead of returning an error: {message}")
})
}
fn assert_window_rejected(t: &TestEngine, rql: &str) {
let result = without_panic(rql, || t.inner().admin_as(TestEngine::identity(), rql, Params::None));
let Some(err) = result.error else {
panic!("{rql} runs a window outside a deferred view, so it must be an error, got {:?}", result.frames);
};
let diagnostic: Diagnostic = err.diagnostic();
assert_eq!(
diagnostic.code, "QUERY_007",
"{rql} must be refused as a window in a batch query, got: {diagnostic:?}"
);
assert!(diagnostic.message.contains("window"), "the error must name window, got: {diagnostic:?}");
}
#[test]
fn a_let_bound_window_reports_an_error_instead_of_panicking() {
let t = setup();
assert_window_rejected(&t, &format!("let $w = {WINDOW}; FROM $w"));
}
#[test]
fn a_window_in_an_if_branch_reports_an_error_instead_of_panicking() {
let t = setup();
assert_window_rejected(&t, &format!("if true {{ {WINDOW} }}"));
}
#[test]
fn a_window_in_a_for_iterable_reports_an_error_instead_of_panicking() {
let t = setup();
assert_window_rejected(&t, &format!("let $n = 0; for $r in {{ {WINDOW} }} {{ $n = $n + 1 }}; map {{ n: $n }}"));
}
#[test]
fn a_window_as_inner_join_input_reports_an_error_instead_of_panicking() {
let t = setup();
assert_window_rejected(&t, &format!("FROM test::t INNER JOIN {{ {WINDOW} }} AS s USING (g, s.g)"));
}
#[test]
fn a_window_as_left_join_input_reports_an_error_instead_of_panicking() {
let t = setup();
assert_window_rejected(&t, &format!("FROM test::t LEFT JOIN {{ {WINDOW} }} AS s USING (g, s.g)"));
}
#[test]
fn a_window_as_natural_join_input_reports_an_error_instead_of_panicking() {
let t = setup();
assert_window_rejected(&t, &format!("FROM test::t NATURAL JOIN {{ {WINDOW} }} AS s"));
}
#[test]
fn a_window_in_a_procedure_body_reports_an_error_instead_of_panicking() {
let t = setup();
t.admin(
"CREATE PROCEDURE test::windowed AS { FROM test::t window rolling { total: math::sum(v) } with { duration: 1h } by { g } }",
);
assert_window_rejected(&t, "CALL test::windowed()");
}
#[test]
fn a_window_in_a_udf_body_reports_an_error_instead_of_panicking() {
let t = setup();
assert_window_rejected(
&t,
&format!(
"UDF windowed ($x: int4) {{ let $w = {WINDOW}; RETURN $x }}; FROM test::t MAP {{ r: windowed(g) }}"
),
);
}
#[test]
fn a_window_without_input_reports_an_error_instead_of_panicking() {
let t = setup();
assert_window_rejected(&t, "window rolling { total: math::sum(v) } with { duration: 1h } by { g }");
}
#[test]
fn a_window_in_a_test_body_reports_an_error_outcome_instead_of_panicking() {
let t = setup();
t.admin(&format!("CREATE TEST test::windowed {{ {WINDOW} }}"));
let result = without_panic("RUN TESTS test", || {
t.inner().admin_as(TestEngine::identity(), "RUN TESTS test", Params::None)
});
assert!(result.error.is_none(), "a failing test body must not fail RUN TESTS itself, got: {:?}", result.error);
let rows: Vec<_> = result.frames[0].rows().collect();
assert_eq!(rows.len(), 1, "exactly the one test must run, got: {}", result.frames[0]);
assert_eq!(rows[0].get::<String>("outcome").unwrap().unwrap(), "error", "got: {}", result.frames[0]);
let message = rows[0].get::<String>("message").unwrap().unwrap();
assert!(message.contains("QUERY_007"), "the test outcome must carry the window refusal, got: {message}");
}