Skip to content

Commit 4d40540

Browse files
committed
Add NettyHttpServerHttpModeTest.
1 parent 065155d commit 4d40540

File tree

1 file changed

+250
-0
lines changed

1 file changed

+250
-0
lines changed
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
package org.tinystruct.system;
2+
3+
import org.junit.jupiter.api.*;
4+
import org.tinystruct.AbstractApplication;
5+
import org.tinystruct.ApplicationContext;
6+
import org.tinystruct.net.URLRequest;
7+
import org.tinystruct.net.URLResponse;
8+
import org.tinystruct.net.handlers.HTTPHandler;
9+
import org.tinystruct.system.annotation.Action;
10+
11+
import java.net.Socket;
12+
import java.net.URI;
13+
14+
import static org.junit.jupiter.api.Assertions.*;
15+
16+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
17+
public class NettyHttpServerHttpModeTest {
18+
19+
private static final int TEST_PORT = 18080;
20+
private static final String BASE_URL = "http://localhost:" + TEST_PORT;
21+
private NettyHttpServer httpServer;
22+
private Thread serverThread;
23+
private TestWebApp app;
24+
25+
@BeforeAll
26+
public void setUp() throws Exception {
27+
// Initialize settings
28+
Settings settings = new Settings();
29+
settings.set("default.base_url", "/?q=");
30+
settings.set("default.language", "en_US");
31+
settings.set("charset", "utf-8");
32+
33+
// Create and install test app
34+
this.app = new TestWebApp();
35+
ApplicationManager.install(this.app, settings);
36+
37+
// Install required applications
38+
ApplicationManager.install(new Dispatcher());
39+
this.httpServer = new NettyHttpServer();
40+
ApplicationManager.install(this.httpServer, settings);
41+
42+
// Start server in a separate thread
43+
serverThread = new Thread(() -> {
44+
try {
45+
ApplicationContext context = new ApplicationContext();
46+
context.setAttribute("--server-port", String.valueOf(TEST_PORT));
47+
ApplicationManager.call("start", context, Action.Mode.CLI);
48+
} catch (Exception e) {
49+
e.printStackTrace();
50+
}
51+
});
52+
serverThread.setDaemon(true);
53+
serverThread.start();
54+
55+
// Wait for server to be ready
56+
boolean started = false;
57+
for (int i = 0; i < 30; i++) {
58+
try (Socket socket = new Socket("localhost", TEST_PORT)) {
59+
started = true;
60+
break;
61+
} catch (Exception e) {
62+
Thread.sleep(1000);
63+
}
64+
}
65+
if (!started) {
66+
throw new RuntimeException("Server failed to start within 30 seconds");
67+
}
68+
69+
// Give server a moment to fully initialize
70+
Thread.sleep(500);
71+
}
72+
73+
@AfterAll
74+
public void tearDown() {
75+
if (httpServer != null) {
76+
httpServer.stop();
77+
}
78+
if (serverThread != null && serverThread.isAlive()) {
79+
serverThread.interrupt();
80+
}
81+
}
82+
83+
@Test
84+
public void testActionModeFromName() {
85+
// Test HTTP method name to Mode mapping
86+
assertEquals(Action.Mode.HTTP_GET, Action.Mode.fromName("GET"));
87+
assertEquals(Action.Mode.HTTP_POST, Action.Mode.fromName("POST"));
88+
assertEquals(Action.Mode.HTTP_PUT, Action.Mode.fromName("PUT"));
89+
assertEquals(Action.Mode.HTTP_DELETE, Action.Mode.fromName("DELETE"));
90+
assertEquals(Action.Mode.HTTP_PATCH, Action.Mode.fromName("PATCH"));
91+
assertEquals(Action.Mode.HTTP_HEAD, Action.Mode.fromName("HEAD"));
92+
assertEquals(Action.Mode.HTTP_OPTIONS, Action.Mode.fromName("OPTIONS"));
93+
94+
// Test case insensitivity
95+
assertEquals(Action.Mode.HTTP_GET, Action.Mode.fromName("get"));
96+
assertEquals(Action.Mode.HTTP_POST, Action.Mode.fromName("post"));
97+
98+
// Test null and unknown values return DEFAULT
99+
assertEquals(Action.Mode.DEFAULT, Action.Mode.fromName(null));
100+
assertEquals(Action.Mode.DEFAULT, Action.Mode.fromName("UNKNOWN"));
101+
}
102+
103+
@Test
104+
public void testHttpGetRequest() throws Exception {
105+
// Make actual HTTP GET request
106+
String response = sendHttpRequest("GET", BASE_URL + "/?q=api/users", null);
107+
assertTrue(response.contains("GET users"), "GET request should return 'GET users'");
108+
}
109+
110+
@Test
111+
public void testHttpPostRequest() throws Exception {
112+
// Make actual HTTP POST request
113+
String response = sendHttpRequest("POST", BASE_URL + "/?q=api/users", null);
114+
assertTrue(response.contains("POST users"), "POST request should return 'POST users'");
115+
}
116+
117+
@Test
118+
public void testHttpPutRequest() throws Exception {
119+
// Make actual HTTP PUT request
120+
String response = sendHttpRequest("PUT", BASE_URL + "/?q=api/users/123", null);
121+
assertTrue(response.contains("PUT user"), "PUT request should return 'PUT user'");
122+
assertTrue(response.contains("123"), "PUT request should include the ID parameter");
123+
}
124+
125+
@Test
126+
public void testHttpDeleteRequest() throws Exception {
127+
// Make actual HTTP DELETE request
128+
String response = sendHttpRequest("DELETE", BASE_URL + "/?q=api/users/456", null);
129+
assertTrue(response.contains("DELETE user"), "DELETE request should return 'DELETE user'");
130+
assertTrue(response.contains("456"), "DELETE request should include the ID parameter");
131+
}
132+
133+
@Test
134+
public void testDefaultModeAcceptsAllMethods() throws Exception {
135+
// Test that DEFAULT mode actions accept any HTTP method
136+
String getResponse = sendHttpRequest("GET", BASE_URL + "/?q=api/ping", null);
137+
assertTrue(getResponse.contains("pong"), "GET request to ping should return 'pong'");
138+
139+
String postResponse = sendHttpRequest("POST", BASE_URL + "/?q=api/ping", null);
140+
assertTrue(postResponse.contains("pong"), "POST request to ping should return 'pong'");
141+
142+
String putResponse = sendHttpRequest("PUT", BASE_URL + "/?q=api/ping", null);
143+
assertTrue(putResponse.contains("pong"), "PUT request to ping should return 'pong'");
144+
}
145+
146+
@Test
147+
public void testHttpMethodMismatch() throws Exception {
148+
// Try to access GET endpoint with POST method - should fail or return error
149+
// Note: This depends on how the server handles method mismatches
150+
String response = sendHttpRequest("POST", BASE_URL + "/?q=api/users", null);
151+
// POST to api/users should work (there's a POST handler), but let's verify it's not the GET handler
152+
assertTrue(response.contains("POST users"), "POST request should match POST handler, not GET");
153+
}
154+
155+
/**
156+
* Helper method to send HTTP requests
157+
*/
158+
private String sendHttpRequest(String method, String urlString, String body) throws Exception {
159+
URLRequest request = new URLRequest(URI.create(urlString).toURL());
160+
request.setMethod(method.toUpperCase());
161+
162+
if (body != null && (method.equalsIgnoreCase("POST") || method.equalsIgnoreCase("PUT"))) {
163+
request.setHeader("Content-Type", "application/x-www-form-urlencoded");
164+
request.setBody(body);
165+
}
166+
167+
HTTPHandler handler = new HTTPHandler();
168+
URLResponse response = handler.handleRequest(request);
169+
170+
int statusCode = response.getStatusCode();
171+
String responseText = response.getBody();
172+
173+
if (statusCode >= 200 && statusCode < 300) {
174+
return responseText;
175+
} else {
176+
// return error response text for non-2xx responses
177+
return responseText;
178+
}
179+
}
180+
181+
@Test
182+
public void testHttpMethodExtractionFromRequest() {
183+
// Test that HTTP method can be extracted from Method enum
184+
org.tinystruct.http.Method getMethod = org.tinystruct.http.Method.GET;
185+
Action.Mode mode = Action.Mode.fromName(getMethod.name());
186+
assertEquals(Action.Mode.HTTP_GET, mode);
187+
188+
org.tinystruct.http.Method postMethod = org.tinystruct.http.Method.POST;
189+
mode = Action.Mode.fromName(postMethod.name());
190+
assertEquals(Action.Mode.HTTP_POST, mode);
191+
}
192+
193+
public class TestWebApp extends AbstractApplication {
194+
@Override
195+
public void init() {
196+
this.setTemplateRequired(false);
197+
}
198+
199+
@Action(
200+
value = "api/users",
201+
description = "Get users",
202+
mode = Action.Mode.HTTP_GET
203+
)
204+
public String getUsers() {
205+
return "GET users";
206+
}
207+
208+
@Action(
209+
value = "api/users",
210+
description = "Create user",
211+
mode = Action.Mode.HTTP_POST
212+
)
213+
public String createUser() {
214+
return "POST users";
215+
}
216+
217+
@Action(
218+
value = "api/users",
219+
description = "Update user",
220+
mode = Action.Mode.HTTP_PUT
221+
)
222+
public String updateUser(String id) {
223+
return "PUT user " + (id != null ? id : "unknown");
224+
}
225+
226+
@Action(
227+
value = "api/users",
228+
description = "Delete user",
229+
mode = Action.Mode.HTTP_DELETE
230+
)
231+
public String deleteUser(String id) {
232+
return "DELETE user " + (id != null ? id : "unknown");
233+
}
234+
235+
@Action(
236+
value = "api/ping",
237+
description = "Ping endpoint",
238+
mode = Action.Mode.DEFAULT
239+
)
240+
public String ping() {
241+
return "pong";
242+
}
243+
244+
@Override
245+
public String version() {
246+
return "test";
247+
}
248+
}
249+
}
250+

0 commit comments

Comments
 (0)