//! The Live Query "Everything" Test
//! extracted into its own file (compilation unit)
//! for easier debuging
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
};
use common::*;
use dittolive_ditto::{
observer::Observer,
store::batch::{DocChangeKind, ScopedStore},
};
mod common;
#[test]
fn everything() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
let ditto = common::get_ditto().unwrap();
let store = ditto.store();
let uuid1 = uuid::Uuid::new_v4().to_string();
let col1_name = uuid1.clone();
let uuid2 = uuid::Uuid::new_v4().to_string();
let col2_name = uuid2.clone();
let col1 = store.collection(&uuid1).unwrap();
let col2 = store.collection(&uuid2).unwrap();
let doc1 = TestType {
make: String::from("Honda"),
color: TestColor::Red,
..Default::default()
};
let redhonda_id = Arc::new(Mutex::new(col1.upsert(doc1)?));
let doc2 = TestType {
make: String::from("Honda"),
color: TestColor::Crimson,
..Default::default()
};
let _crimsonhonda_id = Arc::new(col1.upsert(doc2)?);
let doc3 = TestType {
make: String::from("Bentley"),
color: TestColor::Red,
..Default::default()
};
let doc4 = TestType {
make: String::from("Jaguar"),
color: TestColor::Red,
..Default::default()
};
let redbentley_id = Arc::new(Mutex::new(col2.upsert(doc3)?));
let redjaguar_id = Arc::new(Mutex::new(col2.upsert(doc4)?));
let redtoyota_id = Arc::new(Mutex::new(DocumentId::default()));
let redford_id = Arc::new(Mutex::new(DocumentId::default()));
let orangemazda_id = Arc::new(Mutex::new(DocumentId::default()));
let yellowam_id = Arc::new(Mutex::new(DocumentId::default()));
let redchevy_id = Arc::new(Mutex::new(DocumentId::default()));
let redhyundai_id = Arc::new(Mutex::new(DocumentId::default()));
let pinkjeep_id = Arc::new(Mutex::new(DocumentId::default()));
let blackbmw_id = Arc::new(Mutex::new(DocumentId::default()));
let redferrari_id = Arc::new(Mutex::new(DocumentId::default()));
let counter = Arc::new(Mutex::new(0));
let collection = col1.clone();
let store_copy = store.clone(); // a clone of the store to move into the EventHandler
let is_complete = Arc::new(AtomicBool::new(false));
let is_complete_copy = Arc::clone(&is_complete);
let handler = move |docs: Vec<BoxedDocument>, event: LiveQueryEvent| {
if let Ok(mut counter) = counter.lock() {
*counter += 1;
}
match event {
LiveQueryEvent::Initial => {
assert_eq!(*counter.lock().unwrap(), 1);
assert_eq!(docs.len(), 1);
let first_doc = docs[0].typed::<TestType>().unwrap();
let expected_doc = TestType {
make: String::from("Honda"),
color: TestColor::Red,
id: Some(redhonda_id.lock().unwrap().clone()),
..Default::default()
};
assert_eq!(first_doc, expected_doc);
let redtoyota = TestType {
color: TestColor::Red,
make: String::from("Toyota"),
..Default::default()
};
*redtoyota_id.lock().unwrap() = collection.upsert(redtoyota).unwrap();
}
LiveQueryEvent::Update {
moves,
insertions,
deletions,
updates,
old_documents,
} => {
assert_eq!(moves.len(), 0);
if let Ok(count) = counter.lock() {
match *count {
2 => {
assert_eq!(docs.len(), 2);
assert_eq!(insertions.len(), 1);
assert_eq!(deletions.len(), 0);
assert_eq!(updates.len(), 0);
assert_eq!(old_documents.len(), 1);
let redford = TestType {
make: String::from("Ford"),
color: TestColor::Red,
..Default::default()
};
*redford_id.lock().unwrap() = collection.upsert(redford).unwrap();
}
3 => {
assert_eq!(docs.len(), 3);
assert_eq!(insertions.len(), 1);
assert_eq!(deletions.len(), 0);
assert_eq!(updates.len(), 0);
assert_eq!(old_documents.len(), 2);
// Assert there is a red Ford
let result = collection
.find_by_id(redford_id.lock().unwrap().clone())
.exec();
assert!(result.is_ok());
// Make the red toyota a red Maserati
collection
.find_by_id(redtoyota_id.lock().unwrap().clone())
.update(|result| {
if let Some(doc) = result {
doc.set("make", String::from("Maserati")).unwrap();
}
})
.unwrap();
}
4 => {
assert_eq!(docs.len(), 3);
assert_eq!(insertions.len(), 0);
assert_eq!(deletions.len(), 0);
assert_eq!(updates.len(), 1);
assert_eq!(old_documents.len(), 3);
let orangemazda = TestType {
make: String::from("Mazda"),
color: TestColor::Orange,
..Default::default()
};
let yellowam = TestType {
make: String::from("Aston Martin"),
color: TestColor::Yellow,
..Default::default()
};
let redchevy = TestType {
make: String::from("Chevrolet"),
color: TestColor::Red,
..Default::default()
};
*orangemazda_id.lock().unwrap() =
collection.upsert(orangemazda).unwrap();
*yellowam_id.lock().unwrap() = collection.upsert(yellowam).unwrap();
*redchevy_id.lock().unwrap() = collection.upsert(redchevy).unwrap();
}
5 => {
assert_eq!(docs.len(), 4);
assert_eq!(insertions.len(), 1);
assert_eq!(deletions.len(), 0);
assert_eq!(updates.len(), 0);
assert_eq!(old_documents.len(), 3);
// verify a red chevy is present
let result = collection
.find_by_id(redchevy_id.lock().unwrap().clone())
.exec();
assert!(result.is_ok());
// make the red honda blue
let _ = collection
.find_by_id(redhonda_id.lock().unwrap().clone())
.update(|result| {
if let Some(doc) = result {
doc.set("color", TestColor::Blue).unwrap();
}
})
.unwrap();
}
6 => {
assert_eq!(docs.len(), 3);
assert_eq!(insertions.len(), 0);
assert_eq!(deletions.len(), 1);
assert_eq!(updates.len(), 0);
assert_eq!(old_documents.len(), 4);
// assert there is NOT a red honda
let results = store_copy.with_batched_write(
// 'batch 'store
|mut tx: ScopedStore<'_>| {
let mut col_tx = tx.collection(col1_name.as_str());
let redhyundai = TestType {
make: String::from("Hyundai"),
color: TestColor::Red,
..Default::default()
};
let pinkjeep = TestType {
make: String::from("Jeep"),
color: TestColor::Pink,
..Default::default()
};
*redhyundai_id.lock().unwrap() =
col_tx.upsert(redhyundai).unwrap().clone();
*pinkjeep_id.lock().unwrap() =
col_tx.upsert(pinkjeep).unwrap().clone();
// remove the red toyota
col_tx
.find_by_id(redtoyota_id.lock().unwrap().clone())
.evict()
.unwrap();
// remove the orange mazda
col_tx
.find_by_id(orangemazda_id.lock().unwrap().clone())
.evict()
.unwrap();
// make the Ford an audi
col_tx
.find_by_id(redford_id.lock().unwrap().clone())
.update(|result| {
if let Some(doc) = result {
doc.set("make", String::from("Audi")).unwrap()
}
})
.unwrap();
// make the chevy white
col_tx
.find_by_id(redchevy_id.lock().unwrap().clone())
.update(|result| {
if let Some(doc) = result {
doc.set("color", TestColor::White).unwrap()
}
})
.unwrap();
// make the aston martin red
col_tx
.find_by_id(yellowam_id.lock().unwrap().clone())
.update(|result| {
if let Some(doc) = result {
doc.set("color", TestColor::Red).unwrap()
}
})
.unwrap();
// in the second collection
// insert a black bmw, evict the red bentley, and make the
// red jaguar a citroen
let mut col2_tx = tx.collection(col2_name.as_str());
let blackbmw = TestType {
make: String::from("BMW"),
color: TestColor::Black,
..Default::default()
};
*blackbmw_id.lock().unwrap() =
col2_tx.upsert(blackbmw).unwrap().clone();
col2_tx
.find_by_id(redbentley_id.lock().unwrap().clone())
.evict()
.unwrap();
col2_tx
.find_by_id(redjaguar_id.lock().unwrap().clone())
.update(|result| {
if let Some(doc) = result {
doc.set("make", String::from("Citroen")).unwrap();
}
})
.unwrap();
tx.commit_changes()
},
);
for (idx, result) in results.unwrap().iter().enumerate() {
match idx {
0 => {
assert_eq!(result.kind, DocChangeKind::Inserted);
}
1 => {
assert_eq!(result.kind, DocChangeKind::Inserted);
}
2 => {
assert_eq!(result.kind, DocChangeKind::Evicted);
}
3 => {
assert_eq!(result.kind, DocChangeKind::Evicted);
}
4 => {
assert_eq!(result.kind, DocChangeKind::Updated);
}
5 => {
assert_eq!(result.kind, DocChangeKind::Updated);
}
6 => {
assert_eq!(result.kind, DocChangeKind::Updated);
}
7 => {
assert_eq!(result.kind, DocChangeKind::Inserted);
}
8 => {
assert_eq!(result.kind, DocChangeKind::Evicted);
}
9 => {
assert_eq!(result.kind, DocChangeKind::Updated);
}
_ => {
panic!("Too many Results");
}
}
}
}
7 => {
assert_eq!(docs.len(), 3);
assert_eq!(insertions.len(), 2);
assert_eq!(deletions.len(), 2);
assert_eq!(updates.len(), 1);
assert_eq!(old_documents.len(), 3);
// assert there is a red Audi, Hyundai, and Aston Martin
let redferrari = TestType {
make: String::from("Ferrari"),
color: TestColor::Red,
..Default::default()
};
*redferrari_id.lock().unwrap() = collection.upsert(redferrari).unwrap();
}
8 => {
assert_eq!(docs.len(), 4);
assert_eq!(insertions.len(), 1);
assert_eq!(deletions.len(), 0);
assert_eq!(updates.len(), 0);
assert_eq!(old_documents.len(), 3);
// assert there is a red ferrari
//
let results = store_copy
.with_batched_write(|mut tx| {
let mut coll = tx.collection(col1_name.as_str());
// find the red ferrari, make it a porsche, then make it brown,
// then evict it
//
let id = redferrari_id.lock().unwrap().clone();
coll.find_by_id(id.clone())
.update(|result| {
if let Some(doc) = result {
doc.set("make", String::from("Porsche")).unwrap()
}
})
.unwrap();
coll.find_by_id(id.clone())
.update(|result| {
if let Some(doc) = result {
doc.set("color", TestColor::Brown).unwrap()
}
})
.unwrap();
coll.find_by_id(id).evict().unwrap();
tx.commit_changes()
})
.unwrap();
for (idx, result) in results.iter().enumerate() {
match idx {
0 => {
assert_eq!(result.kind, DocChangeKind::Updated);
}
1 => {
assert_eq!(result.kind, DocChangeKind::Updated);
}
2 => {
assert_eq!(result.kind, DocChangeKind::Evicted);
}
_ => {
panic!("Too many results");
}
}
}
}
9 => {
assert_eq!(docs.len(), 3);
assert_eq!(insertions.len(), 0);
assert_eq!(deletions.len(), 1);
assert_eq!(updates.len(), 0);
assert_eq!(old_documents.len(), 4);
is_complete_copy.store(true, Ordering::SeqCst);
}
_ => {
panic!("Unexpected count {}", count);
}
}
}
}
}
};
let lq = col1
.find("color == \'Red\'")
.observe_local(handler)
.unwrap();
while !is_complete.load(Ordering::SeqCst) {}
lq.stop();
Ok(())
}