diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c7d17cdc..cf72ad74 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4478,6 +4478,59 @@ network_gtest_discover_tests(network_experimental_quic_server_test message(STATUS "Experimental unit tests enabled (quic_client, quic_server)") +################################################## +# Core Messaging Unit Tests (Issue #966) +################################################## + +# Helper macro to create a standard test target +macro(add_network_test TEST_NAME TEST_SOURCE) + add_executable(${TEST_NAME} ${TEST_SOURCE}) + + target_link_libraries(${TEST_NAME} PRIVATE + network_system + GTest::gtest + GTest::gtest_main + Threads::Threads + ) + + setup_asio_integration(${TEST_NAME}) + + if(COMMON_SYSTEM_INCLUDE_DIR) + target_include_directories(${TEST_NAME} PRIVATE ${COMMON_SYSTEM_INCLUDE_DIR}) + target_compile_definitions(${TEST_NAME} PRIVATE WITH_COMMON_SYSTEM) + endif() + + if(THREAD_SYSTEM_INCLUDE_DIR) + target_include_directories(${TEST_NAME} PRIVATE ${THREAD_SYSTEM_INCLUDE_DIR}) + target_compile_definitions(${TEST_NAME} PRIVATE WITH_THREAD_SYSTEM) + endif() + + if(LOGGER_SYSTEM_INCLUDE_DIR) + target_include_directories(${TEST_NAME} PRIVATE ${LOGGER_SYSTEM_INCLUDE_DIR}) + target_compile_definitions(${TEST_NAME} PRIVATE WITH_LOGGER_SYSTEM) + endif() + + set_target_properties(${TEST_NAME} PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin + ) + + network_gtest_discover_tests(${TEST_NAME} + DISCOVERY_TIMEOUT 60 + ) +endmacro() + +add_network_test(network_messaging_client_test unit/messaging_client_test.cpp) +add_network_test(network_messaging_server_test unit/messaging_server_test.cpp) +add_network_test(network_messaging_udp_client_test unit/messaging_udp_client_test.cpp) +add_network_test(network_messaging_udp_server_test unit/messaging_udp_server_test.cpp) +# secure_messaging_udp tests excluded: dtls_socket symbols in libs/network-udp, not main library + +message(STATUS "Core messaging unit tests enabled (tcp, udp)") + +# WebSocket tests already exist: test_messaging_ws_client.cpp, test_messaging_ws_server.cpp + ################################################## # Integration Tests ################################################## diff --git a/tests/unit/messaging_client_test.cpp b/tests/unit/messaging_client_test.cpp new file mode 100644 index 00000000..462d8224 --- /dev/null +++ b/tests/unit/messaging_client_test.cpp @@ -0,0 +1,145 @@ +/***************************************************************************** +BSD 3-Clause License + +Copyright (c) 2024-2025, kcenon +All rights reserved. +*****************************************************************************/ + +/** + * @file messaging_client_test.cpp + * @brief Unit tests for messaging_client class + * + * Tests validate: + * - Construction with client ID + * - Initial state (not running, not connected) + * - Client ID accessor + * - Callback registration + * - Error paths (send before connect, empty host) + * - Stop when not started + * - Interface compliance (i_protocol_client) + */ + +#include "internal/core/messaging_client.h" + +#include + +#include +#include +#include + +using namespace kcenon::network::core; + +// ============================================================================ +// Construction Tests +// ============================================================================ + +TEST(MessagingClientTest, ConstructWithClientId) +{ + auto client = std::make_shared("tcp-client-1"); + + EXPECT_EQ(client->client_id(), "tcp-client-1"); + EXPECT_FALSE(client->is_running()); + EXPECT_FALSE(client->is_connected()); +} + +TEST(MessagingClientTest, EmptyClientId) +{ + auto client = std::make_shared(""); + + EXPECT_TRUE(client->client_id().empty()); + EXPECT_FALSE(client->is_running()); +} + +// ============================================================================ +// Error Path Tests +// ============================================================================ + +TEST(MessagingClientTest, SendBeforeConnectFails) +{ + auto client = std::make_shared("client"); + + auto result = client->send_packet(std::vector{0x01, 0x02}); + EXPECT_TRUE(result.is_err()); +} + +TEST(MessagingClientTest, SendViaInterfaceBeforeConnectFails) +{ + auto client = std::make_shared("client"); + + auto result = client->send(std::vector{0x01}); + EXPECT_TRUE(result.is_err()); +} + +TEST(MessagingClientTest, StartWithEmptyHostFails) +{ + auto client = std::make_shared("client"); + + auto result = client->start_client("", 8080); + EXPECT_TRUE(result.is_err()); +} + +TEST(MessagingClientTest, StopWhenNotStartedSucceeds) +{ + auto client = std::make_shared("client"); + + auto result = client->stop_client(); + EXPECT_TRUE(result.is_ok()); +} + +TEST(MessagingClientTest, StopViaInterfaceWhenNotStarted) +{ + auto client = std::make_shared("client"); + + auto result = client->stop(); + EXPECT_TRUE(result.is_ok()); +} + +// ============================================================================ +// Callback Tests +// ============================================================================ + +TEST(MessagingClientTest, SetReceiveCallback) +{ + auto client = std::make_shared("client"); + EXPECT_NO_FATAL_FAILURE( + client->set_receive_callback([](const std::vector&) {})); +} + +TEST(MessagingClientTest, SetConnectedCallback) +{ + auto client = std::make_shared("client"); + EXPECT_NO_FATAL_FAILURE( + client->set_connected_callback([]() {})); +} + +TEST(MessagingClientTest, SetDisconnectedCallback) +{ + auto client = std::make_shared("client"); + EXPECT_NO_FATAL_FAILURE( + client->set_disconnected_callback([]() {})); +} + +TEST(MessagingClientTest, SetErrorCallback) +{ + auto client = std::make_shared("client"); + EXPECT_NO_FATAL_FAILURE( + client->set_error_callback([](std::error_code) {})); +} + +TEST(MessagingClientTest, SetNullCallbacks) +{ + auto client = std::make_shared("client"); + EXPECT_NO_FATAL_FAILURE(client->set_receive_callback(nullptr)); + EXPECT_NO_FATAL_FAILURE(client->set_connected_callback(nullptr)); + EXPECT_NO_FATAL_FAILURE(client->set_disconnected_callback(nullptr)); + EXPECT_NO_FATAL_FAILURE(client->set_error_callback(nullptr)); +} + +// ============================================================================ +// Destructor Tests +// ============================================================================ + +TEST(MessagingClientTest, DestructorWhenNotRunning) +{ + { auto client = std::make_shared("client"); } +} diff --git a/tests/unit/messaging_server_test.cpp b/tests/unit/messaging_server_test.cpp new file mode 100644 index 00000000..fb0eb4f0 --- /dev/null +++ b/tests/unit/messaging_server_test.cpp @@ -0,0 +1,115 @@ +/***************************************************************************** +BSD 3-Clause License + +Copyright (c) 2024-2025, kcenon +All rights reserved. +*****************************************************************************/ + +/** + * @file messaging_server_test.cpp + * @brief Unit tests for messaging_server class + * + * Tests validate: + * - Construction with server ID + * - Initial state (not running) + * - Server ID accessor + * - Callback registration + * - Stop when not started + */ + +#include "internal/core/messaging_server.h" + +#include + +#include +#include +#include + +using namespace kcenon::network::core; + +// ============================================================================ +// Construction Tests +// ============================================================================ + +TEST(MessagingServerTest, ConstructWithServerId) +{ + auto server = std::make_shared("tcp-server-1"); + + EXPECT_EQ(server->server_id(), "tcp-server-1"); + EXPECT_FALSE(server->is_running()); +} + +TEST(MessagingServerTest, EmptyServerId) +{ + auto server = std::make_shared(""); + + EXPECT_TRUE(server->server_id().empty()); + EXPECT_FALSE(server->is_running()); +} + +// ============================================================================ +// Error Path Tests +// ============================================================================ + +TEST(MessagingServerTest, StopWhenNotStartedSucceeds) +{ + auto server = std::make_shared("server"); + + auto result = server->stop_server(); + EXPECT_TRUE(result.is_ok()); +} + +// ============================================================================ +// Callback Tests +// ============================================================================ + +TEST(MessagingServerTest, SetConnectionCallback) +{ + auto server = std::make_shared("server"); + EXPECT_NO_FATAL_FAILURE( + server->set_connection_callback( + [](std::shared_ptr) {})); +} + +TEST(MessagingServerTest, SetDisconnectionCallback) +{ + auto server = std::make_shared("server"); + EXPECT_NO_FATAL_FAILURE( + server->set_disconnection_callback([](const std::string&) {})); +} + +TEST(MessagingServerTest, SetReceiveCallback) +{ + auto server = std::make_shared("server"); + EXPECT_NO_FATAL_FAILURE( + server->set_receive_callback( + [](std::shared_ptr, + const std::vector&) {})); +} + +TEST(MessagingServerTest, SetErrorCallback) +{ + auto server = std::make_shared("server"); + EXPECT_NO_FATAL_FAILURE( + server->set_error_callback( + [](std::shared_ptr, + std::error_code) {})); +} + +TEST(MessagingServerTest, SetNullCallbacks) +{ + auto server = std::make_shared("server"); + EXPECT_NO_FATAL_FAILURE(server->set_connection_callback(nullptr)); + EXPECT_NO_FATAL_FAILURE(server->set_disconnection_callback(nullptr)); + EXPECT_NO_FATAL_FAILURE(server->set_receive_callback(nullptr)); + EXPECT_NO_FATAL_FAILURE(server->set_error_callback(nullptr)); +} + +// ============================================================================ +// Destructor Tests +// ============================================================================ + +TEST(MessagingServerTest, DestructorWhenNotRunning) +{ + { auto server = std::make_shared("server"); } +} diff --git a/tests/unit/messaging_udp_client_test.cpp b/tests/unit/messaging_udp_client_test.cpp new file mode 100644 index 00000000..78a23ba2 --- /dev/null +++ b/tests/unit/messaging_udp_client_test.cpp @@ -0,0 +1,116 @@ +/***************************************************************************** +BSD 3-Clause License + +Copyright (c) 2024-2025, kcenon +All rights reserved. +*****************************************************************************/ + +/** + * @file messaging_udp_client_test.cpp + * @brief Unit tests for messaging_udp_client class + * + * Tests validate: + * - Construction with client ID + * - Initial state (not running) + * - Client ID accessor + * - Callback registration + * - Error paths (send before start, empty host) + * - Stop when not started + * - Interface compliance (i_udp_client) + */ + +#include "internal/core/messaging_udp_client.h" + +#include + +#include +#include +#include + +using namespace kcenon::network::core; + +// ============================================================================ +// Construction Tests +// ============================================================================ + +TEST(MessagingUdpClientTest, ConstructWithClientId) +{ + auto client = std::make_shared("udp-client-1"); + + EXPECT_EQ(client->client_id(), "udp-client-1"); + EXPECT_FALSE(client->is_running()); +} + +TEST(MessagingUdpClientTest, EmptyClientId) +{ + auto client = std::make_shared(""); + + EXPECT_TRUE(client->client_id().empty()); +} + +// ============================================================================ +// Error Path Tests +// ============================================================================ + +TEST(MessagingUdpClientTest, SendBeforeStartFails) +{ + auto client = std::make_shared("client"); + + auto result = client->send(std::vector{0x01, 0x02}); + EXPECT_TRUE(result.is_err()); +} + +TEST(MessagingUdpClientTest, StartWithEmptyHostFails) +{ + auto client = std::make_shared("client"); + + auto result = client->start_client("", 9000); + EXPECT_TRUE(result.is_err()); +} + +TEST(MessagingUdpClientTest, StopWhenNotStartedSucceeds) +{ + auto client = std::make_shared("client"); + + auto result = client->stop_client(); + EXPECT_TRUE(result.is_ok()); +} + +TEST(MessagingUdpClientTest, SetTargetBeforeStartFails) +{ + auto client = std::make_shared("client"); + + auto result = client->set_target("127.0.0.1", 9000); + EXPECT_TRUE(result.is_err()); +} + +// ============================================================================ +// Callback Tests +// ============================================================================ + +TEST(MessagingUdpClientTest, SetReceiveCallback) +{ + auto client = std::make_shared("client"); + EXPECT_NO_FATAL_FAILURE( + client->set_receive_callback( + [](const std::vector&, const asio::ip::udp::endpoint&) {})); +} + +TEST(MessagingUdpClientTest, SetErrorCallback) +{ + auto client = std::make_shared("client"); + EXPECT_NO_FATAL_FAILURE( + client->set_error_callback([](std::error_code) {})); +} + +// Note: SetNullCallbacks test omitted — set_receive_callback has ambiguous +// overloads from i_udp_client interface when passed nullptr. + +// ============================================================================ +// Destructor Tests +// ============================================================================ + +TEST(MessagingUdpClientTest, DestructorWhenNotRunning) +{ + { auto client = std::make_shared("client"); } +} diff --git a/tests/unit/messaging_udp_server_test.cpp b/tests/unit/messaging_udp_server_test.cpp new file mode 100644 index 00000000..8440df58 --- /dev/null +++ b/tests/unit/messaging_udp_server_test.cpp @@ -0,0 +1,90 @@ +/***************************************************************************** +BSD 3-Clause License + +Copyright (c) 2024-2025, kcenon +All rights reserved. +*****************************************************************************/ + +/** + * @file messaging_udp_server_test.cpp + * @brief Unit tests for messaging_udp_server class + * + * Tests validate: + * - Construction with server ID + * - Initial state (not running) + * - Server ID accessor + * - Callback registration + * - Stop when not started + */ + +#include "internal/core/messaging_udp_server.h" + +#include + +#include +#include +#include + +using namespace kcenon::network::core; + +// ============================================================================ +// Construction Tests +// ============================================================================ + +TEST(MessagingUdpServerTest, ConstructWithServerId) +{ + auto server = std::make_shared("udp-server-1"); + + EXPECT_EQ(server->server_id(), "udp-server-1"); + EXPECT_FALSE(server->is_running()); +} + +TEST(MessagingUdpServerTest, EmptyServerId) +{ + auto server = std::make_shared(""); + + EXPECT_TRUE(server->server_id().empty()); +} + +// ============================================================================ +// Error Path Tests +// ============================================================================ + +TEST(MessagingUdpServerTest, StopWhenNotStartedSucceeds) +{ + auto server = std::make_shared("server"); + + auto result = server->stop_server(); + EXPECT_TRUE(result.is_ok()); +} + +// ============================================================================ +// Callback Tests +// ============================================================================ + +TEST(MessagingUdpServerTest, SetReceiveCallback) +{ + auto server = std::make_shared("server"); + EXPECT_NO_FATAL_FAILURE( + server->set_receive_callback( + [](const std::vector&, const asio::ip::udp::endpoint&) {})); +} + +TEST(MessagingUdpServerTest, SetErrorCallback) +{ + auto server = std::make_shared("server"); + EXPECT_NO_FATAL_FAILURE( + server->set_error_callback([](std::error_code) {})); +} + +// Note: SetNullCallbacks test omitted — set_receive_callback has ambiguous +// overloads from i_udp_server interface when passed nullptr. + +// ============================================================================ +// Destructor Tests +// ============================================================================ + +TEST(MessagingUdpServerTest, DestructorWhenNotRunning) +{ + { auto server = std::make_shared("server"); } +}