Skip to content

Model#1

Closed
Rickank wants to merge 21 commits into
mainfrom
model
Closed

Model#1
Rickank wants to merge 21 commits into
mainfrom
model

Conversation

@Rickank

@Rickank Rickank commented Nov 13, 2025

Copy link
Copy Markdown
Collaborator

pull request for feedback

Summary by CodeRabbit

  • New Features

    • Real-time chat UI: timestamped message list with auto-scroll, text input and Send action.
    • Pluggable async messaging connection with HTTP/JSON transport and message DTO support.
  • Chores

    • Wider initial window, .env added to gitignore, module/dependency updates for HTTP/JSON/dotenv support.
    • Added a builder-style parameter object and a singleton utility.
  • Tests

    • Comprehensive unit and integration tests covering async sends, receives and mocked server scenarios.

@coderabbitai

coderabbitai Bot commented Nov 13, 2025

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Rickank has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 25 minutes and 42 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 29fefae and e07b438.

📒 Files selected for processing (2)
  • src/main/java/com/example/NtfyConnectionImpl.java (1 hunks)
  • src/test/java/com/example/HelloModelTest.java (1 hunks)

Walkthrough

Adds messaging support: new NtfyConnection API and HTTP implementation, HelloModel with async send/receive and JavaFX bindings, controller/FXML/UI updates (wider window), DTO and utility classes, Maven/module dependency additions, tests with WireMock, and .env added to .gitignore.

Changes

Cohort / File(s) Summary
VCS & Build
\.gitignore, pom.xml, src/main/java/module-info.java
.env added to .gitignore; pom.xml adds dotenv-java, jackson-databind, wiremock (test), slf4j-simple (test) and small formatting tweaks; module-info.java adds requires tools.jackson.databind, requires io.github.cdimascio.dotenv.java, and requires java.net.http.
Messaging API & DTO
src/main/java/com/example/NtfyConnection.java, src/main/java/com/example/NtfyMessageDto.java
New NtfyConnection interface (send, receive) and new NtfyMessageDto record annotated with @JsonIgnoreProperties(ignoreUnknown = true).
HTTP Connection Impl
src/main/java/com/example/NtfyConnectionImpl.java
New NtfyConnectionImpl using Dotenv, Java HttpClient and Jackson: async send(String, Consumer<Boolean>) (POST) and streaming receive(Consumer<NtfyMessageDto>) (GET /mytopic/json) parsing lines to DTOs with console-based error logging.
Model & Async Logic
src/main/java/com/example/HelloModel.java
New stateful HelloModel(NtfyConnection) with ObservableList<NtfyMessageDto>, StringProperty messageToSend, sendMessageAsync(Consumer<Boolean>), input validation, background receiving and FX-thread marshalling (runOnFx).
Controller, UI & App
src/main/java/com/example/HelloController.java, src/main/java/com/example/HelloFX.java, src/main/resources/com/example/hello-view.fxml
Controller binds ListView and TextArea to model, adds custom cell factory (timestamped messages) and auto-scroll; Scene width increased 640→1280; FXML replaced with VBox layout containing greeting, ListView, TextArea and Send button.
Builder & Data Class
src/main/java/com/example/ManyParameters.java, src/main/java/com/example/ManyParametersBuilder.java
Added ManyParameters with a five-arg constructor and a fluent ManyParametersBuilder to construct it.
Singleton Utility
src/main/java/com/example/Singelton.java
Added eager-initialized Singelton class with private constructor and getInstance().
Tests & Test Doubles
src/test/java/com/example/HelloModelTest.java, src/test/java/com/example/NtfyConnectionSpy.java
Added HelloModelTest with JavaFX headless setup, async send/receive validation and WireMock integration; added NtfyConnectionSpy test double to capture sends and simulate incoming messages.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant HelloController
    participant HelloModel
    participant NtfyConnection
    participant HttpServer

    User->>HelloController: enter text + click Send
    HelloController->>HelloModel: sendMessageAsync(callback)
    activate HelloModel
    HelloModel->>HelloModel: validate message
    alt valid
        HelloModel->>NtfyConnection: send(message, callback)
        activate NtfyConnection
        NtfyConnection->>HttpServer: POST /mytopic (body)
        HttpServer-->>NtfyConnection: 2xx / error
        NtfyConnection-->>HelloModel: callback(success/fail)
        deactivate NtfyConnection
        opt success
            HelloModel-->>HelloController: clear bound input (via property)
        end
    else invalid
        HelloModel-->>HelloController: invoke failure callback / no send
    end
    deactivate HelloModel
Loading
sequenceDiagram
    participant HttpServer
    participant NtfyConnection
    participant HelloModel
    participant JavaFX as Platform
    participant UI as ListView

    HttpServer->>NtfyConnection: stream JSON lines (GET /mytopic/json)
    activate NtfyConnection
    loop per line
        NtfyConnection->>NtfyConnection: parse -> NtfyMessageDto
        NtfyConnection->>HelloModel: handler.accept(dto)
        activate HelloModel
        HelloModel->>HelloModel: validate message
        alt valid
            HelloModel->>JavaFX: runOnFx(add to ObservableList)
            JavaFX->>UI: ListView updates & auto-scroll
        else invalid
            HelloModel-->>HelloModel: discard
        end
        deactivate HelloModel
    end
    deactivate NtfyConnection
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Pay special attention to:
    • HelloModel: thread-safety, Platform.runLater fallback and input validation behavior.
    • NtfyConnectionImpl: HTTP streaming parsing, resource handling, exception logging and callback semantics.
    • HelloController / FXML: bidirectional binding, cell factory formatting, auto-scroll edge cases.
    • Tests: potential flakiness in async tests (CountDownLatch) and WireMock integration correctness.

Poem

🐇 I hopped a message through the stream,
Tiny DTOs in a moonlit beam,
Bindings snug and ListViews scroll,
Sends hop out and tests patrol,
A rabbit cheers — the inbox whole.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.20% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Model' is extremely vague and does not meaningfully convey the actual changes made in this pull request, which include implementing a chat application with messaging functionality, HTTP connectivity, UI updates, and comprehensive test coverage. Replace the title with a more descriptive phrase such as 'Add chat messaging functionality with Ntfy integration' or 'Implement message model and async chat UI' to better reflect the primary changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🧹 Nitpick comments (13)
src/main/java/com/example/ManyParametersBuilder.java (2)

4-8: Consider validating required fields.

The builder allows computerName and method to remain null, which may result in invalid ManyParameters objects. If these fields are required, consider validating them in createManyParameters() or the setters.

Example validation in the factory method:

 public ManyParameters createManyParameters() {
+    if (computerName == null || computerName.isEmpty()) {
+        throw new IllegalStateException("computerName is required");
+    }
+    if (method == null || method.isEmpty()) {
+        throw new IllegalStateException("method is required");
+    }
     return new ManyParameters(computerName, timeout, method, size, data);
 }

30-32: Consider defensive copying for mutable array parameter.

The data byte array is stored directly without copying. If the caller modifies the array after passing it to the builder, it will affect the built object. Consider creating a defensive copy if immutability is desired.

 public ManyParametersBuilder setData(byte[] data) {
-    this.data = data;
+    this.data = data != null ? data.clone() : null;
     return this;
 }
src/main/resources/com/example/hello-view.fxml (1)

26-27: Let the input box grow with the window

With the stage now 1280 px wide, the TextArea at Lines 26-27 keeps its 300 px prefWidth and won’t use the extra space. Giving it HBox.hgrow="ALWAYS" (and dropping the fixed width) lets the editor stretch, improving usability on wider displays.

-        <TextArea fx:id="messageInput" prefHeight="50" prefWidth="300" wrapText="true"/>
+        <TextArea fx:id="messageInput" prefHeight="50" wrapText="true" HBox.hgrow="ALWAYS"/>
src/test/java/com/example/NtfyConnectionSpy.java (1)

10-13: Consider tracking send history for comprehensive test assertions.

The spy currently only stores the last message sent. For tests that send multiple messages, consider using a List to track all sent messages or add a send counter.

Example enhancement:

private final List<String> sentMessages = new ArrayList<>();

@Override
public boolean send(String message) {
    this.message = message;
    sentMessages.add(message);
    return true;
}

public List<String> getSentMessages() {
    return Collections.unmodifiableList(sentMessages);
}
src/main/java/com/example/NtfyConnectionImpl.java (2)

40-40: Fix typo in variable name.

Minor typo: reponse should be response.

Apply this diff:

-            var reponse = http.send(httpRequest, HttpResponse.BodyHandlers.discarding());
+            var response = http.send(httpRequest, HttpResponse.BodyHandlers.discarding());

42-45: Improve error logging.

Error messages should be logged to System.err instead of System.out, and should include exception details for debugging.

Apply this diff:

         } catch (IOException e) {
-            System.out.println("Error sending message");
+            System.err.println("Error sending message: " + e.getMessage());
+            e.printStackTrace();
         } catch (InterruptedException e) {
-            System.out.println("Interruped sending message");
+            System.err.println("Interrupted sending message: " + e.getMessage());
+            Thread.currentThread().interrupt(); // Restore interrupt status
         }

Note: When catching InterruptedException, you should restore the interrupt status by calling Thread.currentThread().interrupt().

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

43-49: Add return value to indicate send success/failure.

The sendMessage() method silently fails on invalid input or send errors, providing no feedback to the caller. Consider returning a boolean or throwing an exception.

Apply this diff:

-public void sendMessage() {
+public boolean sendMessage() {
     String msg = messageToSend.get();
     if (msg == null || msg.isBlank()) {
-        return;
+        return false;
     }
-    connection.send(msg);
+    return connection.send(msg);
 }

43-56: Extract duplicate validation logic.

Both sendMessage() and sendMessageAsync() have identical null/blank validation. Extract to a helper method to reduce duplication.

Apply this diff:

+private boolean isValidMessage(String msg) {
+    return msg != null && !msg.isBlank();
+}
+
 public void sendMessage() {
     String msg = messageToSend.get();
-    if (msg == null || msg.isBlank()) {
+    if (!isValidMessage(msg)) {
         return;
     }
     connection.send(msg);
 }

 public void sendMessageAsync(Consumer<Boolean> callback) {
     String msg = messageToSend.get();
-    if (msg == null || msg.isBlank()) {
+    if (!isValidMessage(msg)) {
         callback.accept(false);
         return;
     }
src/test/java/com/example/HelloModelTest.java (5)

68-89: Consider renaming this test for accuracy.

The test name shouldRejectBlankMessages suggests it tests whitespace-only strings ("blank"), but it only tests empty string and null. Consider renaming to shouldRejectEmptyAndNullMessages or add whitespace-only test case.


91-127: Remove duplicate test cases.

These two test methods duplicate the validation already covered by the loop in shouldRejectBlankMessages (lines 73-88). The empty string test (lines 91-108) and null test (lines 110-127) redundantly verify the same scenarios.

Consider removing these methods or consolidating into the parameterized loop approach:

-    @Test
-    void shouldFailWhenSendingEmptyText() throws InterruptedException {
-        NtfyConnectionSpy connectionSpy = new NtfyConnectionSpy();
-        HelloModel model = new HelloModel(connectionSpy);
-        model.setMessageToSend("");
-
-        CountDownLatch latch = new CountDownLatch(1);
-        boolean[] wasSuccessful = new boolean[1];
-
-        model.sendMessageAsync(success -> {
-            wasSuccessful[0] = success;
-            latch.countDown();
-        });
-
-        latch.await(500, TimeUnit.MILLISECONDS);
-        assertThat(wasSuccessful[0]).isFalse();
-        assertThat(connectionSpy.message).isNull();
-    }
-
-    @Test
-    void shouldFailWhenSendingNullMessage() throws InterruptedException {
-        NtfyConnectionSpy connectionSpy = new NtfyConnectionSpy();
-        HelloModel model = new HelloModel(connectionSpy);
-        model.setMessageToSend(null);
-
-        CountDownLatch latch = new CountDownLatch(1);
-        boolean[] wasSuccessful = new boolean[1];
-
-        model.sendMessageAsync(success -> {
-            wasSuccessful[0] = success;
-            latch.countDown();
-        });
-
-        latch.await(500, TimeUnit.MILLISECONDS);
-        assertThat(wasSuccessful[0]).isFalse();
-        assertThat(connectionSpy.message).isNull();
-    }

247-266: Remove duplicate test for invalid incoming messages.

This test redundantly validates the same cases already covered by shouldDiscardNullIncomingMessage (lines 208-224) and shouldIgnoreMessagesWithBlankContent (lines 226-246). All three invalid message types (empty, whitespace, null) have already been individually tested.

Consider removing this test method:

-    @Test
-    void shouldRejectAllInvalidIncomingMessages() throws InterruptedException {
-        NtfyConnectionSpy connectionSpy = new NtfyConnectionSpy();
-        HelloModel model = new HelloModel(connectionSpy);
-
-        CountDownLatch latch = new CountDownLatch(1);
-        model.getMessages().addListener((ListChangeListener<NtfyMessageDto>) c -> {
-            while (c.next()) {
-                if (c.wasAdded()) latch.countDown();
-            }
-        });
-
-        connectionSpy.simulateIncoming(new NtfyMessageDto("id1", 1, "message", "room", ""));
-        connectionSpy.simulateIncoming(new NtfyMessageDto("id2", 2, "message", "room", "  "));
-        connectionSpy.simulateIncoming(null);
-
-        boolean messageAdded = latch.await(500, TimeUnit.MILLISECONDS);
-        assertThat(messageAdded).isFalse();
-        assertThat(model.getMessages()).isEmpty();
-    }

268-283: Clarify or verify the GET stub usage.

Line 275 stubs a GET request to /mytopic/json but doesn't verify it's called. If this endpoint is required for the connection to function properly (e.g., for message polling), consider adding a verification. Otherwise, remove the unused stub.

If the GET stub is needed for receiving messages:

     assertThat(latch.await(1, TimeUnit.SECONDS)).isTrue();
     verify(postRequestedFor(urlEqualTo("/mytopic"))
             .withRequestBody(matching("Hello World")));
+    // Verify GET was called if receiving is part of the connection lifecycle

If not needed:

     stubFor(post("/mytopic").willReturn(ok()));
-    stubFor(get("/mytopic/json").willReturn(ok()));

156-184: Remove unnecessary try-catch block—sendMessageAsync handles exceptions internally.

The test's try-catch block (lines 172-180) will never execute. The sendMessageAsync method executes synchronously on the calling thread and catches all exceptions internally, passing results via the callback. The exception from the mocked send() method is caught within sendMessageAsync and reported as success=false to the callback—the method never throws. Remove the try-catch and rely on the callback to propagate the failure:

CountDownLatch latch = new CountDownLatch(1);
boolean[] wasSuccessful = new boolean[1];

model.sendMessageAsync(success -> {
    wasSuccessful[0] = success;
    latch.countDown();
});

latch.await(500, TimeUnit.MILLISECONDS);
assertThat(wasSuccessful[0]).isFalse();
📜 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 bf7e2bf.

📒 Files selected for processing (15)
  • .gitignore (1 hunks)
  • pom.xml (3 hunks)
  • 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/com/example/ManyParameters.java (1 hunks)
  • src/main/java/com/example/ManyParametersBuilder.java (1 hunks)
  • src/main/java/com/example/NtfyConnection.java (1 hunks)
  • src/main/java/com/example/NtfyConnectionImpl.java (1 hunks)
  • src/main/java/com/example/NtfyMessageDto.java (1 hunks)
  • src/main/java/com/example/Singelton.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)
  • src/test/java/com/example/NtfyConnectionSpy.java (1 hunks)
🔇 Additional comments (12)
src/main/java/com/example/Singelton.java (1)

3-13: Remove unused class or add documented purpose and implementation.

Verification confirms the Singelton class has no references anywhere in the codebase. Combined with its lack of state, behavior, and documentation, this appears to be dead code.

Either remove it or, if intended for future use, add:

  • Clear documentation of its purpose
  • Necessary fields and methods for its intended role
  • Explanation of why it exists if keeping as a template
.gitignore (1)

3-3: Keep .env out of VCS

Line 3 ensures local secrets and API tokens in .env stay out of version control—good preventive hygiene.

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

15-15: Scene width aligns with the richer chat layout

Line 15 bumps the default scene width to fit the wider messaging UI, preventing truncated controls on launch.

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

5-6: DTO ready for resilient deserialization

Line 5’s @JsonIgnoreProperties(ignoreUnknown = true) keeps the record tolerant to future payload fields—nice touch for external integrations.

src/main/java/module-info.java (1)

4-6: LGTM! Appropriate module dependencies added.

The new module requirements correctly support the added functionality: Jackson for JSON processing, Dotenv for environment configuration, and the HTTP client for network communication.

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

5-11: LGTM! Clean interface design.

The interface provides a clear contract for send/receive operations with appropriate return types.

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

43-54: Cell factory implementation looks good.

The custom cell factory correctly handles empty cells and formats messages with timestamps.

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

66-73: Excellent async message handling with Platform.runLater.

The receive implementation correctly updates the JavaFX observable list on the UI thread using Platform.runLater, and includes comprehensive null/blank validation.

src/test/java/com/example/HelloModelTest.java (4)

20-25: LGTM! Proper JavaFX initialization for headless testing.

The setup correctly initializes the JavaFX platform for test execution.


28-65: LGTM! Successful send tests are well-structured.

Both tests properly use CountDownLatch for async coordination and verify expected behavior.


130-154: LGTM! Connection failure handling is correct.

The test properly verifies that when the connection's send method returns false, the callback receives false and the message remains unchanged.


187-246: LGTM! Incoming message validation tests are well-structured.

These tests properly cover valid messages, null messages, and messages with blank content using ListChangeListener and CountDownLatch for async verification.

Comment thread pom.xml
Comment on lines +54 to +57
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>3.0.1</version>
</dependency>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix jackson-databind coordinates

Line 54 uses the non-existent tools.jackson.core group, so Maven will fail to resolve jackson-databind and the build breaks. Switch back to the official com.fasterxml.jackson.core group to restore dependency resolution.

         <dependency>
-            <groupId>tools.jackson.core</groupId>
+            <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
             <version>3.0.1</version>
         </dependency>
📝 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
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>3.0.1</version>
</dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>3.0.1</version>
</dependency>
🤖 Prompt for AI Agents
In pom.xml around lines 54 to 57, the dependency uses an incorrect groupId
"tools.jackson.core" so Maven cannot resolve jackson-databind; change the
groupId to the official "com.fasterxml.jackson.core" to restore dependency
resolution and keep the version as intended.

public class HelloController {

private final HelloModel model = new HelloModel();
private final HelloModel model = new HelloModel(new NtfyConnectionImpl());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Inject NtfyConnection instead of hardcoding implementation.

Direct instantiation of NtfyConnectionImpl creates tight coupling and prevents dependency injection, making testing difficult and violating the dependency inversion principle.

Consider using constructor injection:

-    private final HelloModel model = new HelloModel(new NtfyConnectionImpl());
+    private final NtfyConnection connection;
+    private final HelloModel model;
+
+    public HelloController() {
+        this(new NtfyConnectionImpl());
+    }
+
+    public HelloController(NtfyConnection connection) {
+        this.connection = connection;
+        this.model = new HelloModel(connection);
+    }

Alternatively, use a factory pattern or DI framework if the project scales.

📝 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
private final HelloModel model = new HelloModel(new NtfyConnectionImpl());
private final NtfyConnection connection;
private final HelloModel model;
public HelloController() {
this(new NtfyConnectionImpl());
}
public HelloController(NtfyConnection connection) {
this.connection = connection;
this.model = new HelloModel(connection);
}
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloController.java around line 15, replace the
direct instantiation of NtfyConnectionImpl (private final HelloModel model = new
HelloModel(new NtfyConnectionImpl());) with constructor injection: add a
constructor that accepts an NtfyConnection (interface) and assign model from
that dependency so the controller depends on the abstraction; update call sites
(or framework wiring) to pass a NtfyConnectionImpl instance or a mock in tests;
alternatively, provide a factory/provider that returns NtfyConnection and use
that in the constructor to keep the controller decoupled.

Comment on lines 67 to 70
private void sendMessage(ActionEvent actionEvent) {
model.sendMessage();
messageInput.clear();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Blocking send operation may freeze UI; add error handling.

The model.sendMessage() call is synchronous and may block the JavaFX application thread if the network request is slow. Consider using model.sendMessageAsync() with a callback to provide user feedback on success/failure.

Apply this diff to use async sending with feedback:

 @FXML
 private void sendMessage(ActionEvent actionEvent) {
-    model.sendMessage();
-    messageInput.clear();
+    model.sendMessageAsync(success -> {
+        Platform.runLater(() -> {
+            if (success) {
+                messageInput.clear();
+            } else {
+                // Consider showing error message to user
+                System.err.println("Failed to send message");
+            }
+        });
+    });
 }
📝 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
private void sendMessage(ActionEvent actionEvent) {
model.sendMessage();
messageInput.clear();
}
private void sendMessage(ActionEvent actionEvent) {
model.sendMessageAsync(success -> {
Platform.runLater(() -> {
if (success) {
messageInput.clear();
} else {
// Consider showing error message to user
System.err.println("Failed to send message");
}
});
});
}

Comment thread src/main/java/com/example/HelloModel.java
Comment on lines +5 to +8
public ManyParameters(String computerName, int timeout,
String method, int size, byte[] data) {

}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Constructor parameters are never stored.

The constructor accepts five parameters but does nothing with them. The class has no fields to store this data, making the constructor and the entire class effectively useless.

Add fields and assign the constructor parameters:

 public class ManyParameters {
+    private final String computerName;
+    private final int timeout;
+    private final String method;
+    private final int size;
+    private final byte[] data;

     public ManyParameters(String computerName, int timeout,
                           String method, int size, byte[] data) {
-
+        this.computerName = computerName;
+        this.timeout = timeout;
+        this.method = method;
+        this.size = size;
+        this.data = data;
     }
📝 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 ManyParameters(String computerName, int timeout,
String method, int size, byte[] data) {
}
public class ManyParameters {
private final String computerName;
private final int timeout;
private final String method;
private final int size;
private final byte[] data;
public ManyParameters(String computerName, int timeout,
String method, int size, byte[] data) {
this.computerName = computerName;
this.timeout = timeout;
this.method = method;
this.size = size;
this.data = data;
}
}
🤖 Prompt for AI Agents
In src/main/java/com/example/ManyParameters.java around lines 5 to 8, the
constructor accepts five parameters but does not store them; add private fields
for each parameter (e.g., computerName, timeout, method, size, data) with
appropriate types (make them private and final if immutability is desired) and
assign the constructor parameters to those fields inside the constructor;
optionally add getters for access and defensively copy the byte[] (or document
mutability) when storing/returning it.

Comment thread src/main/java/com/example/NtfyConnectionImpl.java Outdated
public boolean send(String message) {
HttpRequest httpRequest = HttpRequest.newBuilder()
.POST(HttpRequest.BodyPublishers.ofString(message))
.header("Cache", "no")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix invalid HTTP header.

The header Cache: no is invalid. Use Cache-Control: no-cache instead to properly disable caching.

Apply this diff:

-                .header("Cache", "no")
+                .header("Cache-Control", "no-cache")
📝 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
.header("Cache", "no")
.header("Cache-Control", "no-cache")
🤖 Prompt for AI Agents
In src/main/java/com/example/NtfyConnectionImpl.java around line 33, the code
sets an invalid HTTP header "Cache: no"; replace it with a valid cache-control
header by changing the header call to use "Cache-Control" with the value
"no-cache" (i.e., .header("Cache-Control", "no-cache")) so caching is properly
disabled.

Comment thread src/main/java/com/example/NtfyConnectionImpl.java Outdated
Comment on lines +3 to +13
public class Singelton {

private final static Singelton instance = new Singelton();

private Singelton(){

}

public static Singelton getInstance(){
return instance;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix typo in class name: "Singelton" → "Singleton".

The class name is misspelled throughout the file. This should be corrected to "Singleton" for proper naming and maintainability.

The file should also be renamed from Singelton.java to Singleton.java to match the corrected class name.

Apply this diff to fix the typo:

-public class Singelton {
+public class Singleton {

-    private final static Singelton instance = new Singelton();
+    private final static Singleton instance = new Singleton();

-    private Singelton(){
+    private Singleton(){

     }

-    public static Singelton getInstance(){
+    public static Singleton getInstance(){
         return instance;
     }
 }
📝 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 class Singelton {
private final static Singelton instance = new Singelton();
private Singelton(){
}
public static Singelton getInstance(){
return instance;
}
public class Singleton {
private final static Singleton instance = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}
🤖 Prompt for AI Agents
In src/main/java/com/example/Singelton.java around lines 3 to 13, the class and
file name are misspelled as "Singelton"; rename the class to "Singleton" and
update all references inside the file (class declaration, constructor, static
field, and getInstance return type/name) to use "Singleton", then rename the
file to Singleton.java so the filename matches the corrected public class name.

import java.util.function.Consumer;

public class NtfyConnectionSpy implements NtfyConnection {
String message;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Make field private and add getter for test assertions.

The message field should be private to maintain encapsulation. Add a public getter so tests can verify what was sent.

Apply this diff:

-    String message;
+    private String message;
+
+    public String getMessage() {
+        return message;
+    }
📝 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
String message;
private String message;
public String getMessage() {
return message;
}
🤖 Prompt for AI Agents
In src/test/java/com/example/NtfyConnectionSpy.java around line 6, the field
'message' is currently package-private; make it private to enforce encapsulation
and add a public getter (e.g., getMessage()) so tests can read its value. Update
the field declaration to private and implement a simple public String
getMessage() method that returns the private field; adjust any tests that
reference the field directly to call the getter.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf7e2bf and 8e888be.

📒 Files selected for processing (1)
  • src/main/java/com/example/HelloModel.java (1 hunks)

Comment thread src/main/java/com/example/HelloModel.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
src/main/java/com/example/HelloModel.java (3)

43-49: The blocking UI and error handling concerns remain unaddressed.

The previous review correctly identified that connection.send(msg) performs a synchronous HTTP call that will freeze the JavaFX UI thread. This critical issue has not been resolved in the current code.


51-64: The misleading "async" naming remains unresolved.

The previous review correctly noted that this method is not truly asynchronous—it still blocks the calling thread with a synchronous connection.send(). The misleading name persists in the current code.


66-82: IllegalStateException added to catch block—problem expanded rather than fixed.

The previous review correctly identified the UnsupportedOperationException catch as problematic (mixing test concerns into production code). Rather than addressing this, the current code now also catches IllegalStateException at line 78, expanding the anti-pattern.

This suggests production code is encountering platform exceptions that should be properly handled through architecture (e.g., dependency injection of a thread dispatcher), not silently caught and worked around.

The recommended ThreadDispatcher abstraction from the previous review remains the correct solution.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e888be and 8f79299.

📒 Files selected for processing (1)
  • src/main/java/com/example/HelloModel.java (1 hunks)

Comment on lines +16 to +19
public HelloModel(NtfyConnection connection) {
this.connection = connection;
receiveMessage();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Constructor starts background operations and lacks cleanup mechanism.

Starting receiveMessage() in the constructor is problematic:

  1. The background receive process begins immediately, before the caller can configure the object or register listeners
  2. There's no stop() or close() method to shut down the receive operation, potentially causing resource leaks
  3. No null check on connection parameter—will cause NPE if null is passed

Consider refactoring:

 public HelloModel(NtfyConnection connection) {
+    if (connection == null) {
+        throw new IllegalArgumentException("connection cannot be null");
+    }
     this.connection = connection;
-    receiveMessage();
 }
 
+public void start() {
+    receiveMessage();
+}
+
+public void stop() {
+    // Add mechanism to stop receiving messages
+    // and clean up resources
+}

Alternatively, implement AutoCloseable for proper resource management.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/com/example/HelloModel.java around lines 16 to 19, the
constructor immediately starts background receiveMessage() and lacks cleanup and
null-checks; change constructor to validate the NtfyConnection (throw
IllegalArgumentException if null) and remove the automatic call to
receiveMessage(); instead provide explicit lifecycle methods like startReceive()
and stop()/close() (implement AutoCloseable) so callers can register listeners
before starting; ensure the background task uses a controllable executor or
thread with a stop flag/interrupt and that stop()/close() shuts it down and
releases resources to prevent leaks.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/test/java/com/example/HelloModelTest.java (3)

73-132: Consider consolidating duplicate test logic.

The tests shouldFailWhenSendingEmptyText and shouldFailWhenSendingNullMessage duplicate logic already covered in shouldRejectBlankMessages (which tests both empty and null at line 78). Consider removing the redundant tests to reduce maintenance overhead.


192-271: Good coverage of incoming message validation.

The receiving message tests properly verify that the model correctly handles valid messages and rejects invalid ones (null, empty, whitespace).

Note: shouldRejectAllInvalidIncomingMessages (lines 253-271) duplicates scenarios already covered by the preceding tests, though it's a reasonable defensive/integration check.


273-288: Remove unused GET stub at line 280.

The GET stub stubFor(get("/mytopic/json").willReturn(ok())) is never called or verified in this test—the test only invokes model.sendMessageAsync() which triggers the POST. The hardcoded topic "mytopic" matches what NtfyConnectionImpl uses (hardcoded at lines 34 and 54), so there is no mismatch concern.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f79299 and 64ef5aa.

📒 Files selected for processing (1)
  • src/test/java/com/example/HelloModelTest.java (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: AI-Powered Feedback and Autograding
🔇 Additional comments (1)
src/test/java/com/example/HelloModelTest.java (1)

33-70: LGTM!

The successful send tests are well-structured with proper async handling using CountDownLatch and verify both single and multiple consecutive message sends correctly.

Comment thread src/test/java/com/example/HelloModelTest.java Outdated
Comment thread src/test/java/com/example/HelloModelTest.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (5)
pom.xml (1)

53-57: Fix jackson-databind coordinates—build will fail.

Line 54 specifies a non-existent groupId tools.jackson.core; Maven cannot resolve the dependency and the build will fail. The official groupId is com.fasterxml.jackson.core.

         <dependency>
-            <groupId>tools.jackson.core</groupId>
+            <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
             <version>3.0.1</version>
         </dependency>
src/main/java/com/example/HelloModel.java (4)

16-19: Constructor issues persist: no null check and starts background operations.

The constructor still has the previously flagged issues:

  1. No null check on connection parameter—will cause NPE if null is passed
  2. Starts receiveMessage() immediately, before the caller can configure the object
  3. No cleanup mechanism to stop the background receive operation

These concerns were already raised in previous reviews.


43-49: Blocking call and missing error handling still present.

The previously identified issues remain unaddressed:

  1. connection.send(msg) is a synchronous HTTP call that will block and freeze the UI when invoked from the JavaFX thread
  2. No error handling for IOException or InterruptedException that can be thrown
  3. The boolean return value is ignored, providing no feedback on success/failure

Consider making this async or moving to a background thread as suggested in previous reviews.


51-64: Method name remains misleading—still synchronous despite "Async" suffix.

As previously noted, sendMessageAsync still performs a synchronous connection.send() call on the current thread. The "Async" name implies non-blocking execution that isn't present. Consider renaming to sendMessageWithCallback or actually implementing async execution with CompletableFuture.supplyAsync() as suggested in previous reviews.


66-87: Questionable error-handling pattern still present.

The previously flagged issue with catching UnsupportedOperationException (and now IllegalStateException) remains. This pattern suggests testing concerns are leaking into production code. The fallback to direct list modification may violate JavaFX threading requirements.

Consider implementing a ThreadDispatcher abstraction as suggested in previous reviews to properly separate test and production threading concerns.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 64ef5aa and d5c2cd2.

📒 Files selected for processing (2)
  • pom.xml (3 hunks)
  • src/main/java/com/example/HelloModel.java (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: AI-Powered Feedback and Autograding

Comment thread pom.xml
Comment on lines +58 to +63
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<version>4.0.0-beta.15</version>
<scope>test</scope>
</dependency>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify WireMock beta version stability.

The dependency uses WireMock 4.0.0-beta.15, a pre-release version. While acceptable for test scope, verify that this beta version is stable enough for your CI/CD pipeline and has no known critical issues that could cause flaky tests.


🌐 Web query:

What is the latest stable version of WireMock and any known issues with version 4.0.0-beta.15?

💡 Result:

  • Latest stable WireMock (stable, non‑beta): 3.13.1. [1][2]

  • Known/important notes about 4.0.0‑beta.15:

    • 4.x is beta (breaking changes expected): Java 17 is now the baseline and Jetty 12 is shipped by default (Jetty 11 no longer supported). Use beta releases for evaluation only. [3]
    • Behaviour changes called out in the v4 docs (e.g., multiple Content‑Type header handling differs from some 3.x/Jetty11 behaviour). [3]
    • Real‑world report of a regression caused by a 4.0.0‑beta.15 release (a downstream tool—Exercism’s configlet—reported issues and rolled back to beta.14). Investigate compatibility with your toolchain before upgrading. [4]
    • 4.0.0‑beta.15 is published to Maven Central (so available), but later beta releases (e.g. beta.16) also exist; prefer staying on the 3.x stable line for production. [5][6]

Sources:
[1] WireMock community announcement (3.13.1).
[2] WireMock download/install docs showing 3.x as stable.
[3] WireMock v4 Beta docs (breaking changes: Java 17, Jetty 12, header behaviour).
[4] Exercism forum report of issues after 4.0.0‑beta.15.
[5] Maven Central / repo listing for org.wiremock (4.0.0‑beta.15).
[6] Javadoc/Maven index showing available beta versions.


Upgrade WireMock to the latest stable version.

The dependency currently uses WireMock 4.0.0-beta.15. The latest stable version is 3.13.1. While 4.0.0-beta.15 is available, it has known issues including:

  • A reported regression that caused Exercism's configlet tool to roll back to beta.14
  • Breaking changes requiring Java 17 and Jetty 12, with Jetty 11 no longer supported
  • Behaviour differences in header handling compared to the 3.x line

Upgrade to version 3.13.1 for stability and to prevent potential flaky tests.

🤖 Prompt for AI Agents
In pom.xml around lines 58 to 63, the WireMock dependency is pinned to
4.0.0-beta.15; change the <version> value to 3.13.1 to upgrade to the latest
stable 3.x release, then run the project tests (mvn test) and resolve any API
incompatibilities that appear; ensure your build JDK and any Jetty/servlet
dependencies remain compatible with WireMock 3.13.1 and update related test
imports or setup code if compilation or runtime errors occur.

Comment thread src/main/java/com/example/HelloModel.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cfcb6b and b665727.

📒 Files selected for processing (1)
  • src/test/java/com/example/HelloModelTest.java (1 hunks)
🔇 Additional comments (2)
src/test/java/com/example/HelloModelTest.java (2)

1-298: Well-structured test suite with comprehensive coverage.

The test suite demonstrates solid testing practices:

  • Clear separation of test categories (successful sends, invalid inputs, error handling, receiving, integration)
  • Consistent async testing pattern using CountDownLatch
  • Good use of test doubles (NtfyConnectionSpy for unit tests, WireMock for integration)
  • Descriptive test names following clear conventions
  • Appropriate timeout handling at both class and test level

287-288: Remove the unused GET endpoint stub.

The test calls sendMessageAsync(), which only triggers the POST request to /mytopic. The GET endpoint to /mytopic/json is used exclusively by the receive() method, which is not invoked in this test. Removing the unused stub (line 288) will keep the test focused on its actual behavior.

import static org.assertj.core.api.Assertions.assertThat;

@WireMockTest
@Timeout(10) // Global timeout för alla tester i klassen

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Replace Swedish comment with English.

The comment "Global timeout för alla tester i klassen" should be in English for consistency and international collaboration.

Apply this diff:

-@Timeout(10) // Global timeout för alla tester i klassen
+@Timeout(10) // Global timeout for all tests in this class
📝 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
@Timeout(10) // Global timeout för alla tester i klassen
@Timeout(10) // Global timeout for all tests in this class
🤖 Prompt for AI Agents
In src/test/java/com/example/HelloModelTest.java around line 19, the inline
comment for the @Timeout(10) annotation is written in Swedish ("Global timeout
för alla tester i klassen"); replace it with an English comment such as "Global
timeout for all tests in the class" keeping the annotation and spacing intact so
the code semantics do not change.

Comment on lines +100 to +138
@Test
void shouldFailWhenSendingEmptyText() throws InterruptedException {
NtfyConnectionSpy connectionSpy = new NtfyConnectionSpy();
HelloModel model = new HelloModel(connectionSpy);
model.setMessageToSend("");

CountDownLatch latch = new CountDownLatch(1);
boolean[] wasSuccessful = new boolean[1];

model.sendMessageAsync(success -> {
wasSuccessful[0] = success;
latch.countDown();
});

boolean completed = latch.await(5, TimeUnit.SECONDS);
assertThat(completed).as("Timed out waiting for empty text rejection").isTrue();
assertThat(wasSuccessful[0]).isFalse();
assertThat(connectionSpy.message).isNull();
}

@Test
void shouldFailWhenSendingNullMessage() throws InterruptedException {
NtfyConnectionSpy connectionSpy = new NtfyConnectionSpy();
HelloModel model = new HelloModel(connectionSpy);
model.setMessageToSend(null);

CountDownLatch latch = new CountDownLatch(1);
boolean[] wasSuccessful = new boolean[1];

model.sendMessageAsync(success -> {
wasSuccessful[0] = success;
latch.countDown();
});

boolean completed = latch.await(5, TimeUnit.SECONDS);
assertThat(completed).as("Timed out waiting for null message rejection").isTrue();
assertThat(wasSuccessful[0]).isFalse();
assertThat(connectionSpy.message).isNull();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove duplicate test methods.

Both shouldFailWhenSendingEmptyText and shouldFailWhenSendingNullMessage duplicate the coverage already provided by shouldRejectBlankMessages (lines 77-98), which tests both empty string and null inputs in a single parameterized test. The separate methods add maintenance overhead without providing additional value.

Apply this diff to remove the redundant tests:

-    @Test
-    void shouldFailWhenSendingEmptyText() throws InterruptedException {
-        NtfyConnectionSpy connectionSpy = new NtfyConnectionSpy();
-        HelloModel model = new HelloModel(connectionSpy);
-        model.setMessageToSend("");
-
-        CountDownLatch latch = new CountDownLatch(1);
-        boolean[] wasSuccessful = new boolean[1];
-
-        model.sendMessageAsync(success -> {
-            wasSuccessful[0] = success;
-            latch.countDown();
-        });
-
-        boolean completed = latch.await(5, TimeUnit.SECONDS);
-        assertThat(completed).as("Timed out waiting for empty text rejection").isTrue();
-        assertThat(wasSuccessful[0]).isFalse();
-        assertThat(connectionSpy.message).isNull();
-    }
-
-    @Test
-    void shouldFailWhenSendingNullMessage() throws InterruptedException {
-        NtfyConnectionSpy connectionSpy = new NtfyConnectionSpy();
-        HelloModel model = new HelloModel(connectionSpy);
-        model.setMessageToSend(null);
-
-        CountDownLatch latch = new CountDownLatch(1);
-        boolean[] wasSuccessful = new boolean[1];
-
-        model.sendMessageAsync(success -> {
-            wasSuccessful[0] = success;
-            latch.countDown();
-        });
-
-        boolean completed = latch.await(5, TimeUnit.SECONDS);
-        assertThat(completed).as("Timed out waiting for null message rejection").isTrue();
-        assertThat(wasSuccessful[0]).isFalse();
-        assertThat(connectionSpy.message).isNull();
-    }
-
📝 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
@Test
void shouldFailWhenSendingEmptyText() throws InterruptedException {
NtfyConnectionSpy connectionSpy = new NtfyConnectionSpy();
HelloModel model = new HelloModel(connectionSpy);
model.setMessageToSend("");
CountDownLatch latch = new CountDownLatch(1);
boolean[] wasSuccessful = new boolean[1];
model.sendMessageAsync(success -> {
wasSuccessful[0] = success;
latch.countDown();
});
boolean completed = latch.await(5, TimeUnit.SECONDS);
assertThat(completed).as("Timed out waiting for empty text rejection").isTrue();
assertThat(wasSuccessful[0]).isFalse();
assertThat(connectionSpy.message).isNull();
}
@Test
void shouldFailWhenSendingNullMessage() throws InterruptedException {
NtfyConnectionSpy connectionSpy = new NtfyConnectionSpy();
HelloModel model = new HelloModel(connectionSpy);
model.setMessageToSend(null);
CountDownLatch latch = new CountDownLatch(1);
boolean[] wasSuccessful = new boolean[1];
model.sendMessageAsync(success -> {
wasSuccessful[0] = success;
latch.countDown();
});
boolean completed = latch.await(5, TimeUnit.SECONDS);
assertThat(completed).as("Timed out waiting for null message rejection").isTrue();
assertThat(wasSuccessful[0]).isFalse();
assertThat(connectionSpy.message).isNull();
}
🤖 Prompt for AI Agents
In src/test/java/com/example/HelloModelTest.java around lines 100 to 138, remove
the two redundant test methods shouldFailWhenSendingEmptyText and
shouldFailWhenSendingNullMessage because their cases are already covered by the
parameterized test shouldRejectBlankMessages (lines ~77-98); delete both method
blocks and any immediate now-unused local declarations inside them, run tests to
ensure compilation and remove any imports that become unused after deletion.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
src/main/java/com/example/HelloModel.java (3)

16-19: Constructor still lacks null validation and lifecycle control.

The issues previously flagged remain unaddressed:

  1. No null check on connection parameter—passing null will cause NPE on line 18
  2. Calling receiveMessage() immediately prevents callers from setting up listeners before messages start arriving
  3. No stop() or close() method to shut down the background receive operation

Apply this diff to add null validation and separate lifecycle:

 public HelloModel(NtfyConnection connection) {
+    if (connection == null) {
+        throw new IllegalArgumentException("connection cannot be null");
+    }
     this.connection = connection;
-    receiveMessage();
 }
+
+public void start() {
+    receiveMessage();
+}

Consider implementing AutoCloseable with a close() method to properly clean up resources.


43-49: Blocking network call and missing error handling remain unresolved.

The critical issues previously flagged persist:

  1. connection.send(msg) performs synchronous HTTP and will freeze the UI when called from the JavaFX thread
  2. No error handling—will propagate IOException or InterruptedException to caller
  3. Boolean return value ignored—no feedback to indicate success/failure

Make it async by dispatching to a background thread:

 public void sendMessage() {
     String msg = messageToSend.get();
     if (msg == null || msg.isBlank()) {
         return;
     }
-    connection.send(msg);
+    CompletableFuture.runAsync(() -> {
+        try {
+            connection.send(msg);
+        } catch (Exception e) {
+            System.err.println("Failed to send message: " + e.getMessage());
+        }
+    });
 }

51-64: Method name "async" remains misleading.

Despite the previous feedback, sendMessageAsync still performs a synchronous connection.send() call on the calling thread. The method name suggests non-blocking behavior that isn't implemented.

Either rename to reflect synchronous behavior or make it truly async:

Option 1: Rename

-public void sendMessageAsync(Consumer<Boolean> callback) {
+public void sendMessageWithCallback(Consumer<Boolean> callback) {

Option 2: Make truly async

 public void sendMessageAsync(Consumer<Boolean> callback) {
     String msg = messageToSend.get();
     if (msg == null || msg.isBlank()) {
         callback.accept(false);
         return;
     }

-    try {
-        boolean success = connection.send(msg);
-        callback.accept(success);
-    } catch (Exception e) {
-        callback.accept(false);
-    }
+    CompletableFuture.supplyAsync(() -> {
+        try {
+            return connection.send(msg);
+        } catch (Exception e) {
+            return false;
+        }
+    }).thenAccept(callback);
 }
🧹 Nitpick comments (1)
src/main/java/com/example/HelloModel.java (1)

21-23: Consider returning an unmodifiable view of the messages list.

Exposing the internal ObservableList directly allows external code to modify it (add/remove/clear), which could bypass your validation logic or cause unexpected state changes.

If external modification isn't needed, apply this diff:

+import javafx.collections.FXCollections;
+
 public ObservableList<NtfyMessageDto> getMessages() {
-    return messages;
+    return FXCollections.unmodifiableObservableList(messages);
 }

Note: Only apply this if you don't need external code to modify the list. For JavaFX bindings that only read, this is safe.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b665727 and 234a4e0.

📒 Files selected for processing (2)
  • src/main/java/com/example/HelloModel.java (1 hunks)
  • src/test/java/com/example/HelloModelTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/java/com/example/HelloModelTest.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: AI-Powered Feedback and Autograding
🔇 Additional comments (2)
src/main/java/com/example/HelloModel.java (2)

25-35: LGTM!

Standard JavaFX property pattern correctly implemented.


37-41: LGTM!

Simple greeting method with no issues.

Comment on lines 66 to 86
private void receiveMessage() {
connection.receive(message -> {
if (message == null || message.message() == null || message.message().isBlank()) {
return;
}
runOnFx(() -> messages.add(message));
});
}


private static void runOnFx(Runnable task) {
try {
if (Platform.isFxApplicationThread()) {
task.run();
} else {
Platform.runLater(task);
}
} catch (IllegalStateException | IllegalArgumentException e) {
task.run();
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Questionable fallback in runOnFx() exception handler.

The receiveMessage() implementation looks better, but runOnFx() at lines 76-86 has a problematic pattern: catching IllegalStateException | IllegalArgumentException and falling back to direct execution suggests testing concerns are leaking into production code.

If JavaFX isn't initialized or throws these exceptions, running task.run() directly (line 84) may still violate threading requirements for the ObservableList, potentially causing ConcurrentModificationException or UI corruption.

For testing without JavaFX, inject a ThreadDispatcher abstraction instead of catching platform exceptions:

// New interface
public interface ThreadDispatcher {
    void runOnUiThread(Runnable action);
}

// Production implementation
public class JavaFxDispatcher implements ThreadDispatcher {
    @Override
    public void runOnUiThread(Runnable action) {
        if (Platform.isFxApplicationThread()) {
            action.run();
        } else {
            Platform.runLater(action);
        }
    }
}

// Test implementation
public class DirectDispatcher implements ThreadDispatcher {
    @Override
    public void runOnUiThread(Runnable action) {
        action.run();
    }
}

Then inject the dispatcher into HelloModel and use it in receiveMessage(), eliminating the catch block entirely.

🤖 Prompt for AI Agents
In src/main/java/com/example/HelloModel.java around lines 66 to 86, replace the
current runOnFx exception-fallback pattern with a injected ThreadDispatcher
abstraction: add a ThreadDispatcher interface with runOnUiThread(Runnable),
implement JavaFxDispatcher (uses
Platform.isFxApplicationThread()/Platform.runLater) for production and
DirectDispatcher for tests, add a ThreadDispatcher field to HelloModel
constructor (or setter) and use dispatcher.runOnUiThread(...) in
receiveMessage() instead of calling runOnFx, and remove the try/catch fallback
that directly runs the task so threading behavior is explicit and testable.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/main/java/com/example/NtfyConnectionImpl.java (2)

19-22: Provide robust HOST_NAME configuration handling

Line 21 still calls Objects.requireNonNull(dotenv.get("HOST_NAME")), so the application crashes with a bare NullPointerException (or Dotenv exception) whenever .env is missing or the variable is unset—the same issue raised previously. Guard the load, emit a descriptive error, and fail fast with an actionable message.

     public NtfyConnectionImpl() {
-        Dotenv dotenv = Dotenv.load();
-        hostName = Objects.requireNonNull(dotenv.get("HOST_NAME"));
+        try {
+            Dotenv dotenv = Dotenv.load();
+            String configuredHost = dotenv.get("HOST_NAME");
+            if (configuredHost == null || configuredHost.isBlank()) {
+                throw new IllegalStateException("HOST_NAME must be set in .env or environment variables");
+            }
+            hostName = configuredHost;
+        } catch (Exception e) {
+            throw new IllegalStateException("Failed to load HOST_NAME configuration", e);
+        }
     }

33-33: Switch to valid Cache-Control header

Line 33 still sends Cache: no, which is not a valid HTTP header—the earlier review already flagged this. Servers ignore it, so the request is unintentionally cacheable. Replace it with Cache-Control: no-cache.

-                .header("Cache", "no")
+                .header("Cache-Control", "no-cache")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 234a4e0 and 120da00.

📒 Files selected for processing (6)
  • src/main/java/com/example/HelloController.java (1 hunks)
  • src/main/java/com/example/HelloModel.java (1 hunks)
  • src/main/java/com/example/NtfyConnection.java (1 hunks)
  • src/main/java/com/example/NtfyConnectionImpl.java (1 hunks)
  • src/test/java/com/example/HelloModelTest.java (1 hunks)
  • src/test/java/com/example/NtfyConnectionSpy.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/example/NtfyConnection.java
  • src/test/java/com/example/NtfyConnectionSpy.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: AI-Powered Feedback and Autograding

package com.example;

import io.github.cdimascio.dotenv.Dotenv;
import tools.jackson.databind.ObjectMapper;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix ObjectMapper import package

Line 4 imports tools.jackson.databind.ObjectMapper, but the library exposes com.fasterxml.jackson.databind.ObjectMapper. This typo breaks compilation. Point the import at the correct package.

-import tools.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectMapper;
📝 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
import tools.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
🤖 Prompt for AI Agents
In src/main/java/com/example/NtfyConnectionImpl.java around line 4, the import
uses tools.jackson.databind.ObjectMapper which is incorrect; update the import
to com.fasterxml.jackson.databind.ObjectMapper (the correct Jackson package) so
the class compiles and references the proper ObjectMapper implementation.

Comment thread src/test/java/com/example/HelloModelTest.java
@Rickank Rickank closed this Nov 13, 2025
@Rickank
Rickank deleted the model branch November 13, 2025 20:20
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.

1 participant