Skip to content

Commit 8336aa3

Browse files
committed
test: add exercise tests
1 parent eedb2ac commit 8336aa3

4 files changed

Lines changed: 420 additions & 1 deletion

File tree

.github/workflows/classroom-autograding.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ env:
99
GRADING_REPO: cbfacademy/autograding-java-exercises
1010
GRADING_REPO_PATH: grading-repo
1111
STUDENT_REPO_PATH: student-repo
12-
MAX_TESTS: 0 # TODO: Update this to the actual number of tests available for the exercise
12+
MAX_TESTS: 8
1313

1414
jobs:
1515
autograding:

src/test/java/com/cbfacademy/AppTest.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
import org.junit.jupiter.api.DisplayName;
44
import org.junit.jupiter.api.Test;
5+
import org.junit.jupiter.api.AfterEach;
6+
import org.junit.jupiter.api.BeforeEach;
7+
8+
import java.io.BufferedReader;
9+
import java.io.ByteArrayOutputStream;
10+
import java.io.InputStreamReader;
11+
import java.io.PrintStream;
12+
import java.net.URI;
13+
import java.net.URL;
14+
import java.net.URLConnection;
515

616
import static org.hamcrest.CoreMatchers.is;
717
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -10,11 +20,63 @@
1020
@DisplayName(value = "Basic Test Suite")
1121
public class AppTest {
1222

23+
private final PrintStream originalOut = System.out;
24+
private ByteArrayOutputStream outputCapture;
25+
26+
@BeforeEach
27+
public void setUp() {
28+
// Set up output capture
29+
outputCapture = new ByteArrayOutputStream();
30+
System.setOut(new PrintStream(outputCapture));
31+
}
32+
33+
@AfterEach
34+
public void tearDown() {
35+
// Restore original System.out
36+
System.setOut(originalOut);
37+
}
38+
1339
@Test
1440
@DisplayName("creates the app")
1541
public void createsAnApp() {
1642
final App app = new App();
1743

1844
assertThat(app, is(notNullValue()));
1945
}
46+
47+
@Test
48+
@DisplayName("main method outputs website content correctly")
49+
public void mainMethodOutputsWebsiteContent() throws Exception {
50+
// Call the main method (which will print to our captured output)
51+
App.main(new String[]{});
52+
53+
// Get the captured output
54+
String actualOutput = outputCapture.toString();
55+
56+
// Make our own request to get expected output
57+
String expectedOutput = getExpectedWebsiteContent();
58+
59+
// Compare the outputs
60+
assertThat("Main method should output the same content as direct HTTP request",
61+
actualOutput.trim(), is(expectedOutput.trim()));
62+
}
63+
64+
private String getExpectedWebsiteContent() throws Exception {
65+
URL url = new URI("https://codingblackfemales.com").toURL();
66+
URLConnection connection = url.openConnection();
67+
connection.connect();
68+
69+
StringBuilder content = new StringBuilder();
70+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
71+
String inputLine;
72+
while ((inputLine = reader.readLine()) != null) {
73+
content.append(inputLine);
74+
if (reader.ready()) {
75+
content.append(System.lineSeparator());
76+
}
77+
}
78+
}
79+
80+
return content.toString();
81+
}
2082
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
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

Comments
 (0)