use std::{collections::HashSet, time::Duration}; use bitcasky::internals::common::{ get_temporary_directory_path, RandomTestingDataGenerator, TestingOperations, TestingOperator, }; use bitcasky::options::{BitcaskyOptions, SyncStrategy}; use bitcasky::{bitcasky::Bitcasky, error::BitcaskyError}; use test_log::test; fn execute_testing_operations(bc: &Bitcasky, ops: &TestingOperations) { for op in ops.operations() { match op.operator() { TestingOperator::PUT => bc.put(op.key(), op.value()).unwrap(), TestingOperator::DELETE => bc.delete(&op.key()).unwrap(), TestingOperator::MERGE => bc.merge().unwrap(), TestingOperator::NONE => {} } } } fn get_default_options() -> BitcaskyOptions { BitcaskyOptions::default() .max_data_file_size(10 * 1024) .init_data_file_capacity(100) .init_hint_file_capacity(1024) .sync_strategy(SyncStrategy::Interval(Duration::from_secs(1))) .max_key_size(64) .max_value_size(1024) } #[test] fn test_open_db_twice() { let dir = get_temporary_directory_path(); let _bc = Bitcasky::open(&dir, get_default_options()).unwrap(); let bc2 = Bitcasky::open(&dir, get_default_options()); assert!(bc2.is_err()); assert!(matches!( bc2.err(), Some(BitcaskyError::LockDirectoryFailed(_)) )); } #[test] fn test_read_write_writing_file() { let dir = get_temporary_directory_path(); let bc = Bitcasky::open(&dir, get_default_options()).unwrap(); bc.put("k1", "value1").unwrap(); bc.put("k2", "value2").unwrap(); bc.put("k3", "value3").unwrap(); bc.put("k1", "value4").unwrap(); assert_eq!(bc.get("k1").unwrap().unwrap(), "value4".as_bytes()); assert_eq!(bc.get("k2").unwrap().unwrap(), "value2".as_bytes()); assert_eq!(bc.get("k3").unwrap().unwrap(), "value3".as_bytes()); } #[test] fn test_random_put_and_delete() { let mut gen = RandomTestingDataGenerator::new( 64, 512, vec![TestingOperator::PUT, TestingOperator::DELETE], ); let ops = gen.generate_testing_operations(5000); let dir = get_temporary_directory_path(); let bc = Bitcasky::open(&dir, get_default_options()).unwrap(); execute_testing_operations(&bc, &ops); for op in ops.squash() { assert_eq!(bc.get(op.key()).unwrap().unwrap(), op.value()); } } #[test] fn test_random_put_delete_merge() { let mut gen = RandomTestingDataGenerator::new( 64, 512, vec![ TestingOperator::PUT, TestingOperator::DELETE, TestingOperator::MERGE, ], ); let ops = gen.generate_testing_operations(10); let dir = get_temporary_directory_path(); let bc = Bitcasky::open(&dir, get_default_options()).unwrap(); execute_testing_operations(&bc, &ops); for op in ops.squash() { assert_eq!(bc.get(op.key()).unwrap().unwrap(), op.value()); } } #[test] fn test_recovery() { let mut gen = RandomTestingDataGenerator::new( 64, 512, vec![TestingOperator::PUT, TestingOperator::DELETE], ); let ops = gen.generate_testing_operations(5000); let dir = get_temporary_directory_path(); { let bc = Bitcasky::open(&dir, get_default_options()).unwrap(); execute_testing_operations(&bc, &ops); } let bc = Bitcasky::open(&dir, get_default_options()).unwrap(); for op in ops.squash() { assert_eq!(bc.get(op.key()).unwrap().unwrap(), op.value()); } } #[test] fn test_delete() { let dir = get_temporary_directory_path(); let bc = Bitcasky::open(&dir, get_default_options()).unwrap(); bc.put("k1", "value1").unwrap(); bc.put("k2", "value2").unwrap(); bc.put("k3", "value3").unwrap(); bc.put("k1", "value4").unwrap(); bc.delete("k1").unwrap(); assert_eq!(bc.get("k1").unwrap(), None); bc.delete("k2").unwrap(); assert_eq!(bc.get("k2").unwrap(), None); bc.delete("k3").unwrap(); assert_eq!(bc.get("k3").unwrap(), None); } #[test] fn test_delete_not_exists_key() { let dir = get_temporary_directory_path(); let bc = Bitcasky::open(&dir, get_default_options()).unwrap(); bc.delete("k1").unwrap(); assert_eq!(bc.get("k1").unwrap(), None); bc.delete("k2").unwrap(); assert_eq!(bc.get("k2").unwrap(), None); bc.delete("k3").unwrap(); assert_eq!(bc.get("k3").unwrap(), None); } #[test] fn test_foreach_keys() { let mut gen = RandomTestingDataGenerator::new(64, 512, vec![TestingOperator::PUT]); let ops = gen.generate_testing_operations(100); let dir = get_temporary_directory_path(); let bc = Bitcasky::open(&dir, get_default_options()).unwrap(); execute_testing_operations(&bc, &ops); let mut expected_set: HashSet> = HashSet::new(); let mut actual_set: HashSet> = HashSet::new(); for op in ops.squash() { expected_set.insert(op.key()); } bc.foreach_key(|k| { actual_set.insert(k.clone()); }) .unwrap(); assert_eq!(expected_set, actual_set); } #[test] fn test_fold_keys() { let mut gen = RandomTestingDataGenerator::new(64, 512, vec![TestingOperator::PUT]); let ops = gen.generate_testing_operations(100); let dir = get_temporary_directory_path(); let bc = Bitcasky::open(&dir, get_default_options()).unwrap(); execute_testing_operations(&bc, &ops); let mut expected_set: HashSet> = HashSet::new(); let mut actual_set: HashSet> = HashSet::new(); for op in ops.squash() { expected_set.insert(op.key()); } let ret = bc .fold_key( |k, acc| { actual_set.insert(k.clone()); Ok(Some(acc.unwrap() + 1)) }, Some(0), ) .unwrap(); assert_eq!(expected_set.len(), ret.unwrap()); assert_eq!(expected_set, actual_set); } #[test] fn test_foreach() { let mut gen = RandomTestingDataGenerator::new(64, 512, vec![TestingOperator::PUT]); let ops = gen.generate_testing_operations(100); let dir = get_temporary_directory_path(); let bc = Bitcasky::open(&dir, get_default_options()).unwrap(); execute_testing_operations(&bc, &ops); let expected_pair = ops .squash() .iter() .map(|op| (op.key(), op.value())) .collect::, Vec)>>(); let mut actual_pair: Vec<(Vec, Vec)> = vec![]; bc.foreach(|k, v| { actual_pair.push((k.clone(), v.clone())); }) .unwrap(); assert_eq!(expected_pair, actual_pair); } #[test] fn test_fold() { let mut gen = RandomTestingDataGenerator::new(64, 512, vec![TestingOperator::PUT]); let ops = gen.generate_testing_operations(100); let dir = get_temporary_directory_path(); let bc = Bitcasky::open(&dir, get_default_options()).unwrap(); execute_testing_operations(&bc, &ops); let expected_pair = ops .squash() .iter() .map(|op| (op.key(), op.value())) .collect::, Vec)>>(); let mut actual_pair: Vec<(Vec, Vec)> = vec![]; let ret = bc .fold( |k, v, acc| { actual_pair.push((k.clone(), v.clone())); Ok(Some(acc.unwrap() + 1)) }, Some(0), ) .unwrap(); assert_eq!(expected_pair.len(), ret.unwrap()); assert_eq!(expected_pair, actual_pair); } #[test] fn test_dead_bytes_by_delete() { let dir = get_temporary_directory_path(); let bc = Bitcasky::open(&dir, get_default_options()).unwrap(); bc.put("k1", "value1").unwrap(); bc.put("k2", "value2").unwrap(); bc.delete("k1").unwrap(); assert!( bc.get_telemetry_data() .database .storage_aggregate .total_fragment < 1.0 ); bc.delete("k2").unwrap(); assert_eq!( 1.0, bc.get_telemetry_data() .database .storage_aggregate .total_fragment ); } #[test] fn test_dead_bytes_by_put() { let dir = get_temporary_directory_path(); let bc = Bitcasky::open(&dir, get_default_options()).unwrap(); bc.put("k1", "value1").unwrap(); bc.put("k2", "value2").unwrap(); bc.put("k1", "value3").unwrap(); bc.put("k2", "value4").unwrap(); assert_eq!( 0.5, bc.get_telemetry_data() .database .storage_aggregate .total_fragment ); }