@@ -881,6 +881,210 @@ TEST_F(WebsocketTest, PlainWebsocket_UrlParsing) {
881881 SUCCEED ();
882882}
883883
884+ // ======================== Send Immediately After Open Tests ========================
885+
886+ TEST_F (WebsocketTest, SendImmediatelyAfterOpen_MessageQueuedAndSentAfterConnect) {
887+ EventSynchronizer connected_sync;
888+ EventSynchronizer data_sync;
889+ std::string received_data;
890+ std::atomic<bool > message_sent{false };
891+
892+ auto ws = std::make_shared<Websocket>(
893+ " wss://ws.postman-echo.com/raw" ,
894+ [&]() {
895+ connected_sync.notify ();
896+ },
897+ [&]() {},
898+ [&](const char * data, std::size_t len) {
899+ received_data.assign (data, len);
900+ data_sync.notify ();
901+ },
902+ [&](std::string) {}
903+ );
904+
905+ // Initial state should be DISCONNECTED
906+ EXPECT_EQ (ws->status (), Websocket::Status::DISCONNECTED);
907+
908+ // Open the WebSocket connection
909+ ws->open ();
910+
911+ // Immediately send data right after calling open (before connection is established)
912+ const char * test_message = " Immediate message after open" ;
913+ ws->send (test_message, strlen (test_message));
914+ message_sent.store (true );
915+
916+ // At this point, the connection should be CONNECTING or CONNECTED
917+ auto status_after_send = ws->status ();
918+ EXPECT_TRUE (status_after_send == Websocket::Status::CONNECTING ||
919+ status_after_send == Websocket::Status::CONNECTED);
920+
921+ // Wait for connection to be established
922+ connected_sync.wait_for (std::chrono::milliseconds (10000 ));
923+
924+ EXPECT_TRUE (connected_sync.is_triggered ());
925+ if (connected_sync.is_triggered ()) {
926+ EXPECT_EQ (ws->status (), Websocket::Status::CONNECTED);
927+ EXPECT_TRUE (message_sent.load ());
928+
929+ // Wait for the echo response
930+ data_sync.wait_for (std::chrono::milliseconds (5000 ));
931+
932+ if (data_sync.is_triggered ()) {
933+ // Verify the message was sent and echoed back after connection was established
934+ EXPECT_EQ (received_data, " Immediate message after open" );
935+ }
936+ }
937+
938+ // Always close the websocket
939+ ws->close ();
940+ }
941+
942+ TEST_F (WebsocketTest, SendImmediatelyAfterOpen_MultipleMessages) {
943+ EventSynchronizer connected_sync;
944+ std::atomic<int > messages_received{0 };
945+ std::vector<std::string> received_messages;
946+ std::mutex messages_mutex;
947+
948+ auto ws = std::make_shared<Websocket>(
949+ " wss://ws.postman-echo.com/raw" ,
950+ [&]() {
951+ connected_sync.notify ();
952+ },
953+ [&]() {},
954+ [&](const char * data, std::size_t len) {
955+ std::lock_guard<std::mutex> lock (messages_mutex);
956+ received_messages.emplace_back (data, len);
957+ messages_received++;
958+ },
959+ [&](std::string) {}
960+ );
961+
962+ EXPECT_EQ (ws->status (), Websocket::Status::DISCONNECTED);
963+
964+ // Open connection
965+ ws->open ();
966+
967+ // Immediately send multiple messages right after open
968+ const int num_messages = 3 ;
969+ for (int i = 0 ; i < num_messages; ++i) {
970+ std::string msg = " Quick message " + std::to_string (i);
971+ ws->send (msg.c_str (), msg.size ());
972+ }
973+
974+ // Wait for connection to establish
975+ connected_sync.wait_for (std::chrono::milliseconds (10000 ));
976+
977+ EXPECT_TRUE (connected_sync.is_triggered ());
978+ if (connected_sync.is_triggered ()) {
979+ EXPECT_EQ (ws->status (), Websocket::Status::CONNECTED);
980+
981+ // Wait for all messages to be received
982+ wait_for_condition ([&]() { return messages_received >= num_messages; },
983+ std::chrono::milliseconds (10000 ));
984+
985+ // All messages should have been queued and sent after connection established
986+ EXPECT_GE (messages_received.load (), num_messages);
987+
988+ std::lock_guard<std::mutex> lock (messages_mutex);
989+ EXPECT_GE (received_messages.size (), static_cast <size_t >(num_messages));
990+ }
991+
992+ ws->close ();
993+ }
994+
995+ TEST_F (WebsocketTest, SendImmediatelyAfterOpen_VerifyOrderPreserved) {
996+ EventSynchronizer connected_sync;
997+ std::atomic<int > messages_received{0 };
998+ std::vector<std::string> received_messages;
999+ std::mutex messages_mutex;
1000+
1001+ auto ws = std::make_shared<Websocket>(
1002+ " wss://ws.postman-echo.com/raw" ,
1003+ [&]() {
1004+ connected_sync.notify ();
1005+ },
1006+ [&]() {},
1007+ [&](const char * data, std::size_t len) {
1008+ std::lock_guard<std::mutex> lock (messages_mutex);
1009+ received_messages.emplace_back (data, len);
1010+ messages_received++;
1011+ },
1012+ [&](std::string) {}
1013+ );
1014+
1015+ ws->open ();
1016+
1017+ // Send messages immediately after open - they should be queued
1018+ ws->send (" First" , 5 );
1019+ ws->send (" Second" , 6 );
1020+ ws->send (" Third" , 5 );
1021+
1022+ // Wait for connection
1023+ connected_sync.wait_for (std::chrono::milliseconds (10000 ));
1024+
1025+ EXPECT_TRUE (connected_sync.is_triggered ());
1026+ if (connected_sync.is_triggered ()) {
1027+ // Wait for all messages to arrive
1028+ wait_for_condition ([&]() { return messages_received >= 3 ; },
1029+ std::chrono::milliseconds (10000 ));
1030+
1031+ std::lock_guard<std::mutex> lock (messages_mutex);
1032+ EXPECT_GE (received_messages.size (), 3u );
1033+
1034+ // Verify order is preserved (messages queued before connection should arrive in order)
1035+ if (received_messages.size () >= 3 ) {
1036+ EXPECT_EQ (received_messages[0 ], " First" );
1037+ EXPECT_EQ (received_messages[1 ], " Second" );
1038+ EXPECT_EQ (received_messages[2 ], " Third" );
1039+ }
1040+ }
1041+
1042+ ws->close ();
1043+ }
1044+
1045+ TEST_F (WebsocketTest, SendImmediatelyAfterOpen_LargeMessage) {
1046+ EventSynchronizer connected_sync;
1047+ EventSynchronizer data_sync;
1048+ std::string received_data;
1049+
1050+ auto ws = std::make_shared<Websocket>(
1051+ " wss://ws.postman-echo.com/raw" ,
1052+ [&]() {
1053+ connected_sync.notify ();
1054+ },
1055+ [&]() {},
1056+ [&](const char * data, std::size_t len) {
1057+ received_data.assign (data, len);
1058+ data_sync.notify ();
1059+ },
1060+ [&](std::string) {}
1061+ );
1062+
1063+ ws->open ();
1064+
1065+ // Send a large message immediately after open
1066+ std::string large_message (5120 , ' X' ); // 5KB message
1067+ ws->send (large_message.c_str (), large_message.size ());
1068+
1069+ // Wait for connection
1070+ connected_sync.wait_for (std::chrono::milliseconds (10000 ));
1071+
1072+ EXPECT_TRUE (connected_sync.is_triggered ());
1073+ if (connected_sync.is_triggered ()) {
1074+ EXPECT_EQ (ws->status (), Websocket::Status::CONNECTED);
1075+
1076+ // Wait for the large message echo
1077+ data_sync.wait_for (std::chrono::milliseconds (10000 ));
1078+
1079+ if (data_sync.is_triggered ()) {
1080+ EXPECT_EQ (received_data.size (), large_message.size ());
1081+ EXPECT_EQ (received_data, large_message);
1082+ }
1083+ }
1084+
1085+ ws->close ();
1086+ }
1087+
8841088// Note: Main tests use wss://ws.postman-echo.com which is a public test server.
8851089// Tests may fail if the server is down or network is unavailable.
8861090//
0 commit comments