Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@
<artifactId>s3</artifactId>
<version>2.20.0</version>
</dependency>


</dependencies>

<build>
Expand All @@ -94,7 +96,39 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.14</version>

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

🏁 Script executed:

#!/bin/bash
# Check if JaCoCo 0.8.14 is available on Maven Central
curl -s "https://search.maven.org/solrsearch/select?q=g:org.jacoco+AND+a:jacoco-maven-plugin&rows=5&wt=json" | jq '.response.docs[] | {latestVersion, timestamp}'

Repository: ithsjava25/project-backend-org-random-coders

Length of output: 149


JaCoCo version 0.8.14 does not exist on Maven Central. The latest available version is 0.8.13. Update the plugin version to 0.8.13 or another available release.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pom.xml` at line 102, The JaCoCo plugin version declared as
<version>0.8.14</version> in the pom.xml is invalid on Maven Central; update
that <version> element for the JaCoCo plugin to a published release (e.g.,
0.8.13) so the build can resolve the dependency; locate the JaCoCo plugin
declaration in pom.xml and change the <version> value from 0.8.14 to 0.8.13 (or
another known-good released version).

<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>target/jacoco.exec</dataFile>
<outputDirectory>target/jacoco-ut</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
Comment on lines +99 to +129

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

Missing JaCoCo exclusions and coverage threshold per issue #110 requirements.

The linked issue #110 specifies:

  1. Configure exclusions for entities/, dto/, ErrorResponse, and Vet1177Application main class.
  2. Set a minimum coverage threshold of 60%.

Currently, the plugin only configures report generation without exclusions or enforcement.

Proposed fix to add exclusions and coverage check
             <plugin>
                 <groupId>org.jacoco</groupId>
                 <artifactId>jacoco-maven-plugin</artifactId>
                 <version>0.8.14</version>
+                <configuration>
+                    <excludes>
+                        <exclude>**/entities/**</exclude>
+                        <exclude>**/dto/**</exclude>
+                        <exclude>**/ErrorResponse.*</exclude>
+                        <exclude>**/Vet1177Application.*</exclude>
+                    </excludes>
+                </configuration>
                 <executions>
                     <execution>
                         <id>prepare-agent</id>
                         <goals>
                             <goal>prepare-agent</goal>
                         </goals>
                     </execution>
                     <execution>
                         <id>report</id>
                         <phase>prepare-package</phase>
                         <goals>
                             <goal>report</goal>
                         </goals>
                     </execution>
                     <execution>
                         <id>post-unit-test</id>
                         <phase>test</phase>
                         <goals>
                             <goal>report</goal>
                         </goals>
                         <configuration>
                             <dataFile>target/jacoco.exec</dataFile>
                             <outputDirectory>target/jacoco-ut</outputDirectory>
                         </configuration>
                     </execution>
+                    <execution>
+                        <id>check</id>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                        <configuration>
+                            <rules>
+                                <rule>
+                                    <element>BUNDLE</element>
+                                    <limits>
+                                        <limit>
+                                            <counter>LINE</counter>
+                                            <value>COVEREDRATIO</value>
+                                            <minimum>0.60</minimum>
+                                        </limit>
+                                    </limits>
+                                </rule>
+                            </rules>
+                        </configuration>
+                    </execution>
                 </executions>
             </plugin>
📝 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
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.14</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>target/jacoco.exec</dataFile>
<outputDirectory>target/jacoco-ut</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.14</version>
<configuration>
<excludes>
<exclude>**/entities/**</exclude>
<exclude>**/dto/**</exclude>
<exclude>**/ErrorResponse.*</exclude>
<exclude>**/Vet1177Application.*</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>target/jacoco.exec</dataFile>
<outputDirectory>target/jacoco-ut</outputDirectory>
</configuration>
</execution>
<execution>
<id>check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.60</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pom.xml` around lines 99 - 129, Update the org.jacoco:jacoco-maven-plugin
configuration to add exclusions and a coverage check: inside the plugin's
<executions> add or modify the report execution to include
<configuration><excludes> patterns for /**/entities/**, /**/dto/**,
**/ErrorResponse*, and **/Vet1177Application* (to exclude those classes/packages
from reports), and add a new execution (or augment existing) with goal "check"
that runs in the verify phase and contains a <rules> block enforcing a minimum
instruction/line/branch coverage of 60% (violation rules -> limit -> minimum
0.60) so the build fails if coverage drops below 60%. Ensure these changes
target the jacoco-maven-plugin configuration (executions/report and
executions/check) so exclusions and the 60% threshold are enforced.

</plugins>

</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package org.example.vet1177.dto.request.comment;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.util.Set;
import java.util.UUID;

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

class CreateCommentRequestValidationTest {

private static ValidatorFactory factory;
private static Validator validator;

@BeforeAll
static void setUp() {
factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}

@AfterAll
static void tearDown() {
factory.close();
}

// --- Happy paths ---

@Test
void validRequest_shouldProduceNoViolations() {
var request = new CreateCommentRequest(UUID.randomUUID(), "En giltig kommentar.");

Set<ConstraintViolation<CreateCommentRequest>> violations = validator.validate(request);

assertThat(violations).isEmpty();
}

@Test
void bodyWithExactly5000Characters_shouldProduceNoViolations() {
var request = new CreateCommentRequest(UUID.randomUUID(), "x".repeat(5000));

Set<ConstraintViolation<CreateCommentRequest>> violations = validator.validate(request);

assertThat(violations).isEmpty();
}

// --- Sad paths: recordId ---

@Test
void nullRecordId_shouldProduceViolationWithCorrectMessage() {
var request = new CreateCommentRequest(null, "En giltig kommentar.");

Set<ConstraintViolation<CreateCommentRequest>> violations = validator.validate(request);

assertThat(violations).hasSize(1);
assertThat(violations.iterator().next().getMessage()).isEqualTo("Ärende måste anges");
}

// --- Sad paths: body ---

@Test
void blankBody_shouldProduceViolationWithCorrectMessage() {
var request = new CreateCommentRequest(UUID.randomUUID(), " ");

Set<ConstraintViolation<CreateCommentRequest>> violations = validator.validate(request);

assertThat(violations).hasSize(1);
assertThat(violations.iterator().next().getMessage()).isEqualTo("Kommentar får inte vara tom");
}

@Test
void emptyBody_shouldProduceViolationWithCorrectMessage() {
var request = new CreateCommentRequest(UUID.randomUUID(), "");

Set<ConstraintViolation<CreateCommentRequest>> violations = validator.validate(request);

assertThat(violations).hasSize(1);
assertThat(violations.iterator().next().getMessage()).isEqualTo("Kommentar får inte vara tom");
}

@Test
void nullBody_shouldProduceViolationWithCorrectMessage() {
var request = new CreateCommentRequest(UUID.randomUUID(), null);

Set<ConstraintViolation<CreateCommentRequest>> violations = validator.validate(request);

assertThat(violations).hasSize(1);
assertThat(violations.iterator().next().getMessage()).isEqualTo("Kommentar får inte vara tom");
}

@Test
void bodyExceeding5000Characters_shouldProduceViolationWithCorrectMessage() {
var request = new CreateCommentRequest(UUID.randomUUID(), "x".repeat(5001));

Set<ConstraintViolation<CreateCommentRequest>> violations = validator.validate(request);

assertThat(violations).hasSize(1);
assertThat(violations.iterator().next().getMessage()).isEqualTo("Kommentar får max vara 5000 tecken");
}

@Test
void allFieldsInvalid_shouldProduceTwoViolations() {
var request = new CreateCommentRequest(null, "");

Set<ConstraintViolation<CreateCommentRequest>> violations = validator.validate(request);

assertThat(violations).hasSize(2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package org.example.vet1177.dto.request.comment;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.util.Set;

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

class UpdateCommentRequestValidationTest {

private static ValidatorFactory factory;
private static Validator validator;

@BeforeAll
static void setUp() {
factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}

@AfterAll
static void tearDown() {
factory.close();
}

// --- Happy paths ---

@Test
void validRequest_shouldProduceNoViolations() {
var request = new UpdateCommentRequest("Uppdaterad kommentar med ny information.");

Set<ConstraintViolation<UpdateCommentRequest>> violations = validator.validate(request);

assertThat(violations).isEmpty();
}

@Test
void bodyWithExactly5000Characters_shouldProduceNoViolations() {
var request = new UpdateCommentRequest("x".repeat(5000));

Set<ConstraintViolation<UpdateCommentRequest>> violations = validator.validate(request);

assertThat(violations).isEmpty();
}

// --- Sad paths: body ---

@Test
void blankBody_shouldProduceViolationWithCorrectMessage() {
var request = new UpdateCommentRequest(" ");

Set<ConstraintViolation<UpdateCommentRequest>> violations = validator.validate(request);

assertThat(violations).hasSize(1);
assertThat(violations.iterator().next().getMessage()).isEqualTo("Kommentar får inte vara tom");
}

@Test
void emptyBody_shouldProduceViolationWithCorrectMessage() {
var request = new UpdateCommentRequest("");

Set<ConstraintViolation<UpdateCommentRequest>> violations = validator.validate(request);

assertThat(violations).hasSize(1);
assertThat(violations.iterator().next().getMessage()).isEqualTo("Kommentar får inte vara tom");
}

@Test
void nullBody_shouldProduceViolationWithCorrectMessage() {
var request = new UpdateCommentRequest(null);

Set<ConstraintViolation<UpdateCommentRequest>> violations = validator.validate(request);

assertThat(violations).hasSize(1);
assertThat(violations.iterator().next().getMessage()).isEqualTo("Kommentar får inte vara tom");
}

@Test
void bodyExceeding5000Characters_shouldProduceViolationWithCorrectMessage() {
var request = new UpdateCommentRequest("x".repeat(5001));

Set<ConstraintViolation<UpdateCommentRequest>> violations = validator.validate(request);

assertThat(violations).hasSize(1);
assertThat(violations.iterator().next().getMessage()).isEqualTo("Kommentar får max vara 5000 tecken");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package org.example.vet1177.dto.response.comment;

import org.example.vet1177.entities.Comment;
import org.example.vet1177.entities.MedicalRecord;
import org.example.vet1177.entities.Role;
import org.example.vet1177.entities.User;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.Instant;
import java.util.UUID;

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

class CommentResponseTest {

private Comment comment;
private User author;
private MedicalRecord medicalRecord;
private UUID commentId;
private UUID authorId;
private UUID recordId;


@BeforeEach
void setUp() throws Exception {
commentId = UUID.randomUUID();
authorId = UUID.randomUUID();
recordId = UUID.randomUUID();

author = new User("Dr. Sara Lindqvist", "sara@vet.se", "hash", Role.VET);
setPrivateField(author, "id", authorId);

medicalRecord = new MedicalRecord();
medicalRecord.setId(recordId);

comment = new Comment();
setPrivateField(comment, "id", commentId);
comment.setBody("Djuret är friskt och kan skrivas ut.");
comment.setAuthor(author);
comment.setMedicalRecord(medicalRecord);
callProtectedMethod(comment, "onCreate");
}

// --- Happy path ---

@Test
void from_shouldMapAllFieldsCorrectly() {
CommentResponse response = CommentResponse.from(comment);

assertThat(response.id()).isEqualTo(commentId);
assertThat(response.recordId()).isEqualTo(recordId);
assertThat(response.authorId()).isEqualTo(authorId);
assertThat(response.authorName()).isEqualTo("Dr. Sara Lindqvist");
assertThat(response.body()).isEqualTo("Djuret är friskt och kan skrivas ut.");
assertThat(response.createdAt()).isNotNull();
assertThat(response.updatedAt()).isNotNull();
}

@Test
void from_createdAtAndUpdatedAtShouldReflectCommentTimestamps() {
CommentResponse response = CommentResponse.from(comment);

assertThat(response.createdAt()).isEqualTo(comment.getCreatedAt());
assertThat(response.updatedAt()).isEqualTo(comment.getUpdatedAt());
}

@Test
void from_shouldMapAuthorNameFromUserEntity() {
CommentResponse response = CommentResponse.from(comment);

assertThat(response.authorName()).isEqualTo(author.getName());
}

// --- Sad paths ---

@Test
void from_shouldThrowWhenAuthorIsNull() {
comment.setAuthor(null);

assertThatThrownBy(() -> CommentResponse.from(comment))
.isInstanceOf(NullPointerException.class);
}

@Test
void from_shouldThrowWhenMedicalRecordIsNull() {
comment.setMedicalRecord(null);

assertThatThrownBy(() -> CommentResponse.from(comment))
.isInstanceOf(NullPointerException.class);
}

// --- Helpers ---

private static void setPrivateField(Object target, String fieldName, Object value) throws Exception {
Field field = target.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(target, value);
}

private static void callProtectedMethod(Object target, String methodName) throws Exception {
Method method = target.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
method.invoke(target);
}
}
Loading