explorerd/test: introduce auxiliary RPC parameter validation functions and add shared Explorerd instance support

This commit enhances `test_utils` module by introducing a shared `Explorerd` instance and auxiliary functions for verifying invalid JSON-RPC parameter handling. By initializing the `Explorerd` once, tests can run asynchronously. The auxiliary functions cover invalid or missing parameters, ensuring that the Explorerd API correctly handles and reports invalid inputs.

Updates include:
- Defined a global `Explorerd` instance that is shared across tests, improving efficiency
- Updated the test setup so the `Explorerd` instance is initialized on the first invocation and then shared among subsequent calls
- Added `validate_invalid_rpc_parameter` to test JSON-RPC methods for invalid parameter handling, verifying correct error codes and messages
- Added `validate_empty_rpc_parameters`, which checks whether methods that expect no parameters properly reject non-empty parameters
- Added helper functions (`validate_invalid_rpc_contract_id`, `validate_invalid_rpc_header_hash`, `validate_invalid_rpc_tx_hash`, and `validate_invalid_rpc_hash_parameter`) to test invalid hash parameter handling
- Temporarily added `#[allow(dead_code)]` for `test_util` until the test code that utilizes the new utility functions is checked in
This commit is contained in:
kalm
2025-03-24 04:42:57 -07:00
parent ea1433d25f
commit ba628d0d9d
2 changed files with 174 additions and 0 deletions

View File

@@ -55,6 +55,8 @@ mod error;
/// Test utilities used for unit and integration testing
#[cfg(test)]
#[allow(dead_code)]
// TODO: Remove when test code that utilizes the new utility functions is checked in
mod test_utils;
const CONFIG_FILE: &str = "explorerd_config.toml";

View File

@@ -16,6 +16,27 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use std::sync::{Arc, Mutex};
use lazy_static::lazy_static;
use smol::Executor;
use tinyjson::JsonValue;
use url::Url;
use darkfi::rpc::{
jsonrpc::{ErrorCode, JsonRequest, JsonResult},
server::RequestHandler,
};
use crate::Explorerd;
const TEST_DATA_DIR: &str = "src/test_utils/test_data";
// Defines a global `Explorerd` instance shared across all tests
lazy_static! {
static ref EXPLORERD_INSTANCE: Mutex<Option<Arc<Explorerd>>> = Mutex::new(None);
}
/// Initializes logging for test cases, which is useful for debugging issues encountered during testing.
/// The logger is configured based on the provided list of targets to ignore and the desired log level.
#[cfg(test)]
@@ -43,3 +64,154 @@ pub fn init_logger(log_level: simplelog::LevelFilter, ignore_targets: Vec<&str>)
eprintln!("Logger failed to initialize");
}
}
#[cfg(test)]
/// Sets up the `Explorerd` instance for testing, ensuring a single instance is initialized only
/// once and shared among subsequent setup calls.
pub fn setup() -> Arc<Explorerd> {
let mut instance = EXPLORERD_INSTANCE.lock().expect("Failed to lock EXPLORERD_INSTANCE mutex");
if instance.is_none() {
// Initialize logger for the first time
init_logger(simplelog::LevelFilter::Info, vec!["sled", "runtime", "net"]);
// Prepare parameters for Explorerd::new
let db_path = format!("{}/explorerd_0", TEST_DATA_DIR);
let darkfid_endpoint = Url::parse("http://127.0.0.1:8240").expect("Invalid URL");
let executor = Arc::new(Executor::new());
// Block on the async function to resolve Explorerd::new
let explorerd = smol::block_on(Explorerd::new(db_path, darkfid_endpoint, executor))
.expect("Failed to initialize Explorerd instance");
// Store the initialized instance in the global Mutex
*instance = Some(Arc::new(explorerd));
}
// Return a clone of the shared instance
Arc::clone(instance.as_ref().unwrap())
}
/// Auxiliary function that validates the correct handling of an invalid JSON-RPC parameter. It
/// prepares a JSON-RPC request with the provided method and params. It then sends the request using
/// the [`Explorerd::handle_request`] function of the provided [`Explorerd`] instance. Verifies the
/// response is an error, matching the expected error code and message.
pub async fn validate_invalid_rpc_parameter(
explorerd: &Explorerd,
method_name: &str,
params: &[JsonValue],
expected_error_code: i32,
expected_error_message: &str,
) {
// Prepare an invalid JSON-RPC request with the provided `params`
let request = JsonRequest {
id: 1,
jsonrpc: "2.0",
method: method_name.to_string(),
params: JsonValue::Array(params.to_vec()),
};
// Call `handle_request` on the Explorerd instance
let response = explorerd.handle_request(request).await;
// Verify response is a `JsonError` with the appropriate error code and message
match response {
JsonResult::Error(actual_error) => {
assert_eq!(actual_error.error.message, expected_error_message);
assert_eq!(actual_error.error.code, expected_error_code);
}
_ => panic!(
"Expected a JSON error response for method: {}, but got something else",
method_name
),
}
}
/// Auxiliary function that validates the handling of non-empty parameters when they are supposed
/// to be empty for the given RPC `method`. It uses the provided [`Explorerd`] instance to ensure
/// that unexpected non-empty parameters result in the expected error for invalid parameters.
pub async fn validate_empty_rpc_parameters(explorerd: &Explorerd, method: String) {
// Prepare a JSON-RPC request for `ping_darkfid`
let request = JsonRequest {
id: 1,
jsonrpc: "2.0",
method,
params: JsonValue::Array(vec![JsonValue::String("non_empty_param".to_string())]),
};
// Call `handle_request` on the Explorerd instance.
let response = explorerd.handle_request(request).await;
// Verify the response is a `JsonError` with the `PingFailed` error code
match response {
JsonResult::Error(actual_error) => {
let expected_error_code = ErrorCode::InvalidParams.code();
let expected_error_msg =
"Parameters not permited, received: \"[\\\"non_empty_param\\\"]\"";
assert_eq!(actual_error.error.code, expected_error_code);
assert_eq!(actual_error.error.message, expected_error_msg);
}
_ => panic!("Expected a JSON object for the response, but got something else"),
}
}
/// Auxiliary function that validates the handling of an invalid contract ID when calling the specified
/// JSON-RPC method, ensuring appropriate error responses from provided [`Explorerd`].
pub fn validate_invalid_rpc_contract_id(explorerd: &Explorerd, method: &str) {
validate_invalid_rpc_hash_parameter(explorerd, method, "contract_id", "Invalid contract ID");
}
/// Auxiliary function that validates the handling of an invalid header hash when calling the specified
/// JSON-RPC `method`, ensuring appropriate error responses from provided [`Explorerd`].
pub fn validate_invalid_rpc_header_hash(explorerd: &Explorerd, method: &str) {
validate_invalid_rpc_hash_parameter(explorerd, method, "header_hash", "Invalid header hash");
}
/// Auxiliary function that validates the handling of an invalid tx hash when calling the specified JSON-RPC
/// `method`, ensuring appropriate error responses from provided [`Explorerd`].
pub fn validate_invalid_rpc_tx_hash(explorerd: &Explorerd, method: &str) {
validate_invalid_rpc_hash_parameter(explorerd, method, "tx_hash", "Invalid tx hash");
}
/// Auxiliary function that validates the correct handling of invalid hash parameters
/// when calling the given RPC `method` using the provided [`Explorerd`]. This includes checks for
/// missing parameters, incorrect parameter types, and invalid hash values, ensuring it returns
/// error responses matching the expected error codes and messages.
fn validate_invalid_rpc_hash_parameter(
explorerd: &Explorerd,
method: &str,
parameter_name: &str,
invalid_hash_value_message: &str,
) {
smol::block_on(async {
// Test for missing `parameter_name` parameter
validate_invalid_rpc_parameter(
explorerd,
method,
&[],
ErrorCode::InvalidParams.code(),
&format!("Parameter '{}' at index 0 is missing", parameter_name),
)
.await;
// Test for invalid `parameter_name` parameter type
validate_invalid_rpc_parameter(
explorerd,
method,
&[JsonValue::Number(123.0)],
ErrorCode::InvalidParams.code(),
&format!("Parameter '{}' is not a valid string", parameter_name),
)
.await;
// Test for invalid `contract_id` value
validate_invalid_rpc_parameter(
explorerd,
method,
&[JsonValue::String("0x0222".to_string())],
ErrorCode::InvalidParams.code(),
&format!("{}: 0x0222", invalid_hash_value_message),
)
.await;
});
}