service: initial tests

This commit is contained in:
hinto.janai 2024-04-24 16:19:00 -04:00
parent c485b951f3
commit ae2a0be37e
No known key found for this signature in database
GPG key ID: D47CE05FA175A499
2 changed files with 179 additions and 41 deletions

View file

@ -121,23 +121,15 @@ impl DatabaseReadHandle {
} }
} }
/// TODO /// Access to the actual database environment.
///
/// TODO: we need this for testing but should we allow it publicly?
/// Or within `crate`? It allows anyone to start tampering with the
/// database directly instead of going through `service`.
#[inline] #[inline]
pub const fn env(&self) -> &Arc<ConcreteEnv> { pub const fn env(&self) -> &Arc<ConcreteEnv> {
&self.env &self.env
} }
/// TODO
#[inline]
pub const fn semaphore(&self) -> &PollSemaphore {
&self.semaphore
}
/// TODO
#[inline]
pub const fn permit(&self) -> &Option<OwnedSemaphorePermit> {
&self.permit
}
} }
impl tower::Service<ReadRequest> for DatabaseReadHandle { impl tower::Service<ReadRequest> for DatabaseReadHandle {

View file

@ -9,36 +9,202 @@
#![allow(unused_mut, clippy::significant_drop_tightening)] #![allow(unused_mut, clippy::significant_drop_tightening)]
use std::sync::{
atomic::{AtomicU64, Ordering},
Arc,
};
//---------------------------------------------------------------------------------------------------- Use //---------------------------------------------------------------------------------------------------- Use
use tower::{Service, ServiceExt}; use tower::{Service, ServiceExt};
use cuprate_types::service::{ReadRequest, Response, WriteRequest}; use cuprate_test_utils::data::{block_v16_tx0, block_v1_tx2, block_v9_tx3};
use cuprate_types::{
service::{ReadRequest, Response, WriteRequest},
ExtendedBlockHeader, VerifiedBlockInformation,
};
use crate::{ use crate::{
config::Config, config::Config,
ops::block::{get_block_extended_header_from_height, get_block_info},
service::{init, DatabaseReadHandle, DatabaseWriteHandle}, service::{init, DatabaseReadHandle, DatabaseWriteHandle},
tables::Tables,
tests::AssertTableLen,
ConcreteEnv, DatabaseRo, Env, EnvInner, RuntimeError,
}; };
//---------------------------------------------------------------------------------------------------- Tests //---------------------------------------------------------------------------------------------------- Helper functions
/// Initialize the `service`. /// Initialize the `service`.
fn init_service() -> (DatabaseReadHandle, DatabaseWriteHandle, tempfile::TempDir) { fn init_service() -> (
DatabaseReadHandle,
DatabaseWriteHandle,
Arc<ConcreteEnv>,
tempfile::TempDir,
) {
let tempdir = tempfile::tempdir().unwrap(); let tempdir = tempfile::tempdir().unwrap();
let config = Config::low_power(Some(tempdir.path().into())); let config = Config::low_power(Some(tempdir.path().into()));
let (reader, writer) = init(config).unwrap(); let (reader, writer) = init(config).unwrap();
(reader, writer, tempdir) let env = reader.env().clone();
(reader, writer, env, tempdir)
} }
/// Send a write request, and receive a response,
/// asserting the response the expected value.
async fn write_request(
writer: &mut DatabaseWriteHandle,
block_fn: fn() -> &'static VerifiedBlockInformation,
) {
// HACK: `add_block()` asserts blocks with non-sequential heights
// cannot be added, to get around this, manually edit the block height.
let mut block = block_fn().clone();
block.height = 0;
// Request a block to be written, assert it was written.
let request = WriteRequest::WriteBlock(block);
let response_channel = writer.call(request);
let response = response_channel.await.unwrap();
assert_eq!(response, Response::WriteBlockOk);
}
//---------------------------------------------------------------------------------------------------- Tests
/// Simply `init()` the service and then drop it. /// Simply `init()` the service and then drop it.
/// ///
/// If this test fails, something is very wrong. /// If this test fails, something is very wrong.
#[test] #[test]
fn init_drop() { fn init_drop() {
let (reader, writer, _tempdir) = init_service(); let (reader, writer, env, _tempdir) = init_service();
} }
// TODO: /// Assert write/read correctness of [`block_v1_tx2`].
// un-comment and fix these tests when all `{read,write}` #[tokio::test]
// service functions are implemented. async fn v1_tx2() {
let (reader, mut writer, env, _tempdir) = init_service();
write_request(&mut writer, block_v1_tx2).await;
// Assert the actual database tables were correctly modified.
let env_inner = env.env_inner();
let tx_ro = env_inner.tx_ro().unwrap();
let tables = env_inner.open_tables(&tx_ro).unwrap();
AssertTableLen {
block_infos: 1,
block_blobs: 1,
block_heights: 1,
key_images: 65,
num_outputs: 38,
pruned_tx_blobs: 0,
prunable_hashes: 0,
outputs: 107,
prunable_tx_blobs: 0,
rct_outputs: 0,
tx_blobs: 2,
tx_ids: 2,
tx_heights: 2,
tx_unlock_time: 0,
}
.assert(&tables);
let height = 0;
let extended_block_header = get_block_extended_header_from_height(&height, &tables).unwrap();
let block_info = get_block_info(&height, tables.block_infos()).unwrap();
// Assert reads are correct.
for (request, expected_response) in [
// Each tuple is a `Request` + `Result<Response, RuntimeError>` pair.
(
ReadRequest::BlockExtendedHeader(0), // The request to send to the service
Ok(Response::BlockExtendedHeader(extended_block_header)), // The expected response
),
(
ReadRequest::BlockExtendedHeader(1),
Err(RuntimeError::KeyNotFound),
),
(
ReadRequest::BlockHash(0),
Ok(Response::BlockHash(block_info.block_hash)),
),
(ReadRequest::BlockHash(1), Err(RuntimeError::KeyNotFound)),
(
ReadRequest::BlockExtendedHeaderInRange(0..1),
Ok(Response::BlockExtendedHeaderInRange(vec![
extended_block_header,
])),
),
(
ReadRequest::BlockExtendedHeaderInRange(0..2),
Err(RuntimeError::KeyNotFound),
),
(
ReadRequest::ChainHeight,
Ok(Response::ChainHeight(height, block_info.block_hash)),
),
(ReadRequest::GeneratedCoins, Ok(Response::GeneratedCoins(0))),
// (ReadRequest::Outputs(HashMap<u64, HashSet<u64>>), ),
// (ReadRequest::NumberOutputsWithAmount(Vec<u64>), ),
// (ReadRequest::CheckKIsNotSpent(HashSet<[u8; 32]>), ),
] {
let response_channel = reader.clone().oneshot(request);
let response = response_channel.await;
println!("response: {response:#?}, expected_response: {expected_response:#?}");
assert!(matches!(response, expected_response));
}
}
/// Assert write/read correctness of [`block_v9_tx3`].
#[tokio::test]
async fn v9_tx3() {
let (reader, mut writer, env, _tempdir) = init_service();
write_request(&mut writer, block_v9_tx3).await;
// Assert the actual database tables were correctly modified.
let env_inner = env.env_inner();
let tx_ro = env_inner.tx_ro().unwrap();
let tables = env_inner.open_tables(&tx_ro).unwrap();
assert_eq!(tables.block_infos().len().unwrap(), 1);
assert_eq!(tables.block_blobs().len().unwrap(), 1);
assert_eq!(tables.block_heights().len().unwrap(), 1);
assert_eq!(tables.key_images().len().unwrap(), 4);
assert_eq!(tables.num_outputs().len().unwrap(), 0);
assert_eq!(tables.pruned_tx_blobs().len().unwrap(), 0);
assert_eq!(tables.prunable_hashes().len().unwrap(), 0);
assert_eq!(tables.outputs().len().unwrap(), 0);
assert_eq!(tables.prunable_tx_blobs().len().unwrap(), 0);
assert_eq!(tables.rct_outputs().len().unwrap(), 6);
assert_eq!(tables.tx_blobs().len().unwrap(), 3);
assert_eq!(tables.tx_ids().len().unwrap(), 3);
assert_eq!(tables.tx_heights().len().unwrap(), 3);
assert_eq!(tables.tx_unlock_time().len().unwrap(), 0);
}
/// Assert write/read correctness of [`block_v16_tx0`].
#[tokio::test]
async fn v16_tx0() {
let (reader, mut writer, env, _tempdir) = init_service();
write_request(&mut writer, block_v16_tx0).await;
// Assert the actual database tables were correctly modified.
let env_inner = env.env_inner();
let tx_ro = env_inner.tx_ro().unwrap();
let tables = env_inner.open_tables(&tx_ro).unwrap();
assert_eq!(tables.block_infos().len().unwrap(), 1);
assert_eq!(tables.block_blobs().len().unwrap(), 1);
assert_eq!(tables.block_heights().len().unwrap(), 1);
assert_eq!(tables.key_images().len().unwrap(), 0);
assert_eq!(tables.num_outputs().len().unwrap(), 0);
assert_eq!(tables.pruned_tx_blobs().len().unwrap(), 0);
assert_eq!(tables.prunable_hashes().len().unwrap(), 0);
assert_eq!(tables.outputs().len().unwrap(), 0);
assert_eq!(tables.prunable_tx_blobs().len().unwrap(), 0);
assert_eq!(tables.rct_outputs().len().unwrap(), 0);
assert_eq!(tables.tx_blobs().len().unwrap(), 0);
assert_eq!(tables.tx_ids().len().unwrap(), 0);
assert_eq!(tables.tx_heights().len().unwrap(), 0);
assert_eq!(tables.tx_unlock_time().len().unwrap(), 0);
}
// /// Send a read request, and receive a response, // /// Send a read request, and receive a response,
// /// asserting the response the expected value. // /// asserting the response the expected value.
@ -60,23 +226,3 @@ fn init_drop() {
// assert_eq!(response, expected_response); // assert_eq!(response, expected_response);
// } // }
// } // }
// /// Send a write request, and receive a response,
// /// asserting the response the expected value.
// #[tokio::test]
// async fn write_request() {
// let (reader, mut writer, _tempdir) = init_service();
// for (request, expected_response) in [
// (WriteRequest::Example1, Response::Example1),
// (WriteRequest::Example2(123), Response::Example2(123)),
// (
// WriteRequest::Example3("hello".into()),
// Response::Example3("hello".into()),
// ),
// ] {
// let response_channel = writer.call(request);
// let response = response_channel.await.unwrap();
// assert_eq!(response, expected_response);
// }
// }