1+ package com .cbfacademy ;
2+
3+ import org .junit .jupiter .api .AfterEach ;
4+ import org .junit .jupiter .api .BeforeEach ;
5+ import org .junit .jupiter .api .DisplayName ;
6+ import org .junit .jupiter .api .Test ;
7+ import org .junit .jupiter .api .Timeout ;
8+
9+ import static org .junit .jupiter .api .Assumptions .assumeTrue ;
10+
11+ import java .io .BufferedReader ;
12+ import java .io .ByteArrayOutputStream ;
13+ import java .io .IOException ;
14+ import java .io .InputStreamReader ;
15+ import java .io .PrintStream ;
16+ import java .net .ServerSocket ;
17+ import java .net .Socket ;
18+ import java .util .concurrent .CountDownLatch ;
19+ import java .util .concurrent .TimeUnit ;
20+ import java .util .concurrent .atomic .AtomicReference ;
21+
22+ import static org .hamcrest .CoreMatchers .containsString ;
23+ import static org .hamcrest .CoreMatchers .is ;
24+ import static org .hamcrest .MatcherAssert .assertThat ;
25+
26+ @ DisplayName ("Socket Client Test Suite" )
27+ public class SocketClientTest {
28+
29+ private final PrintStream originalOut = System .out ;
30+ private ByteArrayOutputStream outputCapture ;
31+ private Thread mockServerThread ;
32+ private CountDownLatch serverReady ;
33+ private AtomicReference <String > receivedMessage ;
34+
35+ @ BeforeEach
36+ public void setUp () {
37+ // Set up output capture
38+ outputCapture = new ByteArrayOutputStream ();
39+ System .setOut (new PrintStream (outputCapture ));
40+ serverReady = new CountDownLatch (1 );
41+ receivedMessage = new AtomicReference <>();
42+ }
43+
44+ @ AfterEach
45+ public void tearDown () {
46+ // Stop mock server thread if running
47+ if (mockServerThread != null && mockServerThread .isAlive ()) {
48+ mockServerThread .interrupt ();
49+ try {
50+ mockServerThread .join (1000 );
51+ } catch (InterruptedException e ) {
52+ Thread .currentThread ().interrupt ();
53+ }
54+ }
55+
56+ // Restore original System.out
57+ System .setOut (originalOut );
58+ }
59+
60+ @ Test
61+ @ DisplayName ("client connects and sends correct message" )
62+ @ Timeout (value = 10 , unit = TimeUnit .SECONDS )
63+ public void clientConnectsAndSendsMessage () throws Exception {
64+ // Skip test if SocketClient class doesn't exist
65+ assumeTrue (isSocketClientImplemented (), "SocketClient class must be implemented to run this test" );
66+
67+ // Start mock server
68+ startMockServer ();
69+
70+ // Wait for server to be ready
71+ assertThat ("Mock server should start within timeout" ,
72+ serverReady .await (5 , TimeUnit .SECONDS ));
73+
74+ // Run the client using reflection to avoid compilation dependency
75+ runSocketClientMain ();
76+
77+ // Give time for message to be processed
78+ Thread .sleep (100 );
79+
80+ // Verify client sent the expected message
81+ assertThat ("Client should send 'Hello, World!' message" ,
82+ receivedMessage .get (), is ("Hello, World!" ));
83+
84+ // Verify console output
85+ String output = outputCapture .toString ();
86+ assertThat ("Client should log sent message" ,
87+ output , containsString ("Sent message to server: Hello, World!" ));
88+ }
89+
90+ @ Test
91+ @ DisplayName ("client handles server connection successfully" )
92+ @ Timeout (value = 10 , unit = TimeUnit .SECONDS )
93+ public void clientHandlesConnection () throws Exception {
94+ // Skip test if SocketClient class doesn't exist
95+ assumeTrue (isSocketClientImplemented (), "SocketClient class must be implemented to run this test" );
96+
97+ // Start mock server
98+ startMockServer ();
99+ serverReady .await (5 , TimeUnit .SECONDS );
100+
101+ // Run client using reflection
102+ runSocketClientMain ();
103+ Thread .sleep (100 );
104+
105+ // Verify no exceptions in output (no stack trace)
106+ String output = outputCapture .toString ();
107+ assertThat ("Client should connect without errors" ,
108+ !output .contains ("Exception" ) && !output .contains ("Error" ));
109+ }
110+
111+ @ Test
112+ @ DisplayName ("client sends message with proper formatting" )
113+ @ Timeout (value = 10 , unit = TimeUnit .SECONDS )
114+ public void clientSendsProperlyFormattedMessage () throws Exception {
115+ // Skip test if SocketClient class doesn't exist
116+ assumeTrue (isSocketClientImplemented (), "SocketClient class must be implemented to run this test" );
117+
118+ startMockServer ();
119+ serverReady .await (5 , TimeUnit .SECONDS );
120+
121+ runSocketClientMain ();
122+ Thread .sleep (100 );
123+
124+ // Verify exact message content
125+ String received = receivedMessage .get ();
126+ assertThat ("Message should be exactly 'Hello, World!'" ,
127+ received , is ("Hello, World!" ));
128+ assertThat ("Message should not contain extra whitespace" ,
129+ received .trim (), is (received ));
130+ }
131+
132+ private void startMockServer () {
133+ mockServerThread = new Thread (() -> {
134+ try (ServerSocket serverSocket = new ServerSocket (4040 )) {
135+ // Signal that server is ready
136+ serverReady .countDown ();
137+
138+ // Accept one connection and read the message
139+ try (Socket clientSocket = serverSocket .accept ();
140+ BufferedReader in = new BufferedReader (
141+ new InputStreamReader (clientSocket .getInputStream ()))) {
142+
143+ String message = in .readLine ();
144+ receivedMessage .set (message );
145+ }
146+ } catch (IOException e ) {
147+ if (!Thread .currentThread ().isInterrupted ()) {
148+ e .printStackTrace ();
149+ }
150+ }
151+ });
152+
153+ mockServerThread .setDaemon (true );
154+ mockServerThread .start ();
155+ }
156+
157+ /**
158+ * Helper method to run SocketClient.main() using reflection to avoid compilation dependency.
159+ */
160+ private void runSocketClientMain () throws Exception {
161+ Class <?> clientClass = Class .forName ("com.cbfacademy.SocketClient" );
162+ java .lang .reflect .Method mainMethod = clientClass .getMethod ("main" , String [].class );
163+ mainMethod .invoke (null , (Object ) new String []{});
164+ }
165+
166+ /**
167+ * Helper method to check if SocketClient class is implemented.
168+ * Tests will be skipped until the class exists.
169+ */
170+ static boolean isSocketClientImplemented () {
171+ try {
172+ Class .forName ("com.cbfacademy.SocketClient" );
173+ return true ; // Class exists, run tests
174+ } catch (ClassNotFoundException e ) {
175+ return false ; // Class doesn't exist, skip tests
176+ }
177+ }
178+ }
0 commit comments