mentat/tests/query.rs
Richard Newman daca8def57 UUIDs and instants. Fixes #44, #45, #426, #427. (#438) r=nalexander
* Pre: unused import in translate.rs.

* Part 2: take a dependency on rusqlite for query arguments.

* Part 1: flatten V2 schema into V1. Add UUID and URI.

Bump expected ident and bootstrap datom count in tests.

* Part 5: parse edn::Value::Uuid.

* Part 3: extend ValueType and TypedValue to include Uuid.

* Part 4: add Uuid to query arguments.

* Part 6: extend db to support Uuid.

* Part 8: add a tx-parser test for #f NaN and #uuid.

* Part 7: parse and algebrize UUIDs in queries.

* Part 1: parse #inst in EDN and throughout query engine.

* Part 3: handle instants in db.

* Part 2: instants never matches integers in queries.

* Part 4: use DateTime for tx_instants.

* Add a test for adding and querying UUIDs and instants.

* Review comments.
2017-04-28 20:11:55 -07:00

257 lines
8.5 KiB
Rust

// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
extern crate chrono;
extern crate time;
extern crate mentat;
extern crate mentat_core;
extern crate mentat_db;
use std::str::FromStr;
use chrono::FixedOffset;
use mentat_core::{
TypedValue,
ValueType,
UTC,
Uuid,
};
use mentat::{
NamespacedKeyword,
QueryInputs,
QueryResults,
Variable,
new_connection,
q_once,
};
use mentat::conn::Conn;
use mentat::errors::{
Error,
ErrorKind,
};
#[test]
fn test_rel() {
let mut c = new_connection("").expect("Couldn't open conn.");
let db = mentat_db::db::ensure_current_version(&mut c).expect("Couldn't open DB.");
// Rel.
let start = time::PreciseTime::now();
let results = q_once(&c, &db.schema,
"[:find ?x ?ident :where [?x :db/ident ?ident]]", None)
.expect("Query failed");
let end = time::PreciseTime::now();
// This will need to change each time we add a default ident.
assert_eq!(39, results.len());
// Every row is a pair of a Ref and a Keyword.
if let QueryResults::Rel(ref rel) = results {
for r in rel {
assert_eq!(r.len(), 2);
assert!(r[0].matches_type(ValueType::Ref));
assert!(r[1].matches_type(ValueType::Keyword));
}
} else {
panic!("Expected rel.");
}
println!("{:?}", results);
println!("Rel took {}µs", start.to(end).num_microseconds().unwrap());
}
#[test]
fn test_failing_scalar() {
let mut c = new_connection("").expect("Couldn't open conn.");
let db = mentat_db::db::ensure_current_version(&mut c).expect("Couldn't open DB.");
// Scalar that fails.
let start = time::PreciseTime::now();
let results = q_once(&c, &db.schema,
"[:find ?x . :where [?x :db/fulltext true]]", None)
.expect("Query failed");
let end = time::PreciseTime::now();
assert_eq!(0, results.len());
if let QueryResults::Scalar(None) = results {
} else {
panic!("Expected failed scalar.");
}
println!("Failing scalar took {}µs", start.to(end).num_microseconds().unwrap());
}
#[test]
fn test_scalar() {
let mut c = new_connection("").expect("Couldn't open conn.");
let db = mentat_db::db::ensure_current_version(&mut c).expect("Couldn't open DB.");
// Scalar that succeeds.
let start = time::PreciseTime::now();
let results = q_once(&c, &db.schema,
"[:find ?ident . :where [24 :db/ident ?ident]]", None)
.expect("Query failed");
let end = time::PreciseTime::now();
assert_eq!(1, results.len());
if let QueryResults::Scalar(Some(TypedValue::Keyword(ref rc))) = results {
// Should be '24'.
assert_eq!(&NamespacedKeyword::new("db.type", "keyword"), rc.as_ref());
assert_eq!(24,
db.schema.get_entid(rc).unwrap());
} else {
panic!("Expected scalar.");
}
println!("{:?}", results);
println!("Scalar took {}µs", start.to(end).num_microseconds().unwrap());
}
#[test]
fn test_tuple() {
let mut c = new_connection("").expect("Couldn't open conn.");
let db = mentat_db::db::ensure_current_version(&mut c).expect("Couldn't open DB.");
// Tuple.
let start = time::PreciseTime::now();
let results = q_once(&c, &db.schema,
"[:find [?index ?cardinality]
:where [:db/txInstant :db/index ?index]
[:db/txInstant :db/cardinality ?cardinality]]",
None)
.expect("Query failed");
let end = time::PreciseTime::now();
assert_eq!(1, results.len());
if let QueryResults::Tuple(Some(ref tuple)) = results {
let cardinality_one = NamespacedKeyword::new("db.cardinality", "one");
assert_eq!(tuple.len(), 2);
assert_eq!(tuple[0], TypedValue::Boolean(true));
assert_eq!(tuple[1], TypedValue::Ref(db.schema.get_entid(&cardinality_one).unwrap()));
} else {
panic!("Expected tuple.");
}
println!("{:?}", results);
println!("Tuple took {}µs", start.to(end).num_microseconds().unwrap());
}
#[test]
fn test_coll() {
let mut c = new_connection("").expect("Couldn't open conn.");
let db = mentat_db::db::ensure_current_version(&mut c).expect("Couldn't open DB.");
// Coll.
let start = time::PreciseTime::now();
let results = q_once(&c, &db.schema,
"[:find [?e ...] :where [?e :db/ident _]]", None)
.expect("Query failed");
let end = time::PreciseTime::now();
assert_eq!(39, results.len());
if let QueryResults::Coll(ref coll) = results {
assert!(coll.iter().all(|item| item.matches_type(ValueType::Ref)));
} else {
panic!("Expected coll.");
}
println!("{:?}", results);
println!("Coll took {}µs", start.to(end).num_microseconds().unwrap());
}
#[test]
fn test_inputs() {
let mut c = new_connection("").expect("Couldn't open conn.");
let db = mentat_db::db::ensure_current_version(&mut c).expect("Couldn't open DB.");
// entids::DB_INSTALL_VALUE_TYPE = 5.
let ee = (Variable::from_valid_name("?e"), TypedValue::Ref(5));
let inputs = QueryInputs::with_value_sequence(vec![ee]);
let results = q_once(&c, &db.schema,
"[:find ?i . :in ?e :where [?e :db/ident ?i]]", inputs)
.expect("query to succeed");
if let QueryResults::Scalar(Some(TypedValue::Keyword(value))) = results {
assert_eq!(value.as_ref(), &NamespacedKeyword::new("db.install", "valueType"));
} else {
panic!("Expected scalar.");
}
}
/// Ensure that a query won't be run without all of its `:in` variables being bound.
#[test]
fn test_unbound_inputs() {
let mut c = new_connection("").expect("Couldn't open conn.");
let db = mentat_db::db::ensure_current_version(&mut c).expect("Couldn't open DB.");
// Bind the wrong var by 'mistake'.
let xx = (Variable::from_valid_name("?x"), TypedValue::Ref(5));
let inputs = QueryInputs::with_value_sequence(vec![xx]);
let results = q_once(&c, &db.schema,
"[:find ?i . :in ?e :where [?e :db/ident ?i]]", inputs);
match results {
Result::Err(Error(ErrorKind::UnboundVariables(vars), _)) => {
assert_eq!(vars, vec!["?e".to_string()].into_iter().collect());
},
_ => panic!("Expected unbound variables."),
}
}
#[test]
fn test_instants_and_uuids() {
// We assume, perhaps foolishly, that the clocks on test machines won't lose more than an
// hour while this test is running.
let start = UTC::now() + FixedOffset::west(60 * 60);
let mut c = new_connection("").expect("Couldn't open conn.");
let mut conn = Conn::connect(&mut c).expect("Couldn't open DB.");
conn.transact(&mut c, r#"[
[:db/add "s" :db/ident :foo/uuid]
[:db/add "s" :db/valueType :db.type/uuid]
[:db/add "s" :db/cardinality :db.cardinality/one]
]"#).unwrap();
conn.transact(&mut c, r#"[
[:db/add "u" :foo/uuid #uuid "cf62d552-6569-4d1b-b667-04703041dfc4"]
]"#).unwrap();
// We don't yet support getting the tx from a pattern (#440), so run wild.
let r = conn.q_once(&mut c,
r#"[:find [?x ?u ?when]
:where [?x :foo/uuid ?u]
[?tx :db/txInstant ?when]]"#, None);
match r {
Result::Ok(QueryResults::Tuple(Some(vals))) => {
let mut vals = vals.into_iter();
match (vals.next(), vals.next(), vals.next(), vals.next()) {
(Some(TypedValue::Ref(e)),
Some(TypedValue::Uuid(u)),
Some(TypedValue::Instant(t)),
None) => {
assert!(e > 39); // There are at least this many entities in the store.
assert_eq!(Ok(u), Uuid::from_str("cf62d552-6569-4d1b-b667-04703041dfc4"));
assert!(t > start);
},
_ => panic!("Unexpected results."),
}
},
_ => panic!("Expected query to work."),
}
}