|
@@ -1,1048 +0,0 @@
|
|
|
-#![allow(unused)]
|
|
|
-
|
|
|
-fn open_test_db<DB: super::Database>(identifier: &'static str) -> DB {
|
|
|
- let path = format!("/tmp/microrm-{identifier}.db");
|
|
|
- let _ = std::fs::remove_file(path.as_str());
|
|
|
- DB::open_path(path).expect("couldn't open database file")
|
|
|
-}
|
|
|
-
|
|
|
-mod manual_test_db {
|
|
|
- // simple hand-built database example
|
|
|
- use crate::db::{Connection, StatementContext, StatementRow};
|
|
|
- use crate::schema::datum::{ConcreteDatum, Datum};
|
|
|
- use crate::schema::entity::{
|
|
|
- Entity, EntityID, EntityPart, EntityPartList, EntityPartVisitor, EntityVisitor,
|
|
|
- };
|
|
|
- use crate::schema::{Database, DatabaseItem, DatabaseItemVisitor, IDMap};
|
|
|
- use test_log::test;
|
|
|
-
|
|
|
- #[derive(Debug)]
|
|
|
- struct SimpleEntity {
|
|
|
- name: String,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Clone, Copy, Default, PartialEq, PartialOrd, Hash, Debug)]
|
|
|
- struct SimpleEntityID(i64);
|
|
|
-
|
|
|
- impl Datum for SimpleEntityID {
|
|
|
- fn sql_type() -> &'static str {
|
|
|
- "int"
|
|
|
- }
|
|
|
- fn accept_entity_visitor(_: &mut impl EntityVisitor) {}
|
|
|
- fn accept_discriminator(d: &mut impl crate::schema::DatumDiscriminator)
|
|
|
- where
|
|
|
- Self: Sized,
|
|
|
- {
|
|
|
- d.visit_entity_id::<<Self as EntityID>::Entity>();
|
|
|
- }
|
|
|
-
|
|
|
- fn bind_to<'a>(&self, _stmt: &mut StatementContext<'a>, index: i32) {
|
|
|
- todo!()
|
|
|
- }
|
|
|
- fn build_from<'a>(
|
|
|
- rdata: crate::schema::relation::RelationData,
|
|
|
- stmt: &mut StatementRow,
|
|
|
- index: &mut i32,
|
|
|
- ) -> crate::DBResult<Self>
|
|
|
- where
|
|
|
- Self: Sized,
|
|
|
- {
|
|
|
- todo!()
|
|
|
- }
|
|
|
- }
|
|
|
- impl ConcreteDatum for SimpleEntityID {}
|
|
|
-
|
|
|
- impl EntityID for SimpleEntityID {
|
|
|
- type Entity = SimpleEntity;
|
|
|
-
|
|
|
- fn from_raw(id: i64) -> Self {
|
|
|
- Self(id)
|
|
|
- }
|
|
|
- fn into_raw(self) -> i64 {
|
|
|
- self.0
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- impl EntityPart for SimpleEntityID {
|
|
|
- type Datum = Self;
|
|
|
- type Entity = SimpleEntity;
|
|
|
-
|
|
|
- fn unique() -> bool {
|
|
|
- true
|
|
|
- }
|
|
|
- fn part_name() -> &'static str {
|
|
|
- "id"
|
|
|
- }
|
|
|
- fn desc() -> Option<&'static str> {
|
|
|
- None
|
|
|
- }
|
|
|
-
|
|
|
- fn get_datum(from: &Self::Entity) -> &Self::Datum {
|
|
|
- unreachable!()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Clone, Default)]
|
|
|
- struct SimpleEntityName;
|
|
|
- impl EntityPart for SimpleEntityName {
|
|
|
- type Datum = String;
|
|
|
- type Entity = SimpleEntity;
|
|
|
- fn part_name() -> &'static str {
|
|
|
- "name"
|
|
|
- }
|
|
|
- fn unique() -> bool {
|
|
|
- true
|
|
|
- }
|
|
|
- fn desc() -> Option<&'static str> {
|
|
|
- None
|
|
|
- }
|
|
|
-
|
|
|
- fn get_datum(from: &Self::Entity) -> &Self::Datum {
|
|
|
- &from.name
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- impl Entity for SimpleEntity {
|
|
|
- type Parts = SimpleEntityName;
|
|
|
- type Keys = SimpleEntityName;
|
|
|
- type ID = SimpleEntityID;
|
|
|
-
|
|
|
- fn build(name: String) -> Self {
|
|
|
- Self { name }
|
|
|
- }
|
|
|
-
|
|
|
- fn entity_name() -> &'static str {
|
|
|
- "simple_entity"
|
|
|
- }
|
|
|
- fn accept_part_visitor(visitor: &mut impl EntityPartVisitor<Entity = Self>) {
|
|
|
- visitor.visit::<SimpleEntityName>();
|
|
|
- }
|
|
|
- fn accept_part_visitor_ref(&self, visitor: &mut impl EntityPartVisitor<Entity = Self>) {
|
|
|
- visitor.visit_datum::<SimpleEntityName>(&self.name);
|
|
|
- }
|
|
|
- fn accept_part_visitor_mut(&mut self, visitor: &mut impl EntityPartVisitor<Entity = Self>) {
|
|
|
- visitor.visit_datum_mut::<SimpleEntityName>(&mut self.name);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- struct SimpleDatabase {
|
|
|
- strings: IDMap<SimpleEntity>,
|
|
|
- }
|
|
|
-
|
|
|
- impl Database for SimpleDatabase {
|
|
|
- fn build(conn: Connection) -> Self
|
|
|
- where
|
|
|
- Self: Sized,
|
|
|
- {
|
|
|
- Self {
|
|
|
- strings: IDMap::build(conn),
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fn accept_item_visitor(visitor: &mut impl DatabaseItemVisitor)
|
|
|
- where
|
|
|
- Self: Sized,
|
|
|
- {
|
|
|
- <IDMap<SimpleEntity> as DatabaseItem>::accept_item_visitor(visitor);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn part_visitor() {
|
|
|
- struct V<E: Entity> {
|
|
|
- v: Vec<std::any::TypeId>,
|
|
|
- _ghost: std::marker::PhantomData<E>,
|
|
|
- }
|
|
|
- impl<E: Entity> EntityPartVisitor for V<E> {
|
|
|
- type Entity = E;
|
|
|
- fn visit<EP: EntityPart>(&mut self) {
|
|
|
- self.v.push(std::any::TypeId::of::<EP>());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- let mut vis = V {
|
|
|
- v: vec![],
|
|
|
- _ghost: Default::default(),
|
|
|
- };
|
|
|
- SimpleEntity::accept_part_visitor(&mut vis);
|
|
|
- assert_eq!(
|
|
|
- vis.v.as_slice(),
|
|
|
- &[std::any::TypeId::of::<SimpleEntityName>()]
|
|
|
- );
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-mod derive_tests {
|
|
|
- #![allow(unused)]
|
|
|
-
|
|
|
- use crate::prelude::*;
|
|
|
- use crate::schema::{Database, IDMap};
|
|
|
- use microrm_macros::{Database, Entity};
|
|
|
- use test_log::test;
|
|
|
-
|
|
|
- #[derive(Entity)]
|
|
|
- struct Role {
|
|
|
- title: String,
|
|
|
- permissions: String,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Entity)]
|
|
|
- struct Person {
|
|
|
- #[key]
|
|
|
- name: String,
|
|
|
- roles: microrm::RelationMap<Role>,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Database)]
|
|
|
- struct PeopleDB {
|
|
|
- people: IDMap<Person>,
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn collect_test() {
|
|
|
- microrm::schema::build::collect_from_database::<PeopleDB>();
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn open_test() {
|
|
|
- PeopleDB::open_path(":memory:");
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn check_for_inserted() {
|
|
|
- std::fs::remove_file("/tmp/schema.db").ok();
|
|
|
- let db = PeopleDB::open_path("/tmp/schema.db").expect("couldn't open database");
|
|
|
- // let db = PeopleDB::open_path(":memory:").expect("couldn't open database");
|
|
|
- let name_string = "name_here".to_string();
|
|
|
- // check that it isn't in the database before we insert it
|
|
|
- assert!(db.people.keyed(&name_string).get().ok().flatten().is_none());
|
|
|
-
|
|
|
- db.people
|
|
|
- .insert(Person {
|
|
|
- name: name_string.clone(),
|
|
|
- roles: Default::default(),
|
|
|
- })
|
|
|
- .expect("failed to insert");
|
|
|
-
|
|
|
- // check that it is in the database after we insert it
|
|
|
- assert!(db.people.keyed(&name_string).get().ok().flatten().is_some());
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn check_relation_query_construction() {
|
|
|
- let db = PeopleDB::open_path(":memory:").expect("couldn't open database");
|
|
|
-
|
|
|
- let name_string = "name_here".to_string();
|
|
|
- db.people
|
|
|
- .insert(Person {
|
|
|
- name: name_string.clone(),
|
|
|
- roles: Default::default(),
|
|
|
- })
|
|
|
- .expect("couldn't insert test person");
|
|
|
-
|
|
|
- let person = db
|
|
|
- .people
|
|
|
- .keyed(&name_string)
|
|
|
- .get()
|
|
|
- .ok()
|
|
|
- .flatten()
|
|
|
- .expect("couldn't re-get test person entity");
|
|
|
-
|
|
|
- person
|
|
|
- .roles
|
|
|
- .get()
|
|
|
- .expect("couldn't get related role entity");
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn check_relation_insertion() {
|
|
|
- std::fs::remove_file("/tmp/insert.db").ok();
|
|
|
- let db = PeopleDB::open_path("/tmp/insert.db").expect("couldn't open database");
|
|
|
- // let db = PeopleDB::open_path(":memory:").expect("couldn't open database");
|
|
|
-
|
|
|
- let name_string = "name_here".to_string();
|
|
|
- db.people
|
|
|
- .insert(Person {
|
|
|
- name: name_string.clone(),
|
|
|
- roles: Default::default(),
|
|
|
- })
|
|
|
- .expect("couldn't insert test person");
|
|
|
-
|
|
|
- let person = db
|
|
|
- .people
|
|
|
- .keyed(&name_string)
|
|
|
- .get()
|
|
|
- .ok()
|
|
|
- .flatten()
|
|
|
- .expect("couldn't re-get test person entity");
|
|
|
-
|
|
|
- person.roles.insert(Role {
|
|
|
- title: "title A".to_string(),
|
|
|
- permissions: "permissions A".to_string(),
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn delete_test() {
|
|
|
- let db = PeopleDB::open_path(":memory:").expect("couldn't open test db");
|
|
|
-
|
|
|
- let id = db
|
|
|
- .people
|
|
|
- .insert(Person {
|
|
|
- name: "person_name".to_string(),
|
|
|
- roles: Default::default(),
|
|
|
- })
|
|
|
- .expect("couldn't insert test person");
|
|
|
- assert!(db.people.by_id(id).expect("couldn't query db").is_some());
|
|
|
-
|
|
|
- db.people.with(id, &id).delete();
|
|
|
-
|
|
|
- assert!(db.people.by_id(id).expect("couldn't query db").is_none());
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn update_test() {
|
|
|
- let db = PeopleDB::open_path(":memory:").expect("couldn't open test db");
|
|
|
-
|
|
|
- let mut stored = db
|
|
|
- .people
|
|
|
- .insert_and_return(Person {
|
|
|
- name: "person_name".to_string(),
|
|
|
- roles: Default::default(),
|
|
|
- })
|
|
|
- .expect("couldn't insert test person");
|
|
|
-
|
|
|
- assert_eq!(
|
|
|
- db.people
|
|
|
- .with(Person::Name, "person_name")
|
|
|
- .count()
|
|
|
- .expect("couldn't execute count query"),
|
|
|
- 1
|
|
|
- );
|
|
|
-
|
|
|
- stored.name = "another_person_name".into();
|
|
|
- stored.sync();
|
|
|
-
|
|
|
- assert_eq!(
|
|
|
- db.people
|
|
|
- .with(Person::Name, "person_name")
|
|
|
- .count()
|
|
|
- .expect("couldn't execute count query"),
|
|
|
- 0
|
|
|
- );
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-mod mutual_relationship {
|
|
|
- use super::open_test_db;
|
|
|
- use crate::prelude::*;
|
|
|
- use microrm_macros::{Database, Entity};
|
|
|
- use test_log::test;
|
|
|
-
|
|
|
- struct CR;
|
|
|
- impl Relation for CR {
|
|
|
- type Domain = Customer;
|
|
|
- type Range = Receipt;
|
|
|
-
|
|
|
- const NAME: &'static str = "CR";
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Entity)]
|
|
|
- struct Customer {
|
|
|
- name: String,
|
|
|
- receipts: microrm::RelationDomain<CR>,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Entity)]
|
|
|
- struct Receipt {
|
|
|
- value: usize,
|
|
|
- customers: microrm::RelationRange<CR>,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Database)]
|
|
|
- struct ReceiptDB {
|
|
|
- customers: microrm::IDMap<Customer>,
|
|
|
- receipts: microrm::IDMap<Receipt>,
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn check_schema_creation() {
|
|
|
- let db = open_test_db::<ReceiptDB>("mutual_relationship_create");
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn simple_operations() {
|
|
|
- let db = open_test_db::<ReceiptDB>("mutual_relationship_simple");
|
|
|
-
|
|
|
- let ca = db
|
|
|
- .customers
|
|
|
- .insert(Customer {
|
|
|
- name: "customer A".to_string(),
|
|
|
- receipts: Default::default(),
|
|
|
- })
|
|
|
- .expect("couldn't insert customer record");
|
|
|
-
|
|
|
- let ra = db
|
|
|
- .receipts
|
|
|
- .insert(Receipt {
|
|
|
- value: 32usize,
|
|
|
- customers: Default::default(),
|
|
|
- })
|
|
|
- .expect("couldn't insert receipt record");
|
|
|
-
|
|
|
- let rb = db
|
|
|
- .receipts
|
|
|
- .insert(Receipt {
|
|
|
- value: 64usize,
|
|
|
- customers: Default::default(),
|
|
|
- })
|
|
|
- .expect("couldn't insert receipt record");
|
|
|
-
|
|
|
- let e_ca = db
|
|
|
- .customers
|
|
|
- .by_id(ca)
|
|
|
- .expect("couldn't retrieve customer record")
|
|
|
- .expect("no customer record");
|
|
|
-
|
|
|
- println!(
|
|
|
- "connecting customer a (ID {:?}) to receipt a (ID {:?})",
|
|
|
- ca, ra
|
|
|
- );
|
|
|
- e_ca.receipts
|
|
|
- .connect_to(ra)
|
|
|
- .expect("couldn't relationiate customer with receipt a");
|
|
|
- println!(
|
|
|
- "connecting customer a (ID {:?}) to receipt b (ID {:?})",
|
|
|
- ca, rb
|
|
|
- );
|
|
|
- e_ca.receipts
|
|
|
- .connect_to(rb)
|
|
|
- .expect("couldn't relationiate customer with receipt b");
|
|
|
-
|
|
|
- println!("connected!");
|
|
|
-
|
|
|
- // technically this can fail if sqlite gives ra and rb back in the opposite order, which is
|
|
|
- // valid behaviour
|
|
|
- assert_eq!(
|
|
|
- e_ca.receipts
|
|
|
- .get()
|
|
|
- .expect("couldn't get receipts related with customer")
|
|
|
- .into_iter()
|
|
|
- .map(|x| x.id())
|
|
|
- .collect::<Vec<_>>(),
|
|
|
- vec![ra, rb]
|
|
|
- );
|
|
|
-
|
|
|
- // check that the reverse direction was also added
|
|
|
- let e_ra = db
|
|
|
- .receipts
|
|
|
- .by_id(ra)
|
|
|
- .expect("couldn't retreieve receipt record")
|
|
|
- .expect("no receipt record");
|
|
|
-
|
|
|
- assert_eq!(
|
|
|
- e_ra.customers
|
|
|
- .get()
|
|
|
- .expect("couldn't get related customers")
|
|
|
- .into_iter()
|
|
|
- .map(|x| x.id())
|
|
|
- .collect::<Vec<_>>(),
|
|
|
- vec![ca]
|
|
|
- );
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-mod reserved_words {
|
|
|
- use crate::db::Connection;
|
|
|
- use crate::prelude::*;
|
|
|
- use crate::schema::entity::Entity;
|
|
|
- use test_log::test;
|
|
|
-
|
|
|
- #[derive(Entity)]
|
|
|
- struct Select {
|
|
|
- delete: String,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Entity)]
|
|
|
- struct Group {
|
|
|
- by: microrm::RelationMap<Select>,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Database)]
|
|
|
- struct ReservedWordDB {
|
|
|
- group: microrm::IDMap<Group>,
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn open_test() {
|
|
|
- ReservedWordDB::open_path(":memory:");
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-mod join_test {
|
|
|
- use super::open_test_db;
|
|
|
- use crate::prelude::*;
|
|
|
- use crate::schema;
|
|
|
- use test_log::test;
|
|
|
-
|
|
|
- #[derive(Default, Entity)]
|
|
|
- struct Base {
|
|
|
- name: String,
|
|
|
- targets: microrm::RelationMap<Target>,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Default, Entity)]
|
|
|
- struct Target {
|
|
|
- name: String,
|
|
|
- indirect_targets: microrm::RelationMap<IndirectTarget>,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Default, Entity)]
|
|
|
- struct IndirectTarget {
|
|
|
- name: String,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Database)]
|
|
|
- struct JoinDB {
|
|
|
- bases: microrm::IDMap<Base>,
|
|
|
- targets: microrm::IDMap<Target>,
|
|
|
- indirect: microrm::IDMap<IndirectTarget>,
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn simple_join() {
|
|
|
- let db = open_test_db::<JoinDB>("simple_join_test");
|
|
|
-
|
|
|
- let b1id = db
|
|
|
- .bases
|
|
|
- .insert(Base {
|
|
|
- name: "base1".to_string(),
|
|
|
- targets: Default::default(),
|
|
|
- })
|
|
|
- .expect("couldn't insert base");
|
|
|
- let b2id = db
|
|
|
- .bases
|
|
|
- .insert(Base {
|
|
|
- name: "base2".to_string(),
|
|
|
- targets: Default::default(),
|
|
|
- })
|
|
|
- .expect("couldn't insert base");
|
|
|
- let b3id = db
|
|
|
- .bases
|
|
|
- .insert(Base {
|
|
|
- name: "base3".to_string(),
|
|
|
- targets: Default::default(),
|
|
|
- })
|
|
|
- .expect("couldn't insert base");
|
|
|
-
|
|
|
- let t1id = db
|
|
|
- .targets
|
|
|
- .insert(Target {
|
|
|
- name: "target1".to_string(),
|
|
|
- ..Default::default()
|
|
|
- })
|
|
|
- .expect("couldn't insert target");
|
|
|
- let t2id = db
|
|
|
- .targets
|
|
|
- .insert(Target {
|
|
|
- name: "target2".to_string(),
|
|
|
- ..Default::default()
|
|
|
- })
|
|
|
- .expect("couldn't insert target");
|
|
|
- let t3id = db
|
|
|
- .targets
|
|
|
- .insert(Target {
|
|
|
- name: "target3".to_string(),
|
|
|
- ..Default::default()
|
|
|
- })
|
|
|
- .expect("couldn't insert target");
|
|
|
-
|
|
|
- let it1id = db
|
|
|
- .indirect
|
|
|
- .insert(IndirectTarget {
|
|
|
- name: "itarget1".to_string(),
|
|
|
- ..Default::default()
|
|
|
- })
|
|
|
- .expect("couldn't insert target");
|
|
|
- let it2id = db
|
|
|
- .indirect
|
|
|
- .insert(IndirectTarget {
|
|
|
- name: "itarget2".to_string(),
|
|
|
- ..Default::default()
|
|
|
- })
|
|
|
- .expect("couldn't insert target");
|
|
|
- let it3id = db
|
|
|
- .indirect
|
|
|
- .insert(IndirectTarget {
|
|
|
- name: "itarget3".to_string(),
|
|
|
- ..Default::default()
|
|
|
- })
|
|
|
- .expect("couldn't insert target");
|
|
|
-
|
|
|
- log::trace!("looking up base by ID {:?}", b1id);
|
|
|
-
|
|
|
- let b1 = db
|
|
|
- .bases
|
|
|
- .by_id(b1id)
|
|
|
- .expect("couldn't get base")
|
|
|
- .expect("couldn't get base");
|
|
|
- b1.targets
|
|
|
- .connect_to(t1id)
|
|
|
- .expect("couldn't connect b1 to t1id");
|
|
|
- b1.targets
|
|
|
- .connect_to(t2id)
|
|
|
- .expect("couldn't connect b1 to t2id");
|
|
|
-
|
|
|
- let b2 = db
|
|
|
- .bases
|
|
|
- .by_id(b2id)
|
|
|
- .expect("couldn't get base")
|
|
|
- .expect("couldn't get base");
|
|
|
- b2.targets
|
|
|
- .connect_to(t2id)
|
|
|
- .expect("couldn't connect b2 to t2id");
|
|
|
- b2.targets
|
|
|
- .connect_to(t3id)
|
|
|
- .expect("couldn't connect b2 to t3id");
|
|
|
-
|
|
|
- let t1 = db
|
|
|
- .targets
|
|
|
- .by_id(t2id)
|
|
|
- .expect("couldn't get target")
|
|
|
- .expect("couldn't get target");
|
|
|
- t1.indirect_targets
|
|
|
- .connect_to(it1id)
|
|
|
- .expect("couldn't connect t1 to it1id");
|
|
|
-
|
|
|
- assert_eq!(
|
|
|
- db.bases
|
|
|
- .join(Base::Targets)
|
|
|
- .get()
|
|
|
- .expect("couldn't get joined results")
|
|
|
- .len(),
|
|
|
- 3
|
|
|
- );
|
|
|
-
|
|
|
- let double_join = db
|
|
|
- .bases
|
|
|
- .join(Base::Targets)
|
|
|
- .join(Target::IndirectTargets)
|
|
|
- .get()
|
|
|
- .expect("couldn't get double-joined results");
|
|
|
- assert_eq!(double_join.len(), 1);
|
|
|
-
|
|
|
- let double_join_count = db
|
|
|
- .bases
|
|
|
- .join(Base::Targets)
|
|
|
- .join(Target::IndirectTargets)
|
|
|
- .count()
|
|
|
- .expect("couldn't count double-joined results");
|
|
|
- assert_eq!(double_join_count, 1);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-mod query_equivalence {
|
|
|
- use crate::prelude::*;
|
|
|
- use test_log::test;
|
|
|
- #[derive(Entity)]
|
|
|
- struct SingleItem {
|
|
|
- #[key]
|
|
|
- s: String,
|
|
|
- v: Vec<u8>,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Entity)]
|
|
|
- struct DoubleItem {
|
|
|
- #[key]
|
|
|
- s: String,
|
|
|
- #[key]
|
|
|
- t: String,
|
|
|
- v: usize,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Database)]
|
|
|
- struct ItemDB {
|
|
|
- single_items: microrm::IDMap<SingleItem>,
|
|
|
- double_items: microrm::IDMap<DoubleItem>,
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn single_test() {
|
|
|
- let db = ItemDB::open_path(":memory:").expect("couldn't open test db");
|
|
|
-
|
|
|
- db.single_items
|
|
|
- .insert(SingleItem {
|
|
|
- s: "string 1".into(),
|
|
|
- v: vec![0u8, 1u8, 2u8],
|
|
|
- })
|
|
|
- .expect("couldn't insert test item");
|
|
|
-
|
|
|
- db.single_items
|
|
|
- .insert(SingleItem {
|
|
|
- s: "string 2".into(),
|
|
|
- v: vec![0u8, 1u8, 2u8],
|
|
|
- })
|
|
|
- .expect("couldn't insert test item");
|
|
|
-
|
|
|
- assert!(db
|
|
|
- .single_items
|
|
|
- .keyed("string 2")
|
|
|
- .get()
|
|
|
- .expect("couldn't query db")
|
|
|
- .is_some());
|
|
|
- assert!(db
|
|
|
- .single_items
|
|
|
- .keyed("string 3")
|
|
|
- .get()
|
|
|
- .expect("couldn't query db")
|
|
|
- .is_none());
|
|
|
-
|
|
|
- assert!(db
|
|
|
- .single_items
|
|
|
- .keyed(String::from("string 2"))
|
|
|
- .get()
|
|
|
- .expect("couldn't query db")
|
|
|
- .is_some());
|
|
|
- assert!(db
|
|
|
- .single_items
|
|
|
- .keyed(String::from("string 3"))
|
|
|
- .get()
|
|
|
- .expect("couldn't query db")
|
|
|
- .is_none());
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn double_test() {
|
|
|
- let db = ItemDB::open_path(":memory:").expect("couldn't open test db");
|
|
|
-
|
|
|
- db.double_items
|
|
|
- .insert(DoubleItem {
|
|
|
- s: "string 1".into(),
|
|
|
- t: "second string 1".into(),
|
|
|
- v: 42,
|
|
|
- })
|
|
|
- .expect("couldn't insert test item");
|
|
|
-
|
|
|
- db.double_items
|
|
|
- .insert(DoubleItem {
|
|
|
- s: "string 2".into(),
|
|
|
- t: "second string 2".into(),
|
|
|
- v: 6,
|
|
|
- })
|
|
|
- .expect("couldn't insert test item");
|
|
|
-
|
|
|
- assert!(db
|
|
|
- .double_items
|
|
|
- .keyed(("string 2", "second string 2"))
|
|
|
- .get()
|
|
|
- .expect("couldn't query db")
|
|
|
- .is_some());
|
|
|
- assert!(db
|
|
|
- .double_items
|
|
|
- .keyed(("string 3", "second string 3"))
|
|
|
- .get()
|
|
|
- .expect("couldn't query db")
|
|
|
- .is_none());
|
|
|
-
|
|
|
- assert!(db
|
|
|
- .double_items
|
|
|
- .keyed((String::from("string 2"), String::from("second string 2")))
|
|
|
- .get()
|
|
|
- .expect("couldn't query db")
|
|
|
- .is_some());
|
|
|
- assert!(db
|
|
|
- .double_items
|
|
|
- .keyed((String::from("string 3"), String::from("second string 3")))
|
|
|
- .get()
|
|
|
- .expect("couldn't query db")
|
|
|
- .is_none());
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn with_test() {
|
|
|
- let db = ItemDB::open_path(":memory:").expect("couldn't open test db");
|
|
|
-
|
|
|
- db.single_items
|
|
|
- .insert(SingleItem {
|
|
|
- s: "string 1".into(),
|
|
|
- v: vec![0u8, 1u8, 2u8],
|
|
|
- })
|
|
|
- .expect("couldn't insert test item");
|
|
|
-
|
|
|
- db.single_items
|
|
|
- .insert(SingleItem {
|
|
|
- s: "string 2".into(),
|
|
|
- v: vec![0u8, 1u8, 2u8],
|
|
|
- })
|
|
|
- .expect("couldn't insert test item");
|
|
|
-
|
|
|
- assert_eq!(
|
|
|
- db.single_items
|
|
|
- .with(SingleItem::V, [0u8].as_slice())
|
|
|
- .get()
|
|
|
- .expect("couldn't query db")
|
|
|
- .len(),
|
|
|
- 0
|
|
|
- );
|
|
|
- assert_eq!(
|
|
|
- db.single_items
|
|
|
- .with(SingleItem::V, [0u8, 1, 2].as_slice())
|
|
|
- .get()
|
|
|
- .expect("couldn't query db")
|
|
|
- .len(),
|
|
|
- 2
|
|
|
- );
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-mod injective_test {
|
|
|
- use microrm::prelude::*;
|
|
|
- use test_log::test;
|
|
|
-
|
|
|
- struct AuthorBookRelation;
|
|
|
- impl microrm::Relation for AuthorBookRelation {
|
|
|
- type Domain = Author;
|
|
|
- type Range = Book;
|
|
|
-
|
|
|
- const NAME: &'static str = "AuthorBook";
|
|
|
- const INJECTIVE: bool = true;
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Entity)]
|
|
|
- struct Author {
|
|
|
- #[key]
|
|
|
- name: String,
|
|
|
-
|
|
|
- works: microrm::RelationDomain<AuthorBookRelation>,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Entity)]
|
|
|
- struct Book {
|
|
|
- #[key]
|
|
|
- title: String,
|
|
|
-
|
|
|
- creator: microrm::RelationRange<AuthorBookRelation>,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Database)]
|
|
|
- struct ABDB {
|
|
|
- authors: microrm::IDMap<Author>,
|
|
|
- books: microrm::IDMap<Book>,
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn schema_creation_test() {
|
|
|
- let db = ABDB::open_path(":memory:").expect("couldn't open in-memory database");
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn single_connection() {
|
|
|
- let db = ABDB::open_path(":memory:").expect("couldn't open in-memory database");
|
|
|
-
|
|
|
- let a1 = db
|
|
|
- .authors
|
|
|
- .insert_and_return(Author {
|
|
|
- name: "Homer".to_string(),
|
|
|
- works: Default::default(),
|
|
|
- })
|
|
|
- .expect("couldn't insert author");
|
|
|
-
|
|
|
- let a2 = db
|
|
|
- .authors
|
|
|
- .insert_and_return(Author {
|
|
|
- name: "Virgil".to_string(),
|
|
|
- works: Default::default(),
|
|
|
- })
|
|
|
- .expect("couldn't insert author");
|
|
|
-
|
|
|
- let b1_id = db
|
|
|
- .books
|
|
|
- .insert(Book {
|
|
|
- title: "Odyssey".to_string(),
|
|
|
- creator: Default::default(),
|
|
|
- })
|
|
|
- .expect("couldn't insert book");
|
|
|
- let b2_id = db
|
|
|
- .books
|
|
|
- .insert(Book {
|
|
|
- title: "Aeneid".to_string(),
|
|
|
- creator: Default::default(),
|
|
|
- })
|
|
|
- .expect("couldn't insert book");
|
|
|
-
|
|
|
- a1.works
|
|
|
- .connect_to(b1_id)
|
|
|
- .expect("couldn't connect a1 and b1");
|
|
|
- a2.works
|
|
|
- .connect_to(b2_id)
|
|
|
- .expect("couldn't connect a2 and b2");
|
|
|
-
|
|
|
- // we can't claim that Homer wrote the Aeneid because it's an injective relationship and
|
|
|
- // in this model, only one Author can claim the Book as their work
|
|
|
- match a1.works.connect_to(b2_id) {
|
|
|
- Err(microrm::Error::ConstraintViolation(_)) => {
|
|
|
- // all good
|
|
|
- }
|
|
|
- Err(_) => {
|
|
|
- panic!("Unexpected error while testing injective connection");
|
|
|
- }
|
|
|
- Ok(_) => {
|
|
|
- panic!("Unexpected success while testing injective connection");
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-mod index_test {
|
|
|
- use microrm::prelude::*;
|
|
|
- use microrm_macros::index_cols;
|
|
|
- use test_log::test;
|
|
|
-
|
|
|
- use microrm::schema::index::Index;
|
|
|
-
|
|
|
- #[derive(Entity)]
|
|
|
- struct TwoWay {
|
|
|
- up: String,
|
|
|
- left: String,
|
|
|
- right: String,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Database)]
|
|
|
- struct TWDB {
|
|
|
- entries: microrm::IDMap<TwoWay>,
|
|
|
- left_index: Index<TwoWay, index_cols![TwoWay::left]>,
|
|
|
- right_index: Index<TwoWay, index_cols![TwoWay::right]>,
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn db_construction() {
|
|
|
- let db = TWDB::open_path(":memory:").expect("couldn't open in-memory db");
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn lookup_test() {
|
|
|
- let db = TWDB::open_path(":memory:").expect("couldn't open in-memory db");
|
|
|
-
|
|
|
- let e1 = db
|
|
|
- .entries
|
|
|
- .insert_and_return(TwoWay {
|
|
|
- up: "up1".into(),
|
|
|
- left: "left1".into(),
|
|
|
- right: "right1".into(),
|
|
|
- })
|
|
|
- .expect("couldn't insert entry");
|
|
|
- let e2 = db
|
|
|
- .entries
|
|
|
- .insert_and_return(TwoWay {
|
|
|
- up: "up2".into(),
|
|
|
- left: "left2".into(),
|
|
|
- right: "right2".into(),
|
|
|
- })
|
|
|
- .expect("couldn't insert entry");
|
|
|
-
|
|
|
- assert!(db
|
|
|
- .entries
|
|
|
- .indexed(&db.left_index, "left3")
|
|
|
- .get()
|
|
|
- .expect("couldn't query left index")
|
|
|
- .is_none());
|
|
|
- assert_eq!(
|
|
|
- db.entries
|
|
|
- .indexed(&db.left_index, "left2")
|
|
|
- .get()
|
|
|
- .expect("couldn't query left index")
|
|
|
- .as_ref(),
|
|
|
- Some(&e2)
|
|
|
- );
|
|
|
- assert_eq!(
|
|
|
- db.entries
|
|
|
- .indexed(&db.left_index, "left1")
|
|
|
- .get()
|
|
|
- .expect("couldn't query left index")
|
|
|
- .as_ref(),
|
|
|
- Some(&e1)
|
|
|
- );
|
|
|
-
|
|
|
- assert!(db
|
|
|
- .entries
|
|
|
- .indexed(&db.right_index, "right3")
|
|
|
- .get()
|
|
|
- .expect("couldn't query right index")
|
|
|
- .is_none());
|
|
|
- assert_eq!(
|
|
|
- db.entries
|
|
|
- .indexed(&db.right_index, "right2")
|
|
|
- .get()
|
|
|
- .expect("couldn't query right index")
|
|
|
- .as_ref(),
|
|
|
- Some(&e2)
|
|
|
- );
|
|
|
- assert_eq!(
|
|
|
- db.entries
|
|
|
- .indexed(&db.right_index, "right1")
|
|
|
- .get()
|
|
|
- .expect("couldn't query right index")
|
|
|
- .as_ref(),
|
|
|
- Some(&e1)
|
|
|
- );
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-mod foreign_key {
|
|
|
- use microrm::prelude::*;
|
|
|
- use test_log::test;
|
|
|
-
|
|
|
- #[derive(Entity)]
|
|
|
- struct Child {
|
|
|
- #[key]
|
|
|
- name: String,
|
|
|
- parent: ParentID,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Entity)]
|
|
|
- struct Parent {
|
|
|
- name: String,
|
|
|
- }
|
|
|
-
|
|
|
- #[derive(Database)]
|
|
|
- struct PeopleDB {
|
|
|
- children: microrm::IDMap<Child>,
|
|
|
- parents: microrm::IDMap<Parent>,
|
|
|
- }
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn simple_fk_query() {
|
|
|
- let db = PeopleDB::open_path(":memory:").expect("couldn't open DB");
|
|
|
-
|
|
|
- let pid = db
|
|
|
- .parents
|
|
|
- .insert(Parent {
|
|
|
- name: "parent1".to_string(),
|
|
|
- })
|
|
|
- .unwrap();
|
|
|
- let c1id = db
|
|
|
- .children
|
|
|
- .insert(Child {
|
|
|
- name: "child1".to_string(),
|
|
|
- parent: pid,
|
|
|
- })
|
|
|
- .unwrap();
|
|
|
-
|
|
|
- assert_eq!(
|
|
|
- db.children
|
|
|
- .foreign(Child::Parent)
|
|
|
- .get_ids()
|
|
|
- .expect("couldn't run query"),
|
|
|
- vec![pid]
|
|
|
- );
|
|
|
-
|
|
|
- assert_eq!(
|
|
|
- db.children
|
|
|
- .keyed("child1")
|
|
|
- .foreign(Child::Parent)
|
|
|
- .get_ids()
|
|
|
- .expect("couldn't run query"),
|
|
|
- Some(pid)
|
|
|
- );
|
|
|
- }
|
|
|
-}
|