diff --git a/pom.xml b/pom.xml index 083ac4b6..23354c8e 100644 --- a/pom.xml +++ b/pom.xml @@ -86,6 +86,8 @@ s3 2.20.0 + + @@ -94,7 +96,39 @@ org.springframework.boot spring-boot-maven-plugin + + org.jacoco + jacoco-maven-plugin + 0.8.14 + + + prepare-agent + + prepare-agent + + + + report + prepare-package + + report + + + + post-unit-test + test + + report + + + target/jacoco.exec + target/jacoco-ut + + + + + diff --git a/src/test/java/org/example/vet1177/dto/request/comment/CreateCommentRequestValidationTest.java b/src/test/java/org/example/vet1177/dto/request/comment/CreateCommentRequestValidationTest.java new file mode 100644 index 00000000..5e3bc2ca --- /dev/null +++ b/src/test/java/org/example/vet1177/dto/request/comment/CreateCommentRequestValidationTest.java @@ -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> violations = validator.validate(request); + + assertThat(violations).isEmpty(); + } + + @Test + void bodyWithExactly5000Characters_shouldProduceNoViolations() { + var request = new CreateCommentRequest(UUID.randomUUID(), "x".repeat(5000)); + + Set> violations = validator.validate(request); + + assertThat(violations).isEmpty(); + } + + // --- Sad paths: recordId --- + + @Test + void nullRecordId_shouldProduceViolationWithCorrectMessage() { + var request = new CreateCommentRequest(null, "En giltig kommentar."); + + Set> 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> 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> 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> 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> 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> violations = validator.validate(request); + + assertThat(violations).hasSize(2); + } +} diff --git a/src/test/java/org/example/vet1177/dto/request/comment/UpdateCommentRequestValidationTest.java b/src/test/java/org/example/vet1177/dto/request/comment/UpdateCommentRequestValidationTest.java new file mode 100644 index 00000000..6dea760c --- /dev/null +++ b/src/test/java/org/example/vet1177/dto/request/comment/UpdateCommentRequestValidationTest.java @@ -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> violations = validator.validate(request); + + assertThat(violations).isEmpty(); + } + + @Test + void bodyWithExactly5000Characters_shouldProduceNoViolations() { + var request = new UpdateCommentRequest("x".repeat(5000)); + + Set> violations = validator.validate(request); + + assertThat(violations).isEmpty(); + } + + // --- Sad paths: body --- + + @Test + void blankBody_shouldProduceViolationWithCorrectMessage() { + var request = new UpdateCommentRequest(" "); + + Set> 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> 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> 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> violations = validator.validate(request); + + assertThat(violations).hasSize(1); + assertThat(violations.iterator().next().getMessage()).isEqualTo("Kommentar får max vara 5000 tecken"); + } +} diff --git a/src/test/java/org/example/vet1177/dto/response/comment/CommentResponseTest.java b/src/test/java/org/example/vet1177/dto/response/comment/CommentResponseTest.java new file mode 100644 index 00000000..235aa20b --- /dev/null +++ b/src/test/java/org/example/vet1177/dto/response/comment/CommentResponseTest.java @@ -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); + } +} diff --git a/src/test/java/org/example/vet1177/entities/CommentEntityTest.java b/src/test/java/org/example/vet1177/entities/CommentEntityTest.java new file mode 100644 index 00000000..603f7ea8 --- /dev/null +++ b/src/test/java/org/example/vet1177/entities/CommentEntityTest.java @@ -0,0 +1,111 @@ +package org.example.vet1177.entities; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.time.Instant; + +import static org.assertj.core.api.Assertions.assertThat; + +class CommentEntityTest { + + private Comment comment; + private User author; + private MedicalRecord medicalRecord; + + @BeforeEach + void setUp() { + comment = new Comment(); + author = new User("Dr. Elin Svensson", "elin@vet.se", "hash123", Role.VET); + medicalRecord = new MedicalRecord(); + } + + // --- Getters & setters --- + + @Test + void setBody_shouldStoreAndReturnCorrectValue() { + comment.setBody("Patienten visar tecken på förbättring."); + + assertThat(comment.getBody()).isEqualTo("Patienten visar tecken på förbättring."); + } + + @Test + void setAuthor_shouldStoreAndReturnCorrectUser() { + comment.setAuthor(author); + + assertThat(comment.getAuthor()).isSameAs(author); + } + + @Test + void setMedicalRecord_shouldStoreAndReturnCorrectRecord() { + comment.setMedicalRecord(medicalRecord); + + assertThat(comment.getMedicalRecord()).isSameAs(medicalRecord); + } + + // --- Lifecycle: onCreate --- + + @Test + void onCreate_shouldSetBothTimestampsToNonNull() { + comment.onCreate(); + + assertThat(comment.getCreatedAt()).isNotNull(); + assertThat(comment.getUpdatedAt()).isNotNull(); + } + + + // --- Lifecycle: onUpdate --- + + @Test + void onUpdate_shouldRefreshUpdatedAtWithoutModifyingCreatedAt() { + comment.onCreate(); + Instant createdAt = comment.getCreatedAt(); + + comment.onUpdate(); + + assertThat(comment.getUpdatedAt()).isNotNull(); + assertThat(comment.getCreatedAt()).isEqualTo(createdAt); + } + + @Test + void onUpdate_shouldNotModifyCreatedAt() { + comment.onCreate(); + Instant originalCreatedAt = comment.getCreatedAt(); + + comment.onUpdate(); + + assertThat(comment.getCreatedAt()).isEqualTo(originalCreatedAt); + } + + // --- Sad paths: standardvärden innan persist --- + + @Test + void getId_shouldBeNullBeforePersist() { + assertThat(comment.getId()).isNull(); + } + + @Test + void getBody_shouldBeNullWhenNotSet() { + assertThat(comment.getBody()).isNull(); + } + + @Test + void getAuthor_shouldBeNullWhenNotSet() { + assertThat(comment.getAuthor()).isNull(); + } + + @Test + void getMedicalRecord_shouldBeNullWhenNotSet() { + assertThat(comment.getMedicalRecord()).isNull(); + } + + @Test + void getCreatedAt_shouldBeNullBeforeOnCreate() { + assertThat(comment.getCreatedAt()).isNull(); + } + + @Test + void getUpdatedAt_shouldBeNullBeforeOnCreate() { + assertThat(comment.getUpdatedAt()).isNull(); + } +}