use std::error::Error;
use haematite::SyncCounts;
use haematite::db::DatabaseConfig;
use haematite::store::NodeCacheBudget;
use serde_json::value::RawValue;
use crate::serve::protocol::{Change, CollectionWrite, DocumentWrite, Mutation, Version};
use crate::serve::storage::{Storage, branch_name, version};
type TestResult = Result<(), Box<dyn Error>>;
fn create() -> Result<(tempfile::TempDir, Storage), Box<dyn Error>> {
let directory = tempfile::tempdir()?;
let mut storage = Storage::create(
DatabaseConfig {
data_dir: directory.path().join("database"),
shard_count: 1,
distributed: None,
executor_threads: Some(1),
node_cache_budget: Some(NodeCacheBudget::Unlimited),
retention: haematite::Retention::reclaim(0),
},
NodeCacheBudget::Unlimited,
fixture_long_wait_interval(),
)?;
apply(&mut storage, "notes", collection("notes"))?;
Ok((directory, storage))
}
fn head(storage: &Storage) -> Result<Version, Box<dyn Error>> {
Ok(storage
.refs
.get(&branch_name("main"))
.map(version)
.ok_or("no main")?)
}
fn apply(storage: &mut Storage, request_id: &str, change: Change) -> TestResult {
let expected = head(storage)?;
storage.mutate(&Mutation {
request_id: request_id.to_owned(),
branch: "main".to_owned(),
expected,
change,
})?;
Ok(())
}
fn collection(name: &str) -> Change {
Change::Collection(CollectionWrite {
collection: name.to_owned(),
schema: None,
})
}
fn document(id: &str) -> Result<Change, Box<dyn Error>> {
Ok(Change::Put(DocumentWrite {
collection: "notes".to_owned(),
document_id: id.to_owned(),
document: RawValue::from_string(r#"{"body":"text"}"#.to_owned())?,
}))
}
fn counts(storage: &Storage) -> SyncCounts {
storage.writer.sync_counts()
}
const ONE_WRITE: SyncCounts = SyncCounts {
record_installs: 1,
barrier_device_flushes: 1,
ref_file_full_flushes: 1,
directory_full_flushes: 1,
};
#[test]
fn one_document_put_costs_one_commit_and_three_flushes() -> TestResult {
let (_directory, mut storage) = create()?;
let before = counts(&storage);
apply(&mut storage, "put-a", document("a")?)?;
assert_eq!(counts(&storage).since(before), ONE_WRITE);
Ok(())
}
#[test]
fn a_retry_of_an_applied_request_commits_nothing_and_re_fences_once() -> TestResult {
let (_directory, mut storage) = create()?;
let expected = head(&storage)?;
let request = Mutation {
request_id: "put-a".to_owned(),
branch: "main".to_owned(),
expected,
change: document("a")?,
};
storage.mutate(&request)?;
let before = counts(&storage);
storage.mutate(&request)?;
let expected = SyncCounts {
directory_full_flushes: 1,
..SyncCounts::default()
};
assert_eq!(counts(&storage).since(before), expected);
Ok(())
}
#[test]
fn a_write_whose_row_is_kept_already_commits_once() -> TestResult {
let (_directory, mut storage) = create()?;
let record = storage
.refs
.get(&branch_name("main"))
.cloned()
.ok_or("no main")?;
storage.retain_history_row("main", &record)?;
let before = counts(&storage);
apply(&mut storage, "put-a", document("a")?)?;
assert_eq!(counts(&storage).since(before), ONE_WRITE);
Ok(())
}
#[test]
fn one_collection_put_costs_the_same() -> TestResult {
let (_directory, mut storage) = create()?;
let before = counts(&storage);
apply(&mut storage, "letters", collection("letters"))?;
assert_eq!(counts(&storage).since(before), ONE_WRITE);
Ok(())
}
const fn fixture_long_wait_interval() -> std::time::Duration {
const LONG_WAIT_INTERVAL: std::time::Duration = std::time::Duration::from_secs(1);
LONG_WAIT_INTERVAL
}