use super::rpc_core_mock::RpcCoreMock;
use crate::{adaptor::Adaptor, manager::Manager};
use kaspa_core::info;
use kaspa_grpc_client::GrpcClient;
use kaspa_notify::scope::{NewBlockTemplateScope, Scope};
use kaspa_rpc_core::api::rpc::RpcApi;
use kaspa_utils::networking::{ContextualNetAddress, NetAddress};
use std::sync::Arc;
#[tokio::test]
async fn test_client_server_sanity_check() {
kaspa_core::log::try_init_logger("info, kaspa_grpc_core=trace, kaspa_grpc_server=trace, kaspa_grpc_client=trace");
let rpc_core_service = Arc::new(RpcCoreMock::new());
rpc_core_service.start();
let server = create_server(rpc_core_service.clone());
assert!(!server.has_connections(), "server should have no client when just started");
let client = create_client(server.serve_address()).await;
assert_eq!(server.active_connections().len(), 1, "the client failed to connect to the server");
assert!(client.handle_message_id() && client.handle_stop_notify(), "the client failed to collect server features");
rpc_core_service.join().await;
assert!(server.stop().await.is_ok(), "error stopping the server");
assert!(client.disconnect().await.is_ok(), "client failed to disconnect");
drop(client);
drop(server);
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
}
#[tokio::test]
async fn test_client_server_connections() {
enum ClosingEnd {
Client,
Server,
}
struct Test {
name: &'static str,
ends: Vec<ClosingEnd>,
terminate_clients: bool,
}
impl Test {
async fn execute(&self) {
info!("=================================================================================");
info!("{}", self.name);
let rpc_core_service = Arc::new(RpcCoreMock::new());
rpc_core_service.start();
let server = create_server(rpc_core_service.clone());
assert!(!server.has_connections(), "server should have no client when just started");
let mut clients = Vec::with_capacity(self.ends.len());
for _ in 0..self.ends.len() {
clients.push(create_client(server.serve_address()).await);
}
assert_eq!(server.active_connections().len(), self.ends.len(), "one or more clients failed to connect to the server");
let mut clients_left: usize = self.ends.len();
for (i, closing) in self.ends.iter().enumerate() {
match *closing {
ClosingEnd::Client => {
assert!(clients[i].disconnect().await.is_ok(), "client {} failed to disconnect", i);
clients_left -= 1;
}
ClosingEnd::Server => {}
}
}
if clients_left < self.ends.len() {
tokio::time::sleep(std::time::Duration::from_millis(25)).await;
assert_eq!(
server.active_connections().len(),
clients_left,
"server should have {} client(s) left connected",
clients_left
);
}
if self.terminate_clients {
server.terminate_all_connections().await;
tokio::time::sleep(std::time::Duration::from_millis(25)).await;
for (i, client) in clients.iter().enumerate() {
assert!(!client.is_connected(), "server failed to disconnect client {}", i);
}
assert!(!server.has_connections(), "server should have no more clients");
}
rpc_core_service.join().await;
assert!(server.stop().await.is_ok(), "error stopping the server");
if !self.terminate_clients {
tokio::time::sleep(std::time::Duration::from_millis(25)).await;
for (i, client) in clients.iter().enumerate() {
assert!(!client.is_connected(), "server failed to disconnect client {}", i);
}
assert!(!server.has_connections(), "server should have no more clients");
}
drop(server);
tokio::time::sleep(std::time::Duration::from_millis(30)).await;
}
}
let tests = vec![
Test {
name: "3 clients connecting and disconnecting themselves",
ends: vec![ClosingEnd::Client, ClosingEnd::Client, ClosingEnd::Client],
terminate_clients: false,
},
Test {
name: "3 clients connecting and server disconnecting them",
ends: vec![ClosingEnd::Server, ClosingEnd::Server, ClosingEnd::Server],
terminate_clients: true,
},
Test {
name: "3 clients connecting, 1 disconnecting itself, server shutting down",
ends: vec![ClosingEnd::Client, ClosingEnd::Server, ClosingEnd::Client],
terminate_clients: false,
},
];
kaspa_core::log::try_init_logger("info, kaspa_grpc_core=trace, kaspa_grpc_server=trace, kaspa_grpc_client=trace");
for test in tests {
test.execute().await;
}
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
}
#[tokio::test]
async fn test_client_server_notifications() {
kaspa_core::log::try_init_logger("info, kaspa_grpc_core=trace, kaspa_grpc_server=trace, kaspa_grpc_client=trace");
let rpc_core_service = Arc::new(RpcCoreMock::new());
rpc_core_service.start();
let server = create_server(rpc_core_service.clone());
let client1 = create_client(server.serve_address()).await;
let client2 = create_client(server.serve_address()).await;
assert!(client1.start_notify(0, Scope::NewBlockTemplate(NewBlockTemplateScope::default())).await.is_ok());
assert!(client2.start_notify(0, Scope::NewBlockTemplate(NewBlockTemplateScope::default())).await.is_ok());
assert!(rpc_core_service.notify_new_block_template().is_ok());
rpc_core_service.notify_complete().await;
assert!(client1.notification_channel_receiver().recv().await.is_ok());
assert!(client2.notification_channel_receiver().recv().await.is_ok());
assert!(client1.disconnect().await.is_ok(), "client failed to disconnect");
drop(client1);
rpc_core_service.join().await;
assert!(server.stop().await.is_ok(), "error stopping the server");
drop(server);
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
}
fn create_server(core_service: Arc<RpcCoreMock>) -> Arc<Adaptor> {
let manager = Manager::new(128);
Adaptor::server(
get_free_net_address(),
1,
manager,
core_service.clone(),
core_service.core_notifier(),
core_service.subscription_context(),
3,
Default::default(),
)
}
async fn create_client(server_address: NetAddress) -> GrpcClient {
let server_url = format!("grpc://localhost:{}", server_address.port);
GrpcClient::connect(server_url).await.unwrap()
}
fn get_free_net_address() -> NetAddress {
let socket = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let port = socket.local_addr().unwrap().port();
drop(socket);
ContextualNetAddress::unspecified().normalize(port)
}