Skip to content

Färdig JavaFX chat app med tester#9

Closed
dinbah18 wants to merge 1 commit intomainfrom
dinbah18
Closed

Färdig JavaFX chat app med tester#9
dinbah18 wants to merge 1 commit intomainfrom
dinbah18

Conversation

@dinbah18
Copy link

@dinbah18 dinbah18 commented Nov 12, 2025

Inlämning JavaFX Chat App - Dinbah18
Färdig version med fungerande GUI och tester (BUILD SUCCESS).

Summary by CodeRabbit

  • New Features

    • Transformed the application into a chat interface with a message display area.
    • Added message sending capability through an input field and send button.
    • Integrated backend connectivity to transmit and receive messages.
    • Updated application title to "JavaFX Chat App 💬".
  • Tests

    • Added unit tests to validate model initialization and messaging functionality.

@coderabbitai
Copy link

coderabbitai bot commented Nov 12, 2025

Walkthrough

This pull request refactors a JavaFX application to implement a chat interface with HTTP-based backend communication. Changes include migrating the UI from a label-based design to a TextArea/TextField layout, transforming the model from static greeting logic to asynchronous HTTP messaging with message polling, updating module dependencies to include HTTP client support, and adding unit tests for model initialization and message handling.

Changes

Cohort / File(s) Summary
UI Controller Refactoring
src/main/java/com/example/HelloController.java
Replaced label-based UI with TextArea (chatArea) and TextField (inputField) as @FXML fields. Changed model field from final inline initialization to mutable. Refactored initialize() from @FXML private method to public initialization hook. Added new onSendButtonClick() public event handler for sending messages.
Application Bootstrap
src/main/java/com/example/HelloFX.java
Simplified scene creation by passing fxmlLoader.load() directly to Scene constructor instead of using intermediate root node. Updated window title from "Hello MVC" to "JavaFX Chat App 💬".
HTTP-Driven Model
src/main/java/com/example/HelloModel.java
Replaced static greeting logic with HTTP client implementation. Added parameterized constructor accepting topic string and reading BACKEND_URL from environment. Introduced sendMessage(String) for POST requests, listen(MessageHandler) for async message polling, and new public MessageHandler interface for callback handling.
Module Dependencies
src/main/java/module-info.java
Added requires java.net.http; directive to enable HTTP client functionality.
FXML Layout
src/main/resources/com/example/hello-view.fxml
Restructured root from StackPane to BorderPane. Center region now contains TextArea (chatArea); bottom region contains HBox with TextField (inputField) and Send Button linked to onSendButtonClick. Updated imports to use wildcards and added Insets for padding configuration.
Unit Test Suite
src/test/java/com/example/HelloModelTest.java
New test class with three JUnit 5 tests: model initialization validation, BACKEND_URL environment default verification, and JSON message formatting validation.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as HelloController
    participant Model as HelloModel
    participant Backend
    
    User->>UI: Types message in inputField
    User->>UI: Clicks Send Button
    UI->>UI: onSendButtonClick()
    UI->>Model: sendMessage(text)
    Model->>Backend: POST /topic with JSON message
    Backend-->>Model: Response
    
    rect rgb(240, 240, 240)
    note right of Model: Async listening (background)
    Model->>Backend: Poll /topic/json
    Backend-->>Model: New message
    Model->>UI: MessageHandler.onMessage(msg)
    UI->>UI: Append to chatArea
    end
    
    UI-->>User: Display message in chat
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Areas requiring focused attention:

  • HelloModel: Verify HTTP client error handling, environment variable fallback logic, and async polling implementation robustness
  • HelloController: Confirm proper FXML injection and lifecycle (initialize() called before scene setup)
  • hello-view.fxml: Validate fx:id binding correctness and layout constraints
  • Tests: Check test coverage adequacy for HTTP edge cases (network failures, malformed responses)

Poem

🐰 A chat app springs to life, oh my!
With BorderPane and TextArea spry,
HTTP whispers through the air,
Each message posted, shown with care,
Async listeners hop and play—
JavaFX blooms in grand array! 💬

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Färdig JavaFX chat app med tester' accurately describes the main changes: implementing a complete JavaFX chat application with tests. It directly reflects the substantial refactoring across multiple files to enable chat functionality.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (4)
src/main/java/com/example/HelloFX.java (1)

12-13: Consider explicit scene dimensions for better UX.

The scene sizing relies on the FXML content dimensions. While this works, explicitly setting scene dimensions (e.g., new Scene(fxmlLoader.load(), 640, 480)) provides more predictable window sizing for users.

Apply this diff if you prefer explicit dimensions:

-        Scene scene = new Scene(fxmlLoader.load());
+        Scene scene = new Scene(fxmlLoader.load(), 640, 480);
src/main/java/com/example/HelloModel.java (1)

10-10: Consider configuring HTTP client with timeouts.

The default HTTP client has no explicit timeout configuration, which could cause the application to hang indefinitely on network issues.

Apply this diff to add timeout configuration:

-    private final HttpClient client = HttpClient.newHttpClient();
+    private final HttpClient client = HttpClient.newBuilder()
+            .connectTimeout(java.time.Duration.ofSeconds(10))
+            .build();
src/main/java/com/example/HelloController.java (2)

20-26: Consider adding user feedback for send failures.

The current implementation provides no visual feedback if message sending fails due to network issues. Users won't know if their message was sent successfully.

Consider updating HelloModel.sendMessage() to return a CompletableFuture<Boolean> and display feedback:

    @FXML
    public void onSendButtonClick() {
        String text = inputField.getText().trim();
        if (!text.isEmpty()) {
            model.sendMessage(text).thenAccept(success -> {
                if (!success) {
                    chatArea.appendText("[Failed to send message]\n");
                }
            });
            inputField.clear();
        }
    }

15-15: Consider making the topic configurable.

The topic name is hardcoded to "javafx-chat". Consider making it configurable through a constructor parameter, configuration file, or user input.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21c51e8 and eac6bfa.

📒 Files selected for processing (6)
  • src/main/java/com/example/HelloController.java (1 hunks)
  • src/main/java/com/example/HelloFX.java (1 hunks)
  • src/main/java/com/example/HelloModel.java (1 hunks)
  • src/main/java/module-info.java (1 hunks)
  • src/main/resources/com/example/hello-view.fxml (1 hunks)
  • src/test/java/com/example/HelloModelTest.java (1 hunks)
🔇 Additional comments (3)
src/main/java/module-info.java (1)

4-4: LGTM! HTTP module dependency correctly added.

The java.net.http module is properly added to support the HTTP client operations in HelloModel.

src/main/resources/com/example/hello-view.fxml (1)

6-25: LGTM! UI layout is well-structured.

The BorderPane layout with TextArea for chat display and HBox for input is appropriate for a chat application. The controls are properly wired with fx:id attributes and the button action is correctly bound.

src/main/java/com/example/HelloModel.java (1)

14-20: Verify BACKEND_URL environment variable configuration.

The constructor requires BACKEND_URL to be set but the test file (HelloModelTest.java line 24-25) suggests a default of "https://ntfy.sh" should be used when the variable is unset. Ensure this aligns with your requirements.

If a default URL is intended, apply this diff:

     public HelloModel(String topic) {
         this.topic = topic;
         this.backendUrl = System.getenv("BACKEND_URL");
         if (backendUrl == null || backendUrl.isBlank()) {
-            throw new IllegalStateException("BACKEND_URL is not set!");
+            this.backendUrl = "https://ntfy.sh";
         }
     }

Comment on lines +14 to +17
public void initialize() {
model = new HelloModel("javafx-chat"); // Du kan byta topic-namnet
model.listen(msg -> chatArea.appendText(msg + "\n"));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

CRITICAL: Raw JSON displayed in chat area instead of parsed messages.

Line 16 appends the raw message from the listener to chatArea. Based on HelloModel.listen() (line 44 in HelloModel.java), this will be a raw JSON line from the HTTP response, not the parsed message text. Users will see JSON strings like {"message":"Hello"} instead of just "Hello".

Apply this diff to parse the message before displaying:

     @FXML
     public void initialize() {
         model = new HelloModel("javafx-chat"); // Du kan byta topic-namnet
-        model.listen(msg -> chatArea.appendText(msg + "\n"));
+        model.listen(msg -> {
+            // Parse JSON to extract message text
+            // Basic parsing - consider using a JSON library for production
+            int start = msg.indexOf("\"message\"");
+            if (start != -1) {
+                start = msg.indexOf("\"", start + 9) + 1;
+                int end = msg.indexOf("\"", start);
+                if (end != -1) {
+                    String messageText = msg.substring(start, end);
+                    chatArea.appendText(messageText + "\n");
+                }
+            }
+        });
     }

Better solution: Extract the message parsing logic to HelloModel so the MessageHandler.onMessage() receives the parsed message text, not raw JSON.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void initialize() {
model = new HelloModel("javafx-chat"); // Du kan byta topic-namnet
model.listen(msg -> chatArea.appendText(msg + "\n"));
}
@FXML
public void initialize() {
model = new HelloModel("javafx-chat"); // Du kan byta topic-namnet
model.listen(msg -> {
// Parse JSON to extract message text
// Basic parsing - consider using a JSON library for production
int start = msg.indexOf("\"message\"");
if (start != -1) {
start = msg.indexOf("\"", start + 9) + 1;
int end = msg.indexOf("\"", start);
if (end != -1) {
String messageText = msg.substring(start, end);
chatArea.appendText(messageText + "\n");
}
}
});
}
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloController.java around lines 14–17, the
listener currently appends the raw JSON string to chatArea; change this so the
handler receives/display only the parsed message text. Fix by either (A) parsing
the JSON here before calling chatArea.appendText — extract the "message" field
from the JSON payload and append that (with a safe try/catch to fallback to the
raw payload on parse errors), or (B, preferred) move the JSON parsing into
HelloModel.listen so it invokes the callback with the parsed message text
(update HelloModel.listen to parse each raw line, extract "message" and pass
that String to the registered listener), then keep this controller code as
model.listen(msg -> chatArea.appendText(msg + "\n")).

Comment on lines +22 to +33
public void sendMessage(String message) {
String json = "{\"message\": \"" + message + "\"}";
String url = backendUrl + "/" + topic;

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();

client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

CRITICAL: JSON injection vulnerability and missing error handling.

Line 23 constructs JSON by string concatenation without escaping user input. If message contains quotes, backslashes, or newlines, it will break the JSON format. More critically, this could enable injection attacks.

Additionally, line 32 ignores the HTTP response and provides no error handling for network failures.

Apply this diff to use proper JSON handling and add basic error handling:

     public void sendMessage(String message) {
-        String json = "{\"message\": \"" + message + "\"}";
+        // Escape quotes, backslashes, and control characters
+        String escapedMessage = message
+                .replace("\\", "\\\\")
+                .replace("\"", "\\\"")
+                .replace("\n", "\\n")
+                .replace("\r", "\\r")
+                .replace("\t", "\\t");
+        String json = "{\"message\": \"" + escapedMessage + "\"}";
         String url = backendUrl + "/" + topic;
 
         HttpRequest request = HttpRequest.newBuilder()
                 .uri(URI.create(url))
                 .header("Content-Type", "application/json")
                 .POST(HttpRequest.BodyPublishers.ofString(json))
                 .build();
 
-        client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
+        client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
+                .exceptionally(e -> {
+                    System.err.println("Failed to send message: " + e.getMessage());
+                    return null;
+                });
     }

Better solution: Use a proper JSON library like Jackson or Gson instead of manual string concatenation.

🤖 Prompt for AI Agents
In src/main/java/com/example/HelloModel.java around lines 22 to 33, the method
builds JSON via string concatenation (risking JSON injection and broken
payloads) and ignores the HTTP response/errors; replace the manual concatenation
with a JSON serializer (e.g., Jackson ObjectMapper or Gson) to build a safe JSON
payload from a Map or small DTO and serialize it to a String, then send the
request and handle the result: await the response (or attach
thenApply/exceptionally on the CompletableFuture), check HTTP status codes and
log or throw on non-2xx, and catch/log network or serialization exceptions so
failures are not silently ignored.

Comment on lines +35 to +47
public CompletableFuture<Void> listen(MessageHandler handler) {
String url = backendUrl + "/" + topic + "/json";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.build();

return client.sendAsync(request, HttpResponse.BodyHandlers.ofLines())
.thenAccept(response -> response.body().forEach(line -> {
if (line.contains("\"message\"")) {
handler.onMessage(line);
}
}));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

CRITICAL: Naive JSON parsing and missing error handling.

Line 43 uses line.contains("\"message\"") which will match any line containing the substring "message" in quotes, not just valid JSON message fields. This could lead to incorrect message parsing or processing of non-message data.

Additionally, there's no error handling for connection failures, timeouts, or malformed responses.

Consider using a proper JSON parser. Here's a basic improvement with error handling:

     public CompletableFuture<Void> listen(MessageHandler handler) {
         String url = backendUrl + "/" + topic + "/json";
         HttpRequest request = HttpRequest.newBuilder()
                 .uri(URI.create(url))
                 .build();
 
         return client.sendAsync(request, HttpResponse.BodyHandlers.ofLines())
                 .thenAccept(response -> response.body().forEach(line -> {
-                    if (line.contains("\"message\"")) {
+                    // Basic JSON parsing - consider using a proper JSON library
+                    if (line.trim().matches(".*\"message\"\\s*:\\s*\".*\".*")) {
                         handler.onMessage(line);
                     }
-                }));
+                }))
+                .exceptionally(e -> {
+                    System.err.println("Failed to listen for messages: " + e.getMessage());
+                    return null;
+                });
     }

Recommended: Use a JSON library (Jackson/Gson) to properly parse the response and extract the message field value.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public CompletableFuture<Void> listen(MessageHandler handler) {
String url = backendUrl + "/" + topic + "/json";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.build();
return client.sendAsync(request, HttpResponse.BodyHandlers.ofLines())
.thenAccept(response -> response.body().forEach(line -> {
if (line.contains("\"message\"")) {
handler.onMessage(line);
}
}));
}
public CompletableFuture<Void> listen(MessageHandler handler) {
String url = backendUrl + "/" + topic + "/json";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.build();
return client.sendAsync(request, HttpResponse.BodyHandlers.ofLines())
.thenAccept(response -> response.body().forEach(line -> {
// Basic JSON parsing - consider using a proper JSON library
if (line.trim().matches(".*\"message\"\\s*:\\s*\".*\".*")) {
handler.onMessage(line);
}
}))
.exceptionally(e -> {
System.err.println("Failed to listen for messages: " + e.getMessage());
return null;
});
}
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloModel.java around lines 35 to 47, the code
naively checks for the substring "\"message\"" and lacks error handling; replace
this with proper JSON parsing (e.g., Jackson ObjectMapper or Gson) to parse each
line into a JSON object and extract the "message" field explicitly, call
handler.onMessage only when the field exists and is of the expected type, and
wrap parsing/processing in try/catch to log parsing errors and avoid crashing
the stream; also add error handling for the sendAsync future (handle
exceptionally, timeouts, and connection failures) so failures are logged and the
returned CompletableFuture completes exceptionally or recovers appropriately.

Comment on lines +9 to +18
void testModelInitialization() {
// Försök skapa modellen, hantera fel ifall BACKEND_URL saknas
try {
HelloModel model = new HelloModel("test-topic");
assertNotNull(model, "Model should be created successfully");
} catch (IllegalStateException e) {
// Om miljövariabeln saknas ska felet ha rätt meddelande
assertTrue(e.getMessage().contains("BACKEND_URL"), "Exception should mention BACKEND_URL");
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Test always passes and doesn't verify model behavior.

The try-catch structure makes this test pass in both success and failure scenarios. Additionally, creating a model with a missing BACKEND_URL should fail (as per line 17-19 in HelloModel.java), but the test accepts this as valid. This test provides false confidence.

Apply this diff to properly test model initialization:

     @Test
     void testModelInitialization() {
-        // Försök skapa modellen, hantera fel ifall BACKEND_URL saknas
-        try {
-            HelloModel model = new HelloModel("test-topic");
-            assertNotNull(model, "Model should be created successfully");
-        } catch (IllegalStateException e) {
-            // Om miljövariabeln saknas ska felet ha rätt meddelande
-            assertTrue(e.getMessage().contains("BACKEND_URL"), "Exception should mention BACKEND_URL");
-        }
+        // Test requires BACKEND_URL to be set in test environment
+        String originalUrl = System.getenv("BACKEND_URL");
+        if (originalUrl == null || originalUrl.isBlank()) {
+            // If BACKEND_URL is not set, verify exception is thrown
+            assertThrows(IllegalStateException.class, 
+                () -> new HelloModel("test-topic"),
+                "Model should throw IllegalStateException when BACKEND_URL is not set");
+        } else {
+            // If BACKEND_URL is set, verify model is created
+            HelloModel model = new HelloModel("test-topic");
+            assertNotNull(model, "Model should be created successfully when BACKEND_URL is set");
+        }
     }
🤖 Prompt for AI Agents
In src/test/java/com/example/HelloModelTest.java around lines 9 to 18, the
current try-catch makes the test pass regardless of outcome and thus gives false
confidence; replace it with two explicit assertions: (1) set up a valid
BACKEND_URL env/config (or mock) and assert that new HelloModel("test-topic") is
created (assertNotNull) and behaves as expected, and (2) write a separate test
that uses assertThrows(IllegalStateException.class, () -> new
HelloModel("test-topic")) and assert the thrown exception's message contains
"BACKEND_URL" to verify the failure path when the env var is missing. Ensure you
remove the broad try-catch so both success and failure cases are independently
verified.

Comment on lines +20 to +28
@Test
void testBackendUrlDefault() {
// Testar att miljövariabeln hanteras
String backendUrl = System.getenv("BACKEND_URL");
if (backendUrl == null || backendUrl.isBlank()) {
backendUrl = "https://ntfy.sh";
}
assertTrue(backendUrl.startsWith("https://"), "BACKEND_URL should start with https://");
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Test doesn't verify HelloModel behavior.

This test checks environment variable logic but doesn't test how HelloModel handles the BACKEND_URL. It should verify the model's actual behavior, not replicate the model's internal logic in the test.

Consider removing this test or rewriting it to verify that HelloModel properly uses the backend URL (e.g., by mocking HTTP calls or testing against a test server).

🤖 Prompt for AI Agents
In src/test/java/com/example/HelloModelTest.java around lines 20-28, the test
only re-implements environment-variable logic instead of asserting HelloModel
behavior; replace this test with one that verifies HelloModel actually uses the
BACKEND_URL (or a provided backend URL) by injecting/configuring the URL into
the model and mocking the HTTP client or starting a lightweight test server,
then assert that HelloModel makes requests to the expected https:// URL (use
Mockito to mock the HTTP client and verify the request target or use a test HTTP
server and assert the received request host/path); alternatively remove the
redundant env-only test if no behavioral test is added.

Comment on lines +30 to +37
@Test
void testSendMessageFormatting() {
String userMessage = "Hej världen!";
String formatted = "{\"message\": \"" + userMessage + "\"}";
assertTrue(formatted.contains("Hej världen!"));
assertTrue(formatted.contains("{"));
assertTrue(formatted.contains("}"));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Test doesn't call HelloModel methods.

This test validates string concatenation logic within the test itself rather than testing HelloModel.sendMessage(). It doesn't verify that the model actually formats messages correctly.

The test should verify the actual HTTP request sent by sendMessage(). Consider using a mock HTTP server or intercepting HTTP requests to validate the message format. Alternatively, if you want to test JSON formatting, extract it to a separate method in HelloModel and test that method.

🤖 Prompt for AI Agents
In src/test/java/com/example/HelloModelTest.java around lines 30 to 37, the test
only constructs a string locally instead of invoking HelloModel.sendMessage(),
so it doesn't verify the model's behavior; change the test to either (A) call
HelloModel.sendMessage("Hej världen!") against a controllable HTTP endpoint (use
MockWebServer, WireMock, or an injected mock HTTP client), capture the outgoing
HTTP request body and assert it equals/contains the expected JSON (including
braces and the message), then shut down the mock server, or (B) refactor
HelloModel to extract the JSON formatting into a package-visible method (e.g.,
formatMessage) and write a unit test that calls that method and asserts the
formatted JSON; ensure mocks or extraction are used so the test validates code
in HelloModel rather than recreating the formatting logic in the test.

@kappsegla kappsegla closed this Nov 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants