96 lines
2.7 KiB
Rust
96 lines
2.7 KiB
Rust
|
|
use query_engine::chase::materialize;
|
||
|
|
use query_engine::chase::rule::RuleBuilder;
|
||
|
|
use query_engine::frontend::Session;
|
||
|
|
use query_engine::{Atom, Instance, Term, chase};
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn existential_trigger_stays_inactive_when_head_is_already_satisfied() {
|
||
|
|
let instance: Instance = vec![
|
||
|
|
Atom::new("Person", vec![Term::constant("alice")]),
|
||
|
|
Atom::new(
|
||
|
|
"HasSSN",
|
||
|
|
vec![Term::constant("alice"), Term::constant("known")],
|
||
|
|
),
|
||
|
|
]
|
||
|
|
.into_iter()
|
||
|
|
.collect();
|
||
|
|
|
||
|
|
let rule = RuleBuilder::new()
|
||
|
|
.when("Person", vec![Term::var("X")])
|
||
|
|
.then("HasSSN", vec![Term::var("X"), Term::var("Y")])
|
||
|
|
.build();
|
||
|
|
|
||
|
|
let result = chase(instance, &[rule]);
|
||
|
|
|
||
|
|
assert!(result.terminated);
|
||
|
|
assert_eq!(result.steps, 0);
|
||
|
|
assert_eq!(result.instance.facts_for_predicate("HasSSN").len(), 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn fresh_nulls_skip_existing_input_null_ids() {
|
||
|
|
let instance: Instance = vec![
|
||
|
|
Atom::new("Seed", vec![Term::null(0)]),
|
||
|
|
Atom::new("Person", vec![Term::constant("alice")]),
|
||
|
|
]
|
||
|
|
.into_iter()
|
||
|
|
.collect();
|
||
|
|
|
||
|
|
let rule = RuleBuilder::new()
|
||
|
|
.when("Person", vec![Term::var("X")])
|
||
|
|
.then("HasSSN", vec![Term::var("X"), Term::var("Y")])
|
||
|
|
.build();
|
||
|
|
|
||
|
|
let result = chase(instance, &[rule]);
|
||
|
|
|
||
|
|
assert!(result.terminated);
|
||
|
|
assert_eq!(result.instance.facts_for_predicate("HasSSN").len(), 1);
|
||
|
|
|
||
|
|
let fact = result.instance.facts_for_predicate("HasSSN")[0];
|
||
|
|
assert!(matches!(fact.terms[1], Term::Null(id) if id > 0));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn frontend_materialization_respects_existing_existential_witnesses() {
|
||
|
|
let mut session = Session::new();
|
||
|
|
let output = session
|
||
|
|
.execute_script(
|
||
|
|
"fact Person(alice).\n\
|
||
|
|
fact HasSSN(alice, known).\n\
|
||
|
|
rule Person(?X) -> HasSSN(?X, ?Y).\n\
|
||
|
|
run.\n\
|
||
|
|
query HasSSN(alice, ?Y)?",
|
||
|
|
)
|
||
|
|
.unwrap();
|
||
|
|
|
||
|
|
assert!(output.contains("1 row(s)"));
|
||
|
|
assert!(output.contains("?Y = known"));
|
||
|
|
assert!(!output.contains("⊥"));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn frontend_materialization_starts_after_existing_null_ids() {
|
||
|
|
let instance: Instance = vec![
|
||
|
|
Atom::new("Seed", vec![Term::null(7)]),
|
||
|
|
Atom::new("Employee", vec![Term::constant("alice")]),
|
||
|
|
]
|
||
|
|
.into_iter()
|
||
|
|
.collect();
|
||
|
|
|
||
|
|
let rule = RuleBuilder::new()
|
||
|
|
.when("Employee", vec![Term::var("X")])
|
||
|
|
.then("WorksIn", vec![Term::var("X"), Term::var("Dept")])
|
||
|
|
.build();
|
||
|
|
|
||
|
|
let state = materialize(instance, &[rule]);
|
||
|
|
|
||
|
|
assert!(state.result.terminated);
|
||
|
|
assert_eq!(
|
||
|
|
state.result.instance.facts_for_predicate("WorksIn").len(),
|
||
|
|
1
|
||
|
|
);
|
||
|
|
|
||
|
|
let fact = state.result.instance.facts_for_predicate("WorksIn")[0];
|
||
|
|
assert!(matches!(fact.terms[1], Term::Null(id) if id > 7));
|
||
|
|
}
|