Skip to content

Commit

Permalink
add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
TangentW committed Sep 21, 2023
1 parent 66909ea commit 6545697
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 9 deletions.
2 changes: 1 addition & 1 deletion thetadb-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{medium, meta, tx};
pub type Result<T> = std::result::Result<T, Error>;

/// All possible error cases that can be return by API calls in ThetaDB.
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorCode {
/// An error occured during an I/O operation.
IO,
Expand Down
90 changes: 82 additions & 8 deletions thetadb-core/tests/thetadb_apis.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{collections::HashMap, fs};
use std::{collections::HashMap, fs, io::Write};

use rand::Rng;
use thetadb::{Result, ThetaDB, MAX_KEY_LEN};
use thetadb::{ErrorCode, Result, ThetaDB, MAX_KEY_LEN, MAX_VALUE_LEN};

// Here are the highest level APIs tests.
// Some `mod`s also have their own tests inside.
Expand All @@ -13,10 +13,15 @@ fn test_crud() -> Result<()> {
let key_value_pairs = obtain_key_value_pairs(500, MAX_KEY_LEN, page_size as usize);

// Test `put` and `get`.
for (key, _) in &key_value_pairs {
assert!(!db.contains(key)?);
assert_eq!(db.get(key)?, None);
}
for (key, value) in &key_value_pairs {
db.put(key, value)?;
}
for (key, value) in &key_value_pairs {
assert!(db.contains(key)?);
assert_eq!(db.get(key)?.as_ref(), Some(value));
}

Expand All @@ -28,9 +33,11 @@ fn test_crud() -> Result<()> {
db.delete(key)?;
}
for (key, value) in key_value_pairs {
assert!(db.contains(key)?);
assert_eq!(db.get(key)?.as_ref(), Some(value));
}
for (key, _) in deleted_pairs {
assert!(!db.contains(key)?);
assert_eq!(db.get(key)?.as_ref(), None);
}

Expand All @@ -40,6 +47,7 @@ fn test_crud() -> Result<()> {
db.put(key, value)?;
}
for (key, value) in &new_key_value_pairs {
assert!(db.contains(key)?);
assert_eq!(db.get(key)?.as_ref(), Some(value));
}

Expand All @@ -59,33 +67,99 @@ fn test_crud() -> Result<()> {
fn test_cursor() -> Result<()> {
test_db("test_cursor.theta", |db| {
let page_size = db.debugger()?.page_size()?;
let key_value_pairs = obtain_key_value_pairs(500, MAX_KEY_LEN, page_size as usize);
let mut key_value_pairs = obtain_key_value_pairs(500, MAX_KEY_LEN, page_size as usize);

for (key, value) in &key_value_pairs {
db.put(key, value)?;
}

let mut values = Vec::new();

// Test forward traversal.
let mut cursor = db.first_cursor()?;
while let Some(value) = cursor.value()? {
values.push(value);
cursor.next()?;
}

let mut key_value_pairs = key_value_pairs.into_iter().collect::<Vec<_>>();
key_value_pairs.sort_by(|l, r| l.0.cmp(&r.0));
let sorted_values = key_value_pairs.into_iter().map(|x| x.1).collect::<Vec<_>>();
assert_eq!(
values,
key_value_pairs
.iter()
.map(|x| x.1.clone())
.collect::<Vec<_>>()
);
values.clear();

// Test backward traversal.
let mut cursor = db.last_cursor()?;
while let Some(value) = cursor.value()? {
values.push(value);
cursor.prev()?;
}

key_value_pairs.sort_by(|l, r| r.0.cmp(&l.0));
assert_eq!(
values,
key_value_pairs
.iter()
.map(|x| x.1.clone())
.collect::<Vec<_>>()
);

Ok(())
})
}

#[test]
fn test_open_invalid_file() {
let path = format!("target/invalid.theta");
let mut file = fs::OpenOptions::new()
.write(true)
.create(true)
.open(&path)
.unwrap();
file.write(b"ABCD1234").unwrap();
drop(file);

assert_eq!(
ThetaDB::open(&path).err().unwrap().code(),
ErrorCode::FileUnexpected
);
_ = fs::remove_file(&path);
}

#[test]
fn test_put_large_key_value() -> Result<()> {
test_db("test_error.theta", |db| {
let key = vec![1; MAX_KEY_LEN];
db.put(key, b"")?;

let key = vec![1; MAX_KEY_LEN + 1];
assert_eq!(
db.put(key, b"").err().unwrap().code(),
ErrorCode::InputInvalid
);

let value = vec![1; MAX_VALUE_LEN];
db.put(b"key", value)?;

let value = vec![1; MAX_VALUE_LEN + 1];
assert_eq!(
db.put(b"key", value).err().unwrap().code(),
ErrorCode::InputInvalid
);

assert_eq!(values, sorted_values);
Ok(())
})
}

fn test_db(name: &str, test: impl FnOnce(ThetaDB) -> Result<()>) -> Result<()> {
let path = format!("target/{name}");
ThetaDB::open(&path).and_then(test)?;
let res = ThetaDB::open(&path).and_then(test);
_ = fs::remove_file(&path);
Ok(())
res
}

fn obtain_key_value_pairs(
Expand Down

0 comments on commit 6545697

Please sign in to comment.