use entidb_core::{Config, Database};
use entidb_storage::FileBackend;
use std::path::PathBuf;
use tempfile::TempDir;
pub struct TestDatabase {
pub db: Database,
_temp_dir: Option<TempDir>,
}
impl TestDatabase {
pub fn memory() -> Self {
Self {
db: Database::open_in_memory().expect("Failed to open in-memory database"),
_temp_dir: None,
}
}
pub fn file() -> Self {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let wal_path = temp_dir.path().join("wal.log");
let segment_path = temp_dir.path().join("segments.dat");
let wal_backend =
FileBackend::open_with_create_dirs(&wal_path).expect("Failed to create WAL backend");
let segment_backend = FileBackend::open_with_create_dirs(&segment_path)
.expect("Failed to create segment backend");
let db = Database::open_with_backends(
Config::default(),
Box::new(wal_backend),
Box::new(segment_backend),
)
.expect("Failed to open file database");
Self {
db,
_temp_dir: Some(temp_dir),
}
}
pub fn path(&self) -> Option<PathBuf> {
self._temp_dir
.as_ref()
.map(|d| d.path().join("test.entidb"))
}
}
impl std::ops::Deref for TestDatabase {
type Target = Database;
fn deref(&self) -> &Self::Target {
&self.db
}
}
impl std::ops::DerefMut for TestDatabase {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.db
}
}
pub fn with_temp_db<F, R>(f: F) -> R
where
F: FnOnce(&Database) -> R,
{
let test_db = TestDatabase::memory();
f(&test_db.db)
}
pub fn with_file_db<F, R>(f: F) -> R
where
F: FnOnce(&Database, &std::path::Path) -> R,
{
let test_db = TestDatabase::file();
let path = test_db.path().expect("File database should have a path");
f(&test_db.db, &path)
}
pub fn with_temp_db_mut<F, R>(f: F) -> R
where
F: FnOnce(&mut Database) -> R,
{
let mut test_db = TestDatabase::memory();
f(&mut test_db.db)
}
pub mod scenarios {
use super::*;
use entidb_core::{CollectionId, EntityId};
pub fn populated_database(entity_count: usize) -> TestDatabase {
let test_db = TestDatabase::memory();
let collection_id = test_db.db.collection("test");
for i in 0..entity_count {
let entity_id = EntityId::new();
let data = format!(r#"{{"index":{}}}"#, i).into_bytes();
test_db
.db
.transaction(|txn| {
txn.put(collection_id, entity_id, data)?;
Ok(())
})
.expect("Failed to put entity");
}
test_db
}
pub fn multi_collection_database(collection_count: usize) -> (TestDatabase, Vec<CollectionId>) {
let test_db = TestDatabase::memory();
let mut collections = Vec::with_capacity(collection_count);
for i in 0..collection_count {
let name = format!("collection_{}", i);
let collection_id = test_db.db.collection(&name);
collections.push(collection_id);
let entity_id = EntityId::new();
let data = format!(r#"{{"collection":{}}}"#, i).into_bytes();
test_db
.db
.transaction(|txn| {
txn.put(collection_id, entity_id, data)?;
Ok(())
})
.expect("Failed to put entity");
}
(test_db, collections)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_database() {
let test_db = TestDatabase::memory();
let collection = test_db.collection("test");
let _ = collection;
}
#[test]
fn test_with_temp_db() {
with_temp_db(|db| {
let collection = db.collection("test");
let _ = collection; });
}
#[test]
fn test_populated_scenario() {
let test_db = scenarios::populated_database(10);
let _collection = test_db.collection("test");
}
}