#[macro_use]
extern crate serde_json;
mod common;
#[cfg(test)]
mod snippets {
use std::{
collections::HashMap,
path::Path,
sync::{mpsc::channel, Arc},
};
use dittolive_ditto::{
error::DittoError,
store::{
ditto_attachment_fetch_event::DittoAttachmentFetchEvent,
ditto_attachment_token::DittoAttachmentToken,
},
types::DittoCounter,
};
use serde::Serialize;
use uuid::Uuid;
use super::common::*;
#[derive(Serialize, Debug)]
struct PersonId {
user_id: String,
work_id: i32,
}
#[test]
fn basic() {
let ditto = get_ditto().unwrap();
let store = ditto.store();
let collection = store.collection("people").unwrap();
let doc_id = DocumentId::new(&"123abc".to_string()).unwrap();
let person = json!({ "_id": doc_id,
"name": "Susan".to_string(),
"age": 31,
});
collection.upsert(person).unwrap();
}
#[test]
fn array_to_map() {
let ditto = get_ditto().unwrap();
let store = ditto.store();
let collection = store.collection("people").unwrap();
let person = json!({
"friends": ["Susan", "John"]
});
let doc_id = collection.upsert(person).unwrap();
collection
.find_by_id(doc_id)
.update(|opt_doc| {
if let Some(doc) = opt_doc {
let friends: DittoRegister = doc.get("friends").unwrap();
let mut map = HashMap::new();
let array = friends.value.as_array().unwrap();
for name in array {
let id = Uuid::new_v4().to_string();
let friend = json!({
"name": name,
"id": id
});
map.insert(id, friend);
}
doc.set("friendsMap", map).unwrap();
}
})
.unwrap();
}
#[test]
fn upsert() {
let ditto = get_ditto().unwrap();
let person = json!({
"name": "Susan".to_string(),
"age": 31,
});
let collection = ditto.store().collection("people").unwrap();
let id = collection.upsert(person).unwrap();
collection.find_by_id(id).remove().unwrap();
let collection = ditto.store().collection("people").unwrap();
let complex_id = PersonId {
user_id: "456abc".to_string(),
work_id: 789,
};
let doc_id = DocumentId::new(&serde_json::json!(complex_id)).unwrap();
let doc = json!({
"_id": doc_id,
"name": "Susan".to_string(),
"age": 31,
});
collection.upsert(doc).unwrap();
collection
.upsert(json!({
"boolean": true,
"string": "Hello World",
"number": 10,
"map": {
"key": "value"
},
"array": [1,2,3],
"null": null,
}))
.unwrap();
let default_id = DocumentId::new(&"123abc".to_string()).unwrap();
let data = json!({ "_id": default_id,
"name": "Susan".to_string(),
"age": 31,
});
collection
.upsert_with_strategy(data, WriteStrategy::InsertDefaultIfAbsent)
.unwrap();
}
#[test]
fn attachment() -> Result<(), DittoError> {
let ditto = get_ditto().unwrap();
let images_dir = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap())
.join("tests")
.join("resources");
let store = ditto.store();
let collection = store.collection("foo")?;
let attachment_file_path = images_dir.join("image.png");
let mut metadata = HashMap::new();
metadata.insert("some".to_owned(), "string".to_owned());
let attachment =
collection.new_attachment(attachment_file_path.to_str().unwrap(), metadata)?;
let doc_id = DocumentId::new(&"123abc".to_string())?;
let content = json!({"_id": doc_id, "some": "string", "my_attachment": attachment});
let _ = collection.upsert(content)?;
let doc = collection.find_by_id(doc_id).exec()?;
let attachment_token = doc.get::<DittoAttachmentToken>("my_attachment")?;
let (tx, rx) = channel();
let m_tx = std::sync::Mutex::new(tx);
let fetcher = collection.fetch_attachment(attachment_token, move |event| {
if let DittoAttachmentFetchEvent::Completed { attachment } = event {
let tx = m_tx.lock().unwrap();
tx.send(attachment).unwrap();
}
})?;
let fetched_attachment = rx.recv().unwrap(); let attachment_file_path = fetched_attachment.path();
std::fs::read(attachment_file_path)?;
drop(fetcher);
Ok(())
}
#[test]
fn counter() {
let ditto = get_ditto().unwrap();
let collection = ditto.store().collection("people").unwrap();
let doc_id = collection
.upsert(json!({"name": "Frank", "owned_cars": 0}))
.unwrap();
collection
.find_by_id(doc_id)
.update(|x| {
if let Some(doc) = x {
doc.set("owned_cars", DittoCounter::new()).unwrap();
doc.increment("owned_cars", 1.0).unwrap();
}
})
.unwrap();
}
#[test]
fn update() {
let ditto = get_ditto().unwrap();
let collection = ditto.store().collection("people").unwrap();
let doc_id = collection
.upsert(json!({"name": "Frank", "owned_cars": 0}))
.unwrap();
collection
.find_by_id(doc_id)
.update(|opt_doc| {
if let Some(doc) = opt_doc {
doc.set("age", 32).unwrap();
doc.set("owned_cars", DittoCounter::new()).unwrap();
doc.increment("owned_cars", 1.0).unwrap();
}
})
.unwrap();
}
#[test]
fn querying() -> Result<(), DittoError> {
let ditto = get_ditto().unwrap();
let collection = ditto.store().collection("cars").unwrap();
collection
.find("favoriteBook.title == \'The Great Gatsby\'")
.exec()?;
let args = json!({"name": "Susan", "age": 32});
collection
.find_with_args("name == $args.name && age <= $args.age", args)
.exec()?;
let sort_param = ffi_sdk::COrderByParam {
query_c_str: c!("miles"),
direction: ffi_sdk::QuerySortDirection::Ascending,
};
collection
.find("color == \'red\'")
.sort(vec![sort_param])
.exec()?;
let sort_param = ffi_sdk::COrderByParam {
query_c_str: c!("rank"),
direction: ffi_sdk::QuerySortDirection::Ascending,
};
collection
.find("color == \'red\'")
.sort(vec![sort_param])
.limit(100)
.exec()?;
Ok(())
}
#[test]
fn sync_subscribe() -> Result<(), DittoError> {
let ditto = get_ditto().unwrap();
let store = ditto.store(); let live_query = store
.collection("cars")?
.find("color == \'red\'")
.subscribe();
drop(live_query);
Ok(())
}
#[test]
#[allow(deprecated)]
fn sync_basic() -> Result<(), DittoError> {
let ditto = get_ditto().unwrap();
let store = ditto.store(); let (tx, rx) = channel();
{
let live_query = store
.collection("cars")?
.find("color == \'red\'")
.observe_local(move |mut docs: Vec<BoxedDocument>, event| {
match event {
LiveQueryEvent::Initial { .. } => { }
LiveQueryEvent::Update { mut insertions, .. } => {
insertions.sort_by(|a, b| b.cmp(a));
for idx in insertions.iter() {
let doc = docs.remove(*idx);
tx.send(doc).unwrap();
}
}
}
})?;
store
.collection("cars")?
.upsert(json!({"color": "red"}))
.unwrap();
#[allow(clippy::never_loop)]
for doc in rx.iter() {
println!("New doc {:?}", doc);
break;
}
drop(live_query);
} Ok(())
}
#[test]
fn observe_local() -> Result<(), DittoError> {
let ditto = get_ditto().unwrap();
let store = ditto.store();
store.collection("cars")?.upsert(json!({"color": "red"}))?;
{
let live_query = store
.collection("cars")?
.find("color == \'red\'")
.observe_local(move |cars, event| {
println!("cars {:?}, event {:?}", cars, event);
})?;
drop(live_query);
}
Ok(())
}
#[ignore]
#[test]
fn shared_key() -> Result<(), DittoError> {
let license_token = std::env::var("DITTO_LICENSE").expect("No License Env Var provided");
let p256_der_b64: &str = "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgFUUrOkOH52QN+Rr6uDSDsk4hUTcD1eW4mT0UnGGptFehRANCAATJ3fG8TVLQcDwUV18BJJI8efK0hQAjzB3VJeYOVbfOlqnfukVId0V25r/abxwjD3HfHuPsCGEiefzzmkMbjPo9";
let app_id = AppId::from_env("app")?;
let ditto = Ditto::builder()
.with_root(Arc::new(PersistentRoot::from_current_exe()?))
.with_identity(|ditto_root| identity::SharedKey::new(ditto_root, app_id, p256_der_b64))?
.with_minimum_log_level(CLogLevel::Info)
.build()?;
let res = ditto.set_offline_only_license_token(&license_token);
ditto.start_sync()?;
assert!(res.is_ok());
ditto.stop_sync();
Ok(())
}
#[ignore]
#[test]
fn online_playground() -> Result<(), DittoError> {
let ditto = Ditto::builder()
.with_root(Arc::new(PersistentRoot::from_current_exe()?))
.with_identity(|ditto_root| {
let app_id = AppId::from_env("00000000-0000-4000-0000-000000000000")?;
let shared_token = std::env::var("REPLACE_ME_WITH_A_SHARED_TOKEN").unwrap();
let enable_cloud_sync = true;
let custom_auth_url = None;
OnlinePlayground::new(
ditto_root,
app_id,
shared_token,
enable_cloud_sync,
custom_auth_url,
)
})?
.build()?;
ditto.start_sync()?;
ditto.stop_sync();
Ok(())
}
#[ignore]
#[test]
fn offline_playground() -> Result<(), DittoError> {
let license_token = std::env::var("DITTO_LICENSE").expect("No License Env Var provided");
let ditto = Ditto::builder()
.with_root(Arc::new(PersistentRoot::from_current_exe()?))
.with_identity(|ditto_root| {
let app_id = AppId::from_env("00000000-0000-4000-0000-000000000000")?;
OfflinePlayground::new(ditto_root, app_id)
})?
.build()?;
ditto.start_sync()?;
let res = ditto.set_offline_only_license_token(&license_token);
assert!(res.is_ok());
ditto.stop_sync();
Ok(())
}
#[test]
fn network_remote_ditto() -> Result<(), DittoError> {
let ditto = get_ditto().unwrap();
let mut config = TransportConfig::new();
config
.connect
.tcp_servers
.insert("135.1.5.5:12345".to_string()); config
.connect
.tcp_servers
.insert("185.1.5.5:12345".to_string()); config
.connect
.websocket_urls
.insert("wss://example.com".to_string());
ditto.set_transport_config(config);
ditto.start_sync()?;
ditto.stop_sync();
Ok(())
}
#[test]
fn network_listen() -> Result<(), DittoError> {
let ditto = get_ditto().unwrap();
let mut config = TransportConfig::new();
config.listen.tcp.enabled = true;
config.listen.tcp.interface_ip = "0.0.0.0".to_string();
config.listen.tcp.port = 4000;
config.listen.http.enabled = false;
ditto.set_transport_config(config);
ditto.start_sync()?;
ditto.stop_sync();
Ok(())
}
#[test]
fn network_three() -> Result<(), DittoError> {
let ditto = get_ditto().unwrap();
let mut config = TransportConfig::new();
config.enable_all_peer_to_peer();
config.listen.tcp.enabled = true;
config.listen.tcp.interface_ip = "0.0.0.0".to_string();
config.listen.tcp.port = 4000;
config.listen.http.enabled = false;
config
.connect
.tcp_servers
.insert("135.1.5.5:12345".to_string()); config
.connect
.websocket_urls
.insert("wss://example.com".to_string());
ditto.set_transport_config(config);
ditto.start_sync()?;
ditto.stop_sync();
Ok(())
}
}