From 0e9f5bc92408d022fb2e1b386947bd8018f60ec9 Mon Sep 17 00:00:00 2001 From: Kieran Kihn <114803508+kierankihn@users.noreply.github.com> Date: Thu, 4 Dec 2025 16:39:47 +0800 Subject: [PATCH] test(network): simplify and consolidate `NetworkClientTest` - Removed redundant tests and merged similar cases in `NetworkClientTest`. - Updated `Connect` and `Send` tests to streamline functionality. - Enhanced `MessageSerializerTest` to validate `playerId` in `InitGamePayload`. --- test/unit/network/MessageSerializerTest.cpp | 77 +++- test/unit/network/NetworkClientTest.cpp | 442 +------------------- test/unit/network/NetworkServerTest.cpp | 50 ++- 3 files changed, 113 insertions(+), 456 deletions(-) diff --git a/test/unit/network/MessageSerializerTest.cpp b/test/unit/network/MessageSerializerTest.cpp index 16b04c5..b9b1757 100644 --- a/test/unit/network/MessageSerializerTest.cpp +++ b/test/unit/network/MessageSerializerTest.cpp @@ -195,7 +195,7 @@ TEST(MessageSerializerTest, SerializeInitGameMessage) handCard.draw(Card(CardColor::GREEN, CardType::NUM3)); handCard.draw(Card(CardColor::YELLOW, CardType::REVERSE)); - InitGamePayload payload{discardPile, handCard.getCards(), 2}; + InitGamePayload payload{1, discardPile, handCard.getCards(), 2}; Message message(MessageStatus::OK, MessagePayloadType::INIT_GAME, payload); std::string result = MessageSerializer::serialize(message); @@ -203,6 +203,7 @@ TEST(MessageSerializerTest, SerializeInitGameMessage) EXPECT_EQ(json["status_code"], "OK"); EXPECT_EQ(json["payload_type"], "INIT_GAME"); + EXPECT_EQ(json["payload"]["player_id"], 1); EXPECT_EQ(json["payload"]["current_player"], 2); EXPECT_TRUE(json["payload"]["discard_pile"].is_array()); EXPECT_TRUE(json["payload"]["hand_card"].is_array()); @@ -215,7 +216,7 @@ TEST(MessageSerializerTest, SerializeInitGameMessageWithEmptyPiles) DiscardPile discardPile; HandCard handCard; - InitGamePayload payload{discardPile, handCard.getCards(), 0}; + InitGamePayload payload{0, discardPile, handCard.getCards(), 0}; Message message(MessageStatus::OK, MessagePayloadType::INIT_GAME, payload); std::string result = MessageSerializer::serialize(message); @@ -223,6 +224,7 @@ TEST(MessageSerializerTest, SerializeInitGameMessageWithEmptyPiles) EXPECT_EQ(json["status_code"], "OK"); EXPECT_EQ(json["payload_type"], "INIT_GAME"); + EXPECT_EQ(json["payload"]["player_id"], 0); EXPECT_EQ(json["payload"]["current_player"], 0); EXPECT_TRUE(json["payload"]["discard_pile"].is_array()); EXPECT_TRUE(json["payload"]["hand_card"].is_array()); @@ -386,6 +388,7 @@ TEST(MessageSerializerTest, DeserializeInitGameMessage) "status_code":"OK", "payload_type":"INIT_GAME", "payload":{ + "player_id":42, "discard_pile":[{"card_color":"Red","card_type":"5"}], "hand_card":[{"card_color":"Blue","card_type":"Skip"}], "current_player":2 @@ -397,6 +400,7 @@ TEST(MessageSerializerTest, DeserializeInitGameMessage) EXPECT_EQ(message.getMessageStatus(), MessageStatus::OK); EXPECT_EQ(message.getMessagePayloadType(), MessagePayloadType::INIT_GAME); auto payload = std::get(message.getMessagePayload()); + EXPECT_EQ(payload.playerId, 42); EXPECT_EQ(payload.currentPlayerIndex, 2); } @@ -862,105 +866,139 @@ TEST(MessageSerializerTest, DeserializeInitGameWithArrayPayload) EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } +TEST(MessageSerializerTest, DeserializeInitGameWithMissingPlayerId) +{ + std::string json = R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":[],"hand_card":[],"current_player":0}})"; + EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); +} + +TEST(MessageSerializerTest, DeserializeInitGameWithNullPlayerId) +{ + std::string json = + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":null,"discard_pile":[],"hand_card":[],"current_player":0}})"; + EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); +} + +TEST(MessageSerializerTest, DeserializeInitGameWithStringPlayerId) +{ + std::string json = + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":"0","discard_pile":[],"hand_card":[],"current_player":0}})"; + EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); +} + +TEST(MessageSerializerTest, DeserializeInitGameWithNegativePlayerId) +{ + std::string json = + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":-1,"discard_pile":[],"hand_card":[],"current_player":0}})"; + EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); +} + +TEST(MessageSerializerTest, DeserializeInitGameWithFloatPlayerId) +{ + std::string json = + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":1.5,"discard_pile":[],"hand_card":[],"current_player":0}})"; + EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); +} + TEST(MessageSerializerTest, DeserializeInitGameWithMissingDiscardPile) { - std::string json = R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"hand_card":[],"current_player":0}})"; + std::string json = R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"hand_card":[],"current_player":0}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } TEST(MessageSerializerTest, DeserializeInitGameWithMissingHandCard) { - std::string json = R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":[],"current_player":0}})"; + std::string json = R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"discard_pile":[],"current_player":0}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } TEST(MessageSerializerTest, DeserializeInitGameWithMissingCurrentPlayer) { - std::string json = R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":[],"hand_card":[]}})"; + std::string json = R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"discard_pile":[],"hand_card":[]}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } TEST(MessageSerializerTest, DeserializeInitGameWithNullDiscardPile) { std::string json = - R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":null,"hand_card":[],"current_player":0}})"; + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"discard_pile":null,"hand_card":[],"current_player":0}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } TEST(MessageSerializerTest, DeserializeInitGameWithNullHandCard) { std::string json = - R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":[],"hand_card":null,"current_player":0}})"; + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"discard_pile":[],"hand_card":null,"current_player":0}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } TEST(MessageSerializerTest, DeserializeInitGameWithStringDiscardPile) { std::string json = - R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":"test","hand_card":[],"current_player":0}})"; + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"discard_pile":"test","hand_card":[],"current_player":0}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } TEST(MessageSerializerTest, DeserializeInitGameWithStringHandCard) { std::string json = - R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":[],"hand_card":"test","current_player":0}})"; + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"discard_pile":[],"hand_card":"test","current_player":0}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } TEST(MessageSerializerTest, DeserializeInitGameWithStringCurrentPlayer) { std::string json = - R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":[],"hand_card":[],"current_player":"0"}})"; + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"discard_pile":[],"hand_card":[],"current_player":"0"}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } TEST(MessageSerializerTest, DeserializeInitGameWithNullCurrentPlayer) { std::string json = - R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":[],"hand_card":[],"current_player":null}})"; + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"discard_pile":[],"hand_card":[],"current_player":null}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } TEST(MessageSerializerTest, DeserializeInitGameWithNegativeCurrentPlayer) { std::string json = - R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":[],"hand_card":[],"current_player":-1}})"; + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"discard_pile":[],"hand_card":[],"current_player":-1}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } TEST(MessageSerializerTest, DeserializeInitGameWithFloatCurrentPlayer) { std::string json = - R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":[],"hand_card":[],"current_player":1.5}})"; + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"discard_pile":[],"hand_card":[],"current_player":1.5}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } TEST(MessageSerializerTest, DeserializeInitGameWithInvalidCardInDiscardPile) { std::string json = - R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":[{"card_color":"Invalid","card_type":"5"}],"hand_card":[],"current_player":0}})"; + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"discard_pile":[{"card_color":"Invalid","card_type":"5"}],"hand_card":[],"current_player":0}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } TEST(MessageSerializerTest, DeserializeInitGameWithInvalidCardInHandCard) { std::string json = - R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":[],"hand_card":[{"card_color":"Red","card_type":"Invalid"}],"current_player":0}})"; + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"discard_pile":[],"hand_card":[{"card_color":"Red","card_type":"Invalid"}],"current_player":0}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } TEST(MessageSerializerTest, DeserializeInitGameWithNonObjectCardInDiscardPile) { std::string json = - R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":["string"],"hand_card":[],"current_player":0}})"; + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"discard_pile":["string"],"hand_card":[],"current_player":0}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } TEST(MessageSerializerTest, DeserializeInitGameWithNonObjectCardInHandCard) { std::string json = - R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":[],"hand_card":[123],"current_player":0}})"; + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":0,"discard_pile":[],"hand_card":[123],"current_player":0}})"; EXPECT_THROW(MessageSerializer::deserialize(json), std::invalid_argument); } @@ -1021,9 +1059,10 @@ TEST(MessageSerializerTest, DeserializeLargeDrawCount) TEST(MessageSerializerTest, DeserializeLargeCurrentPlayer) { std::string json = - R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"discard_pile":[],"hand_card":[],"current_player":4294967295}})"; + R"({"status_code":"OK","payload_type":"INIT_GAME","payload":{"player_id":7,"discard_pile":[],"hand_card":[],"current_player":4294967295}})"; Message message = MessageSerializer::deserialize(json); auto payload = std::get(message.getMessagePayload()); + EXPECT_EQ(payload.playerId, 7); EXPECT_EQ(payload.currentPlayerIndex, 4294967295); } @@ -1114,6 +1153,7 @@ TEST(MessageSerializerTest, DeserializeHugeArray) nlohmann::json json; json["status_code"] = "OK"; json["payload_type"] = "INIT_GAME"; + json["payload"]["player_id"] = 0; json["payload"]["discard_pile"] = nlohmann::json::array(); for (int i = 0; i < 10000; ++i) { json["payload"]["discard_pile"].push_back({{"card_color", "Red"}, {"card_type", "5"}}); @@ -1161,4 +1201,3 @@ TEST(MessageSerializerTest, DeserializePathTraversalAttempt) auto payload = std::get(message.getMessagePayload()); EXPECT_EQ(payload.playerName, "../../etc/passwd"); } - diff --git a/test/unit/network/NetworkClientTest.cpp b/test/unit/network/NetworkClientTest.cpp index 5e41f39..c620e0d 100644 --- a/test/unit/network/NetworkClientTest.cpp +++ b/test/unit/network/NetworkClientTest.cpp @@ -7,15 +7,10 @@ #include "../../../src/network/NetworkClient.h" -#include -#include -#include #include -#include using namespace UNO::NETWORK; -// Helper class to create a simple echo server for testing class SimpleTestServer { private: asio::io_context io_context_; @@ -97,8 +92,6 @@ private: } }; -// ========== NetworkClient Constructor Tests ========== - TEST(NetworkClientTest, ConstructorWithCallback) { auto callback = [](std::string message) {}; @@ -106,452 +99,33 @@ TEST(NetworkClientTest, ConstructorWithCallback) EXPECT_NO_THROW({ NetworkClient client(callback); }); } -TEST(NetworkClientTest, ConstructorWithEmptyCallback) -{ - auto callback = [](std::string message) {}; - - EXPECT_NO_THROW({ NetworkClient client(callback); }); -} - -// ========== NetworkClient Connect Tests ========== - -TEST(NetworkClientTest, ConnectToValidServer) +TEST(NetworkClientTest, ConnectToHost) { SimpleTestServer server(30001); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); auto callback = [](std::string message) {}; NetworkClient client(callback); - EXPECT_NO_THROW({ client.connect("127.0.0.1", 30001); }); -} - -TEST(NetworkClientTest, ConnectToLocalhost) -{ - SimpleTestServer server(30002); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - - EXPECT_NO_THROW({ client.connect("localhost", 30002); }); + EXPECT_NO_THROW({ client.connect("localhost", 30001); }); } TEST(NetworkClientTest, ConnectToInvalidHost) { + SimpleTestServer server(30002); + auto callback = [](std::string message) {}; NetworkClient client(callback); - // Should throw when trying to resolve invalid host - EXPECT_THROW({ client.connect("invalid.host.that.does.not.exist.12345", 30003); }, std::exception); + EXPECT_THROW({ client.connect("invalid.host.that.does.not.exist.12345", 30002); }, std::exception); } -TEST(NetworkClientTest, ConnectToUnreachablePort) +TEST(NetworkClientTest, SendAfterConnect) { - auto callback = [](std::string message) {}; - NetworkClient client(callback); - - // Should throw when trying to connect to a port with no server - EXPECT_THROW({ client.connect("127.0.0.1", 30004); }, std::exception); -} - -// ========== NetworkClient Send Tests ========== - -TEST(NetworkClientTest, SendSimpleMessage) -{ - SimpleTestServer server(30005); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + SimpleTestServer server(30003); auto callback = [](std::string message) {}; NetworkClient client(callback); - client.connect("127.0.0.1", 30005); + client.connect("localhost", 30003); EXPECT_NO_THROW({ client.send("Hello, Server!"); }); } - -TEST(NetworkClientTest, SendEmptyMessage) -{ - SimpleTestServer server(30006); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30006); - - EXPECT_NO_THROW({ client.send(""); }); -} - -TEST(NetworkClientTest, SendLargeMessage) -{ - SimpleTestServer server(30007); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30007); - - std::string large_message(1024 * 1024, 'X'); // 1MB message - EXPECT_NO_THROW({ client.send(large_message); }); -} - -TEST(NetworkClientTest, SendMessageWithSpecialCharacters) -{ - SimpleTestServer server(30008); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30008); - - EXPECT_NO_THROW({ client.send("Special: \n\t\r\"'\\"); }); -} - -TEST(NetworkClientTest, SendMessageWithUnicode) -{ - SimpleTestServer server(30009); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30009); - - EXPECT_NO_THROW({ client.send("Unicode: 你好世界 🎮🃏"); }); -} - -TEST(NetworkClientTest, SendMultipleMessages) -{ - SimpleTestServer server(30010); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30010); - - for (int i = 0; i < 10; ++i) { - EXPECT_NO_THROW({ client.send("Message " + std::to_string(i)); }); - } -} - -TEST(NetworkClientTest, SendJSONMessage) -{ - SimpleTestServer server(30011); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30011); - - std::string json_message = R"({"status":"OK","type":"TEST","data":"value"})"; - EXPECT_NO_THROW({ client.send(json_message); }); -} - -// ========== NetworkClient Read Tests ========== - -TEST(NetworkClientTest, ReadMessageFromServer) -{ - SimpleTestServer server(30012); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30012); - - std::string test_message = "Test Message"; - client.send(test_message); - - std::string received; - EXPECT_NO_THROW({ received = client.read(); }); - - EXPECT_EQ(received, test_message); -} - -TEST(NetworkClientTest, ReadEmptyMessage) -{ - SimpleTestServer server(30013); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30013); - - client.send(""); - - std::string received; - EXPECT_NO_THROW({ received = client.read(); }); - - EXPECT_EQ(received, ""); -} - -TEST(NetworkClientTest, ReadLargeMessage) -{ - SimpleTestServer server(30014); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30014); - - std::string large_message(100 * 1024, 'L'); // 100KB - client.send(large_message); - - std::string received; - EXPECT_NO_THROW({ received = client.read(); }); - - EXPECT_EQ(received, large_message); -} - -TEST(NetworkClientTest, ReadMessageWithSpecialCharacters) -{ - SimpleTestServer server(30015); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30015); - - std::string special_message = "Special: \n\t\r\"'\\"; - client.send(special_message); - - std::string received = client.read(); - EXPECT_EQ(received, special_message); -} - -TEST(NetworkClientTest, ReadMessageWithUnicode) -{ - SimpleTestServer server(30016); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30016); - - std::string unicode_message = "Unicode: 你好世界 🎮"; - client.send(unicode_message); - - std::string received = client.read(); - EXPECT_EQ(received, unicode_message); -} - -TEST(NetworkClientTest, ReadMultipleMessages) -{ - SimpleTestServer server(30017); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30017); - - for (int i = 0; i < 5; ++i) { - std::string test_message = "Message " + std::to_string(i); - client.send(test_message); - - std::string received = client.read(); - EXPECT_EQ(received, test_message); - } -} - -// ========== Round-trip Tests ========== - -TEST(NetworkClientTest, RoundTripSimpleMessage) -{ - SimpleTestServer server(30018); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30018); - - std::string original = "Round trip test"; - client.send(original); - std::string received = client.read(); - - EXPECT_EQ(original, received); -} - -TEST(NetworkClientTest, RoundTripJSONMessage) -{ - SimpleTestServer server(30019); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30019); - - std::string json = R"({"player":"Alice","action":"DRAW_CARD","count":3})"; - client.send(json); - std::string received = client.read(); - - EXPECT_EQ(json, received); -} - -TEST(NetworkClientTest, RoundTripMultipleMessages) -{ - SimpleTestServer server(30020); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30020); - - std::vector messages = {"First message", "Second message with 123", "Third: special chars \n\t", "Fourth: 你好", ""}; - - for (const auto &msg : messages) { - client.send(msg); - std::string received = client.read(); - EXPECT_EQ(msg, received); - } -} - -TEST(NetworkClientTest, RoundTripLargeMessages) -{ - SimpleTestServer server(30021); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30021); - - for (size_t size : {1024, 10240, 102400}) { - std::string large_msg(size, 'Z'); - client.send(large_msg); - std::string received = client.read(); - EXPECT_EQ(large_msg.size(), received.size()); - EXPECT_EQ(large_msg, received); - } -} - -// ========== Edge Cases ========== - -TEST(NetworkClientTest, SendBeforeConnect) -{ - auto callback = [](std::string message) {}; - NetworkClient client(callback); - - // Should throw because not connected - EXPECT_THROW({ client.send("Message before connect"); }, std::exception); -} - -TEST(NetworkClientTest, ReadBeforeConnect) -{ - auto callback = [](std::string message) {}; - NetworkClient client(callback); - - // Should throw because not connected - EXPECT_THROW({ client.read(); }, std::exception); -} - -TEST(NetworkClientTest, MultipleConnectAttempts) -{ - SimpleTestServer server1(30022); - SimpleTestServer server2(30023); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - - // First connection should succeed - EXPECT_NO_THROW({ client.connect("127.0.0.1", 30022); }); - - // Second connection attempt should also work (replaces first connection) - EXPECT_NO_THROW({ client.connect("127.0.0.1", 30023); }); -} - -TEST(NetworkClientTest, RapidSendAndRead) -{ - SimpleTestServer server(30025); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30025); - - // Rapidly send and read 100 messages - for (int i = 0; i < 100; ++i) { - std::string msg = std::to_string(i); - client.send(msg); - std::string received = client.read(); - EXPECT_EQ(msg, received); - } -} - -TEST(NetworkClientTest, VeryLongMessageContent) -{ - SimpleTestServer server(30026); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30026); - - // Create a complex message with various characters - std::string complex_message; - for (int i = 0; i < 1000; ++i) { - complex_message += "Line " + std::to_string(i) + ": Hello World! 你好世界 🎮\n"; - } - - client.send(complex_message); - std::string received = client.read(); - EXPECT_EQ(complex_message, received); -} - -TEST(NetworkClientTest, SendBinaryData) -{ - SimpleTestServer server(30027); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30027); - - // Create binary data with all byte values - std::string binary_data; - for (int i = 1; i < 256; ++i) { // Skip 0 to avoid null terminator issues - binary_data += static_cast(i); - } - - client.send(binary_data); - std::string received = client.read(); - EXPECT_EQ(binary_data.size(), received.size()); - EXPECT_EQ(binary_data, received); -} - -TEST(NetworkClientTest, AlternatingReadAndWrite) -{ - SimpleTestServer server(30028); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30028); - - for (int i = 0; i < 20; ++i) { - if (i % 2 == 0) { - client.send("Message " + std::to_string(i)); - } - else { - std::string received = client.read(); - EXPECT_FALSE(received.empty()); - } - } -} - -// ========== Protocol Compliance Tests ========== - -TEST(NetworkClientTest, MessageLengthPrefixCorrect) -{ - SimpleTestServer server(30029); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - auto callback = [](std::string message) {}; - NetworkClient client(callback); - client.connect("127.0.0.1", 30029); - - // Test various message lengths - for (size_t len : {0, 1, 10, 100, 1000, 10000}) { - std::string msg(len, 'A'); - client.send(msg); - std::string received = client.read(); - EXPECT_EQ(len, received.size()); - EXPECT_EQ(msg, received); - } -} diff --git a/test/unit/network/NetworkServerTest.cpp b/test/unit/network/NetworkServerTest.cpp index 6d0e4ba..54e33c6 100644 --- a/test/unit/network/NetworkServerTest.cpp +++ b/test/unit/network/NetworkServerTest.cpp @@ -445,7 +445,7 @@ TEST(SessionTest, SessionCreation) asio::io_context io_context; asio::ip::tcp::socket socket(io_context); - EXPECT_NO_THROW({ Session session(0, std::move(socket)); }); + EXPECT_NO_THROW({ Session session(std::move(socket)); }); } TEST(SessionTest, SessionStartWithCallback) @@ -453,13 +453,57 @@ TEST(SessionTest, SessionStartWithCallback) asio::io_context io_context; asio::ip::tcp::socket socket(io_context); - auto session = std::make_shared(0, std::move(socket)); + auto session = std::make_shared(std::move(socket)); - auto callback = [](size_t player_id, std::string message) {}; + auto callback = [](std::string message) {}; EXPECT_NO_THROW({ session->start(callback); }); } +TEST(SessionTest, SessionInvokesCallbackOnIncomingMessage) +{ + asio::io_context server_context; + asio::ip::tcp::acceptor acceptor(server_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), 0)); + uint16_t port = acceptor.local_endpoint().port(); + + std::atomic callback_called{false}; + std::string received_message; + std::mutex message_mutex; + + acceptor.async_accept([&](const asio::error_code &ec, asio::ip::tcp::socket socket) { + if (!ec) { + auto session = std::make_shared(std::move(socket)); + session->start([&](std::string message) { + std::lock_guard lock(message_mutex); + callback_called = true; + received_message = std::move(message); + }); + } + }); + + std::thread server_thread([&]() { server_context.run(); }); + + asio::io_context client_context; + asio::ip::tcp::socket client_socket(client_context); + client_socket.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), port)); + + std::string message = "Hello Session"; + size_t length = message.size(); + asio::write(client_socket, asio::buffer(&length, sizeof(length))); + asio::write(client_socket, asio::buffer(message)); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + server_context.stop(); + if (server_thread.joinable()) { + server_thread.join(); + } + + std::lock_guard lock(message_mutex); + EXPECT_TRUE(callback_called); + EXPECT_EQ(received_message, message); +} + // ========== Concurrent Access Tests ========== TEST(NetworkServerTest, ConcurrentSendToSamePlayer)