Skip to content

Commit

Permalink
Add unit test for jds-module: lib/mods and lib/status
Browse files Browse the repository at this point in the history
  • Loading branch information
Shourya742 committed Jun 27, 2024
1 parent f7d8378 commit 7022c76
Show file tree
Hide file tree
Showing 2 changed files with 353 additions and 1 deletion.
73 changes: 73 additions & 0 deletions roles/jd-server/src/lib/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,76 @@ where
_ => Err(serde::de::Error::custom("Unsupported duration unit")),
}
}

#[cfg(test)]
mod tests {
use std::path::PathBuf;

use super::*;

fn load_config(path: &str) -> Configuration {
let config_path = PathBuf::from(path);
assert!(
config_path.exists(),
"No config file found at {:?}",
config_path
);

let config_string =
std::fs::read_to_string(config_path).expect("Failed to read the config file");
toml::from_str(&config_string).expect("Failed to parse config")
}

#[test]
fn test_get_coinbase_output_non_empty() {
let config = load_config("config-examples/jds-config-hosted-example.toml");
let outputs = get_coinbase_output(&config).expect("Failed to get coinbase output");

let expected_output = CoinbaseOutput_ {
output_script_type: "P2WPKH".to_string(),
output_script_value:
"036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(),
};
let expected_script: Script = expected_output.try_into().unwrap();
let expected_transaction_output = TxOut {
value: 0,
script_pubkey: expected_script,
};

assert_eq!(outputs[0], expected_transaction_output);
}

#[test]
fn test_get_coinbase_output_empty() {
let mut config = load_config("config-examples/jds-config-hosted-example.toml");
config.coinbase_outputs.clear();

let result = get_coinbase_output(&config);
assert!(
matches!(result, Err(Error::EmptyCoinbaseOutputs)),
"Expected an error for empty coinbase outputs"
);
}

#[test]
fn test_try_from_valid_input() {
let input = CoinbaseOutput {
output_script_type: "P2PKH".to_string(),
output_script_value:
"036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(),
};
let result: Result<CoinbaseOutput_, _> = (&input).try_into();
assert!(result.is_ok());
}

#[test]
fn test_try_from_invalid_input() {
let input = CoinbaseOutput {
output_script_type: "INVALID".to_string(),
output_script_value:
"036adc3bdf21e6f9a0f0fb0066bf517e5b7909ed1563d6958a10993849a7554075".to_string(),
};
let result: Result<CoinbaseOutput_, _> = (&input).try_into();
assert!(matches!(result, Err(Error::UnknownOutputScriptType)));
}
}
281 changes: 280 additions & 1 deletion roles/jd-server/src/lib/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub struct Status {
pub state: State,
}

/// this function is used to discern which componnent experienced the event.
/// this function is used to discern which component experienced the event.
/// With this knowledge we can wrap the status message with information (`State` variants) so
/// the main status loop can decide what should happen
async fn send_status(
Expand Down Expand Up @@ -129,3 +129,282 @@ pub async fn handle_error(sender: &Sender, e: JdsError) -> error_handling::Error
}
}
}

#[cfg(test)]
mod tests {
use std::{convert::TryInto, io::Error};

use super::*;
use async_channel::{bounded, RecvError};
use roles_logic_sv2::mining_sv2::OpenMiningChannelError;

#[tokio::test]
async fn test_send_status_downstream_listener_shutdown() {
let (tx, rx) = bounded(1);
let sender = Sender::DownstreamListener(tx);
let error = JdsError::ChannelRecv(async_channel::RecvError);

send_status(&sender, error, error_handling::ErrorBranch::Continue).await;
match rx.recv().await {
Ok(status) => match status.state {
State::DownstreamShutdown(e) => {
assert_eq!(e.to_string(), "Channel recv failed: `RecvError`")
}
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_send_status_upstream_shutdown() {
let (tx, rx) = bounded(1);
let sender = Sender::Upstream(tx);
let error = JdsError::MempoolError(crate::mempool::error::JdsMempoolError::EmptyMempool);
let error_string = error.to_string();
send_status(&sender, error, error_handling::ErrorBranch::Continue).await;

match rx.recv().await {
Ok(status) => match status.state {
State::TemplateProviderShutdown(e) => assert_eq!(e.to_string(), error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_handle_error_io_error() {
let (tx, rx) = bounded(1);
let sender = Sender::Downstream(tx);
let error = JdsError::Io(Error::new(std::io::ErrorKind::Interrupted, "IO error"));
let error_string = error.to_string();

handle_error(&sender, error).await;
match rx.recv().await {
Ok(status) => match status.state {
State::Healthy(e) => assert_eq!(e, error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_handle_error_channel_send_error() {
let (tx, rx) = bounded(1);
let sender = Sender::Downstream(tx);
let error = JdsError::ChannelSend(Box::new("error"));
let error_string = error.to_string();

handle_error(&sender, error).await;
match rx.recv().await {
Ok(status) => match status.state {
State::Healthy(e) => assert_eq!(e, error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_handle_error_channel_receive_error() {
let (tx, rx) = bounded(1);
let sender = Sender::Downstream(tx);
let error = JdsError::ChannelRecv(RecvError);
let error_string = error.to_string();

handle_error(&sender, error).await;
match rx.recv().await {
Ok(status) => match status.state {
State::DownstreamShutdown(e) => assert_eq!(e.to_string(), error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_handle_error_binary_sv2_error() {
let (tx, rx) = bounded(1);
let sender = Sender::Downstream(tx);
let error = JdsError::BinarySv2(binary_sv2::Error::IoError);
let error_string = error.to_string();
handle_error(&sender, error).await;
match rx.recv().await {
Ok(status) => match status.state {
State::Healthy(e) => assert_eq!(e, error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_handle_error_codec_error() {
let (tx, rx) = bounded(1);
let sender = Sender::Downstream(tx);
let error = JdsError::Codec(codec_sv2::Error::InvalidStepForInitiator);
let error_string = error.to_string();
handle_error(&sender, error).await;
match rx.recv().await {
Ok(status) => match status.state {
State::Healthy(e) => assert_eq!(e, error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_handle_error_noise_error() {
let (tx, rx) = bounded(1);
let sender = Sender::Downstream(tx);
let error = JdsError::Noise(noise_sv2::Error::HandshakeNotFinalized);
let error_string = error.to_string();
handle_error(&sender, error).await;
match rx.recv().await {
Ok(status) => match status.state {
State::Healthy(e) => assert_eq!(e, error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_handle_error_roles_logic_error() {
let (tx, rx) = bounded(1);
let sender = Sender::Downstream(tx);
let error = JdsError::RolesLogic(roles_logic_sv2::Error::BadPayloadSize);
let error_string = error.to_string();
handle_error(&sender, error).await;
match rx.recv().await {
Ok(status) => match status.state {
State::Healthy(e) => assert_eq!(e, error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_handle_error_custom_error() {
let (tx, rx) = bounded(1);
let sender = Sender::Downstream(tx);
let error = JdsError::Custom("error".to_string());
let error_string = error.to_string();
handle_error(&sender, error).await;
match rx.recv().await {
Ok(status) => match status.state {
State::Healthy(e) => assert_eq!(e, error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_handle_error_framing_error() {
let (tx, rx) = bounded(1);
let sender = Sender::Downstream(tx);
let error = JdsError::Framing(codec_sv2::framing_sv2::Error::ExpectedHandshakeFrame);
let error_string = error.to_string();
handle_error(&sender, error).await;
match rx.recv().await {
Ok(status) => match status.state {
State::Healthy(e) => assert_eq!(e, error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_handle_error_poison_lock_error() {
let (tx, rx) = bounded(1);
let sender = Sender::Downstream(tx);
let error = JdsError::PoisonLock("error".to_string());
let error_string = error.to_string();
handle_error(&sender, error).await;
match rx.recv().await {
Ok(status) => match status.state {
State::Healthy(e) => assert_eq!(e, error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_handle_error_impossible_to_reconstruct_block_error() {
let (tx, rx) = bounded(1);
let sender = Sender::Downstream(tx);
let error = JdsError::ImpossibleToReconstructBlock("Impossible".to_string());
let error_string = error.to_string();
handle_error(&sender, error).await;
match rx.recv().await {
Ok(status) => match status.state {
State::Healthy(e) => assert_eq!(e, error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_handle_error_no_last_declared_job_error() {
let (tx, rx) = bounded(1);
let sender = Sender::Downstream(tx);
let error = JdsError::NoLastDeclaredJob;
let error_string = error.to_string();
handle_error(&sender, error).await;
match rx.recv().await {
Ok(status) => match status.state {
State::Healthy(e) => assert_eq!(e, error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_handle_error_last_mempool_error() {
let (tx, rx) = bounded(1);
let sender = Sender::Downstream(tx);
let error = JdsError::MempoolError(crate::mempool::error::JdsMempoolError::EmptyMempool);
let error_string = error.to_string();
handle_error(&sender, error).await;
match rx.recv().await {
Ok(status) => match status.state {
State::TemplateProviderShutdown(e) => assert_eq!(e.to_string(), error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}

#[tokio::test]
async fn test_handle_error_sv2_protocol_error() {
let (tx, rx) = bounded(1);
let sender = Sender::Downstream(tx);
let inner: [u8; 32] = rand::random();
let value = inner.to_vec().try_into().unwrap();
let error = JdsError::Sv2ProtocolError((
12,
Mining::OpenMiningChannelError(OpenMiningChannelError {
request_id: 1,
error_code: value,
}),
));
let error_string = "12";
handle_error(&sender, error).await;
match rx.recv().await {
Ok(status) => match status.state {
State::DownstreamInstanceDropped(e) => assert_eq!(e.to_string(), error_string),
_ => panic!("Unexpected state received"),
},
Err(_) => panic!("Failed to receive status"),
}
}
}

0 comments on commit 7022c76

Please sign in to comment.