mirror of
https://github.com/Cuprate/cuprate.git
synced 2025-03-15 16:32:23 +00:00
service: finish most tests
This commit is contained in:
parent
034b64463b
commit
889532d1be
2 changed files with 237 additions and 161 deletions
|
@ -12,7 +12,7 @@ use crate::{
|
||||||
tables::{
|
tables::{
|
||||||
call_fn_on_all_tables_or_early_return, BlockBlobs, BlockHeights, BlockInfos, KeyImages,
|
call_fn_on_all_tables_or_early_return, BlockBlobs, BlockHeights, BlockInfos, KeyImages,
|
||||||
NumOutputs, Outputs, PrunableHashes, PrunableTxBlobs, PrunedTxBlobs, RctOutputs, Tables,
|
NumOutputs, Outputs, PrunableHashes, PrunableTxBlobs, PrunedTxBlobs, RctOutputs, Tables,
|
||||||
TablesMut, TxHeights, TxIds, TxUnlockTime,
|
TablesIter, TablesMut, TxHeights, TxIds, TxUnlockTime,
|
||||||
},
|
},
|
||||||
transaction::{TxRo, TxRw},
|
transaction::{TxRo, TxRw},
|
||||||
};
|
};
|
||||||
|
@ -233,7 +233,7 @@ where
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// TODO
|
/// TODO
|
||||||
fn open_tables(&self, tx_ro: &Ro) -> Result<impl Tables, RuntimeError> {
|
fn open_tables(&self, tx_ro: &Ro) -> Result<impl TablesIter, RuntimeError> {
|
||||||
call_fn_on_all_tables_or_early_return! {
|
call_fn_on_all_tables_or_early_return! {
|
||||||
Self::open_db_ro(self, tx_ro)
|
Self::open_db_ro(self, tx_ro)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,21 @@
|
||||||
|
|
||||||
// This is only imported on `#[cfg(test)]` in `mod.rs`.
|
// This is only imported on `#[cfg(test)]` in `mod.rs`.
|
||||||
|
|
||||||
#![allow(unused_mut, clippy::significant_drop_tightening)]
|
#![allow(
|
||||||
|
clippy::significant_drop_tightening,
|
||||||
use std::sync::{
|
clippy::await_holding_lock,
|
||||||
atomic::{AtomicU64, Ordering},
|
clippy::too_many_lines
|
||||||
Arc,
|
)]
|
||||||
};
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Use
|
//---------------------------------------------------------------------------------------------------- Use
|
||||||
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicU64, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
use tower::{Service, ServiceExt};
|
use tower::{Service, ServiceExt};
|
||||||
|
|
||||||
use cuprate_test_utils::data::{block_v16_tx0, block_v1_tx2, block_v9_tx3};
|
use cuprate_test_utils::data::{block_v16_tx0, block_v1_tx2, block_v9_tx3};
|
||||||
|
@ -25,11 +32,16 @@ use cuprate_types::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
ops::block::{get_block_extended_header_from_height, get_block_info},
|
ops::{
|
||||||
|
block::{get_block_extended_header_from_height, get_block_info},
|
||||||
|
blockchain::top_block_height,
|
||||||
|
output::get_output,
|
||||||
|
},
|
||||||
service::{init, DatabaseReadHandle, DatabaseWriteHandle},
|
service::{init, DatabaseReadHandle, DatabaseWriteHandle},
|
||||||
tables::Tables,
|
tables::{KeyImages, Tables, TablesIter},
|
||||||
tests::AssertTableLen,
|
tests::AssertTableLen,
|
||||||
ConcreteEnv, DatabaseRo, Env, EnvInner, RuntimeError,
|
types::{Amount, KeyImage},
|
||||||
|
ConcreteEnv, DatabaseIter, DatabaseRo, Env, EnvInner, RuntimeError,
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Helper functions
|
//---------------------------------------------------------------------------------------------------- Helper functions
|
||||||
|
@ -47,22 +59,161 @@ fn init_service() -> (
|
||||||
(reader, writer, env, tempdir)
|
(reader, writer, env, tempdir)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a write request, and receive a response,
|
/// This is the template used in the actual test functions below.
|
||||||
/// asserting the response the expected value.
|
///
|
||||||
async fn write_request(
|
/// - Send write request(s)
|
||||||
writer: &mut DatabaseWriteHandle,
|
/// - Receive response(s)
|
||||||
block_fn: fn() -> &'static VerifiedBlockInformation,
|
/// - Assert proper tables were mutated
|
||||||
|
/// - Assert read requests lead to expected responses
|
||||||
|
#[allow(clippy::future_not_send)] // INVARIANT: tests are using a single threaded runtime
|
||||||
|
async fn test_template(
|
||||||
|
// Which block(s) to add?
|
||||||
|
block_fns: &[fn() -> &'static VerifiedBlockInformation],
|
||||||
|
// Total amount of generated coins after the block(s) have been added.
|
||||||
|
cumulative_generated_coins: u64,
|
||||||
|
// What are the table lengths be after the block(s) have been added?
|
||||||
|
assert_table_len: AssertTableLen,
|
||||||
) {
|
) {
|
||||||
|
//----------------------------------------------------------------------- Write requests
|
||||||
|
let (reader, mut writer, env, _tempdir) = init_service();
|
||||||
|
|
||||||
|
let env_inner = env.env_inner();
|
||||||
|
let tx_ro = env_inner.tx_ro().unwrap();
|
||||||
|
let tables = env_inner.open_tables(&tx_ro).unwrap();
|
||||||
|
|
||||||
// HACK: `add_block()` asserts blocks with non-sequential heights
|
// HACK: `add_block()` asserts blocks with non-sequential heights
|
||||||
// cannot be added, to get around this, manually edit the block height.
|
// cannot be added, to get around this, manually edit the block height.
|
||||||
let mut block = block_fn().clone();
|
for (i, block_fn) in block_fns.iter().enumerate() {
|
||||||
block.height = 0;
|
let mut block = block_fn().clone();
|
||||||
|
block.height = i as u64;
|
||||||
|
|
||||||
// Request a block to be written, assert it was written.
|
// Request a block to be written, assert it was written.
|
||||||
let request = WriteRequest::WriteBlock(block);
|
let request = WriteRequest::WriteBlock(block);
|
||||||
let response_channel = writer.call(request);
|
let response_channel = writer.call(request);
|
||||||
let response = response_channel.await.unwrap();
|
let response = response_channel.await.unwrap();
|
||||||
assert_eq!(response, Response::WriteBlockOk);
|
assert_eq!(response, Response::WriteBlockOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------- Reset the transaction
|
||||||
|
drop(tables);
|
||||||
|
drop(tx_ro);
|
||||||
|
let tx_ro = env_inner.tx_ro().unwrap();
|
||||||
|
let tables = env_inner.open_tables(&tx_ro).unwrap();
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------- Assert all table lengths are correct
|
||||||
|
assert_table_len.assert(&tables);
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------- Read request prep
|
||||||
|
let extended_block_header_0 = Ok(Response::BlockExtendedHeader(
|
||||||
|
get_block_extended_header_from_height(&0, &tables).unwrap(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let extended_block_header_1 = if block_fns.len() > 1 {
|
||||||
|
Ok(Response::BlockExtendedHeader(
|
||||||
|
get_block_extended_header_from_height(&1, &tables).unwrap(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(RuntimeError::KeyNotFound)
|
||||||
|
};
|
||||||
|
|
||||||
|
let block_hash_0 = Ok(Response::BlockHash(
|
||||||
|
get_block_info(&0, tables.block_infos()).unwrap().block_hash,
|
||||||
|
));
|
||||||
|
|
||||||
|
let block_hash_1 = if block_fns.len() > 1 {
|
||||||
|
Ok(Response::BlockHash(
|
||||||
|
get_block_info(&1, tables.block_infos()).unwrap().block_hash,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(RuntimeError::KeyNotFound)
|
||||||
|
};
|
||||||
|
|
||||||
|
let range_0_1 = Ok(Response::BlockExtendedHeaderInRange(vec![
|
||||||
|
get_block_extended_header_from_height(&0, &tables).unwrap(),
|
||||||
|
]));
|
||||||
|
|
||||||
|
let range_0_2 = if block_fns.len() >= 2 {
|
||||||
|
Ok(Response::BlockExtendedHeaderInRange(vec![
|
||||||
|
get_block_extended_header_from_height(&0, &tables).unwrap(),
|
||||||
|
get_block_extended_header_from_height(&1, &tables).unwrap(),
|
||||||
|
]))
|
||||||
|
} else {
|
||||||
|
Err(RuntimeError::KeyNotFound)
|
||||||
|
};
|
||||||
|
|
||||||
|
let chain_height = {
|
||||||
|
let height = top_block_height(tables.block_heights()).unwrap();
|
||||||
|
let block_info = get_block_info(&height, tables.block_infos()).unwrap();
|
||||||
|
Ok(Response::ChainHeight(height, block_info.block_hash))
|
||||||
|
};
|
||||||
|
|
||||||
|
let cumulative_generated_coins = Ok(Response::GeneratedCoins(cumulative_generated_coins));
|
||||||
|
|
||||||
|
// let outputs = tables
|
||||||
|
// .outputs_iter()
|
||||||
|
// .keys()
|
||||||
|
// .unwrap()
|
||||||
|
// .map(Result::unwrap)
|
||||||
|
// .map(key.amount);
|
||||||
|
// .collect::<HashMap<Amount, HashSet<u64>>>();
|
||||||
|
|
||||||
|
let num_req = tables
|
||||||
|
.outputs_iter()
|
||||||
|
.keys()
|
||||||
|
.unwrap()
|
||||||
|
.map(Result::unwrap)
|
||||||
|
.map(|key| key.amount)
|
||||||
|
.collect::<Vec<Amount>>();
|
||||||
|
|
||||||
|
let num_resp = Ok(Response::NumberOutputsWithAmount(
|
||||||
|
num_req
|
||||||
|
.iter()
|
||||||
|
.map(|amount| match tables.num_outputs().get(amount) {
|
||||||
|
// INVARIANT: #[cfg] @ lib.rs asserts `usize == u64`
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
Ok(count) => (*amount, count as usize),
|
||||||
|
Err(RuntimeError::KeyNotFound) => (*amount, 0),
|
||||||
|
Err(e) => panic!(),
|
||||||
|
})
|
||||||
|
.collect::<HashMap<Amount, usize>>(),
|
||||||
|
));
|
||||||
|
|
||||||
|
// Contains a fake non-spent key-image.
|
||||||
|
let ki_req = HashSet::from([[0; 32]]);
|
||||||
|
let ki_resp = Ok(Response::CheckKIsNotSpent(true));
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------- Assert expected response
|
||||||
|
// Assert read requests lead to the expected responses.
|
||||||
|
for (request, expected_response) in [
|
||||||
|
(ReadRequest::BlockExtendedHeader(0), extended_block_header_0),
|
||||||
|
(ReadRequest::BlockExtendedHeader(1), extended_block_header_1),
|
||||||
|
(ReadRequest::BlockHash(0), block_hash_0),
|
||||||
|
(ReadRequest::BlockHash(1), block_hash_1),
|
||||||
|
(ReadRequest::BlockExtendedHeaderInRange(0..1), range_0_1),
|
||||||
|
(ReadRequest::BlockExtendedHeaderInRange(0..2), range_0_2),
|
||||||
|
(ReadRequest::ChainHeight, chain_height),
|
||||||
|
(ReadRequest::GeneratedCoins, cumulative_generated_coins),
|
||||||
|
// (ReadRequest::Outputs(HashMap<u64, HashSet<u64>>), ),
|
||||||
|
(ReadRequest::NumberOutputsWithAmount(num_req), num_resp),
|
||||||
|
(ReadRequest::CheckKIsNotSpent(ki_req), ki_resp),
|
||||||
|
] {
|
||||||
|
let response = reader.clone().oneshot(request).await;
|
||||||
|
println!("response: {response:#?}, expected_response: {expected_response:#?}");
|
||||||
|
match response {
|
||||||
|
Ok(resp) => assert_eq!(resp, expected_response.unwrap()),
|
||||||
|
Err(ref e) => assert!(matches!(response, expected_response)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------- Key image checks
|
||||||
|
// Assert each key image we inserted comes back as "spent".
|
||||||
|
for key_image in tables.key_images_iter().keys().unwrap() {
|
||||||
|
let key_image = key_image.unwrap();
|
||||||
|
let request = ReadRequest::CheckKIsNotSpent(HashSet::from([key_image]));
|
||||||
|
let response = reader.clone().oneshot(request).await;
|
||||||
|
println!("response: {response:#?}, key_image: {key_image:#?}");
|
||||||
|
assert_eq!(response.unwrap(), Response::CheckKIsNotSpent(false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Tests
|
//---------------------------------------------------------------------------------------------------- Tests
|
||||||
|
@ -77,152 +228,77 @@ fn init_drop() {
|
||||||
/// Assert write/read correctness of [`block_v1_tx2`].
|
/// Assert write/read correctness of [`block_v1_tx2`].
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn v1_tx2() {
|
async fn v1_tx2() {
|
||||||
let (reader, mut writer, env, _tempdir) = init_service();
|
test_template(
|
||||||
|
&[block_v1_tx2],
|
||||||
write_request(&mut writer, block_v1_tx2).await;
|
13_138_270_467_918,
|
||||||
|
AssertTableLen {
|
||||||
// Assert the actual database tables were correctly modified.
|
block_infos: 1,
|
||||||
let env_inner = env.env_inner();
|
block_blobs: 1,
|
||||||
let tx_ro = env_inner.tx_ro().unwrap();
|
block_heights: 1,
|
||||||
let tables = env_inner.open_tables(&tx_ro).unwrap();
|
key_images: 65,
|
||||||
|
num_outputs: 38,
|
||||||
AssertTableLen {
|
pruned_tx_blobs: 0,
|
||||||
block_infos: 1,
|
prunable_hashes: 0,
|
||||||
block_blobs: 1,
|
outputs: 107,
|
||||||
block_heights: 1,
|
prunable_tx_blobs: 0,
|
||||||
key_images: 65,
|
rct_outputs: 0,
|
||||||
num_outputs: 38,
|
tx_blobs: 2,
|
||||||
pruned_tx_blobs: 0,
|
tx_ids: 2,
|
||||||
prunable_hashes: 0,
|
tx_heights: 2,
|
||||||
outputs: 107,
|
tx_unlock_time: 0,
|
||||||
prunable_tx_blobs: 0,
|
},
|
||||||
rct_outputs: 0,
|
)
|
||||||
tx_blobs: 2,
|
.await;
|
||||||
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`].
|
/// Assert write/read correctness of [`block_v9_tx3`].
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn v9_tx3() {
|
async fn v9_tx3() {
|
||||||
let (reader, mut writer, env, _tempdir) = init_service();
|
test_template(
|
||||||
|
&[block_v9_tx3],
|
||||||
write_request(&mut writer, block_v9_tx3).await;
|
3_403_774_022_163,
|
||||||
|
AssertTableLen {
|
||||||
// Assert the actual database tables were correctly modified.
|
block_infos: 1,
|
||||||
let env_inner = env.env_inner();
|
block_blobs: 1,
|
||||||
let tx_ro = env_inner.tx_ro().unwrap();
|
block_heights: 1,
|
||||||
let tables = env_inner.open_tables(&tx_ro).unwrap();
|
key_images: 4,
|
||||||
|
num_outputs: 0,
|
||||||
assert_eq!(tables.block_infos().len().unwrap(), 1);
|
pruned_tx_blobs: 0,
|
||||||
assert_eq!(tables.block_blobs().len().unwrap(), 1);
|
prunable_hashes: 0,
|
||||||
assert_eq!(tables.block_heights().len().unwrap(), 1);
|
outputs: 0,
|
||||||
assert_eq!(tables.key_images().len().unwrap(), 4);
|
prunable_tx_blobs: 0,
|
||||||
assert_eq!(tables.num_outputs().len().unwrap(), 0);
|
rct_outputs: 6,
|
||||||
assert_eq!(tables.pruned_tx_blobs().len().unwrap(), 0);
|
tx_blobs: 3,
|
||||||
assert_eq!(tables.prunable_hashes().len().unwrap(), 0);
|
tx_ids: 3,
|
||||||
assert_eq!(tables.outputs().len().unwrap(), 0);
|
tx_heights: 3,
|
||||||
assert_eq!(tables.prunable_tx_blobs().len().unwrap(), 0);
|
tx_unlock_time: 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);
|
.await;
|
||||||
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`].
|
/// Assert write/read correctness of [`block_v16_tx0`].
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn v16_tx0() {
|
async fn v16_tx0() {
|
||||||
let (reader, mut writer, env, _tempdir) = init_service();
|
test_template(
|
||||||
|
&[block_v16_tx0],
|
||||||
write_request(&mut writer, block_v16_tx0).await;
|
600_000_000_000,
|
||||||
|
AssertTableLen {
|
||||||
// Assert the actual database tables were correctly modified.
|
block_infos: 1,
|
||||||
let env_inner = env.env_inner();
|
block_blobs: 1,
|
||||||
let tx_ro = env_inner.tx_ro().unwrap();
|
block_heights: 1,
|
||||||
let tables = env_inner.open_tables(&tx_ro).unwrap();
|
key_images: 0,
|
||||||
|
num_outputs: 0,
|
||||||
assert_eq!(tables.block_infos().len().unwrap(), 1);
|
pruned_tx_blobs: 0,
|
||||||
assert_eq!(tables.block_blobs().len().unwrap(), 1);
|
prunable_hashes: 0,
|
||||||
assert_eq!(tables.block_heights().len().unwrap(), 1);
|
outputs: 0,
|
||||||
assert_eq!(tables.key_images().len().unwrap(), 0);
|
prunable_tx_blobs: 0,
|
||||||
assert_eq!(tables.num_outputs().len().unwrap(), 0);
|
rct_outputs: 0,
|
||||||
assert_eq!(tables.pruned_tx_blobs().len().unwrap(), 0);
|
tx_blobs: 0,
|
||||||
assert_eq!(tables.prunable_hashes().len().unwrap(), 0);
|
tx_ids: 0,
|
||||||
assert_eq!(tables.outputs().len().unwrap(), 0);
|
tx_heights: 0,
|
||||||
assert_eq!(tables.prunable_tx_blobs().len().unwrap(), 0);
|
tx_unlock_time: 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);
|
.await;
|
||||||
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,
|
|
||||||
// /// asserting the response the expected value.
|
|
||||||
// #[tokio::test]
|
|
||||||
// async fn read_request() {
|
|
||||||
// let (reader, writer, _tempdir) = init_service();
|
|
||||||
|
|
||||||
// for (request, expected_response) in [
|
|
||||||
// (ReadRequest::Example1, Response::Example1),
|
|
||||||
// (ReadRequest::Example2(123), Response::Example2(123)),
|
|
||||||
// (
|
|
||||||
// ReadRequest::Example3("hello".into()),
|
|
||||||
// Response::Example3("hello".into()),
|
|
||||||
// ),
|
|
||||||
// ] {
|
|
||||||
// // This calls `poll_ready()` asserting we have a permit before `call()`.
|
|
||||||
// let response_channel = reader.clone().oneshot(request);
|
|
||||||
// let response = response_channel.await.unwrap();
|
|
||||||
// assert_eq!(response, expected_response);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
Loading…
Reference in a new issue