diff --git a/pom.xml b/pom.xml index 374cc6cf..92752b0e 100644 --- a/pom.xml +++ b/pom.xml @@ -91,6 +91,11 @@ com.fasterxml.jackson.core jackson-databind + + com.h2database + h2 + test + diff --git a/src/main/java/org/example/vet1177/exception/GlobalExceptionHandler.java b/src/main/java/org/example/vet1177/exception/GlobalExceptionHandler.java index cf62f5c2..84751097 100644 --- a/src/main/java/org/example/vet1177/exception/GlobalExceptionHandler.java +++ b/src/main/java/org/example/vet1177/exception/GlobalExceptionHandler.java @@ -101,4 +101,16 @@ public ResponseEntity handleConstraintViolation(ConstraintViolati return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST); } + @ExceptionHandler(org.springframework.web.bind.MissingRequestHeaderException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleMissingHeader(org.springframework.web.bind.MissingRequestHeaderException ex) { + + log.warn("Missing header: {}", ex.getHeaderName()); + + return new ErrorResponse( + 400, + "Missing required header: " + ex.getHeaderName(), + null + ); + } } \ No newline at end of file diff --git a/src/test/java/org/example/vet1177/integration/TestDataFactory.java b/src/test/java/org/example/vet1177/integration/TestDataFactory.java new file mode 100644 index 00000000..7e822cff --- /dev/null +++ b/src/test/java/org/example/vet1177/integration/TestDataFactory.java @@ -0,0 +1,67 @@ +package org.example.vet1177.integration; + +import org.example.vet1177.entities.*; +import org.example.vet1177.repository.*; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.UUID; + +public class TestDataFactory { + + public static Clinic createClinic(ClinicRepository clinicRepository) { + Clinic clinic = new Clinic(); + clinic.setName("Test Clinic " + UUID.randomUUID()); + return clinicRepository.save(clinic); + } + + public static User createOwner(UserRepository userRepository, Clinic clinic) { + User owner = new User( + "Owner", + UUID.randomUUID() + "@test.com", + "password123", + Role.OWNER, + clinic + ); + return userRepository.save(owner); + } + + public static Pet createPet(PetRepository petRepository, User owner) { + Pet pet = new Pet( + owner, + "Doggo", + "Dog", + "Labrador", + LocalDate.of(2020, 1, 1), + new BigDecimal("20.5") + ); + return petRepository.save(pet); + } + + public static MedicalRecord createRecord( + MedicalRecordRepository medicalRecordRepository, + User owner, + Clinic clinic, + Pet pet + ) { + MedicalRecord record = new MedicalRecord(); + record.setTitle("Test Record"); + record.setOwner(owner); + record.setClinic(clinic); + record.setCreatedBy(owner); + record.setPet(pet); + + return medicalRecordRepository.save(record); + } + + public static ActivityLog createLog( + ActivityLogRepository repo, + User user, + MedicalRecord record, + ActivityType type, + String desc + ) { + ActivityLog log = new ActivityLog(type, desc, user, record); + return repo.save(log); + } +} diff --git a/src/test/java/org/example/vet1177/integration/activitylog/ActivityLogIntegrationTest.java b/src/test/java/org/example/vet1177/integration/activitylog/ActivityLogIntegrationTest.java new file mode 100644 index 00000000..385562fd --- /dev/null +++ b/src/test/java/org/example/vet1177/integration/activitylog/ActivityLogIntegrationTest.java @@ -0,0 +1,211 @@ +package org.example.vet1177.integration.activitylog; + +import org.checkerframework.checker.units.qual.C; +import org.example.vet1177.config.AwsS3Properties; +import org.example.vet1177.entities.*; +import org.example.vet1177.integration.TestDataFactory; +import org.example.vet1177.repository.*; +import org.example.vet1177.services.FileStorageService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.UUID; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; + +@SpringBootTest +@AutoConfigureMockMvc +@ActiveProfiles("test") +@TestPropertySource(properties = { + "spring.datasource.url=jdbc:h2:mem:testdb;MODE=PostgreSQL", + "spring.datasource.driver-class-name=org.h2.Driver" +}) +public class ActivityLogIntegrationTest { + + @Autowired + private ClinicRepository clinicRepository; + + @Autowired + private UserRepository userRepository; + + @Autowired + private MedicalRecordRepository medicalRecordRepository; + + @Autowired + private ActivityLogRepository activityLogRepository; + @Autowired + private MockMvc mockMvc; + @Autowired + private PetRepository petRepository; + + @MockitoBean + private FileStorageService fileStorageService; + + @MockitoBean + private AwsS3Properties awsS3Properties; + + +// @Test +// void should_return_logs_for_owner_only() throws Exception{ +// //Arrange +// //1. Skapa klinik +// Clinic clinic = new Clinic(); +// clinic.setName("Test Clinic"); +// clinic = clinicRepository.save(clinic); +// +// //2. Skapa owner user +// User owner = new User( +// "Owner Test", +// "owner@test.com", +// "password123", +// Role.OWNER, +// clinic +// ); +// owner = userRepository.save(owner); +// +// Pet pet = new Pet( +// owner, +// "Doggo", +// "Dog", +// "Labrador", +// LocalDate.of(2020, 1, 1), +// new BigDecimal("20.5") +// ); +// +// pet = petRepository.save(pet); +// +// +// // 3. Skapa medical record +// MedicalRecord record = new MedicalRecord(); +// record.setTitle("Test Record"); +// record.setOwner(owner); +// record.setClinic(clinic); +// record.setCreatedBy(owner); +// record.setPet(pet); +// record = medicalRecordRepository.save(record); +// +// // 4. skapa activity logs +// ActivityLog log1 = new ActivityLog( +// ActivityType.CASE_CREATED, +// "First log", +// owner, +// record +// ); +// +// ActivityLog log2 = new ActivityLog( +// ActivityType.UPDATED, +// "Second log", +// owner, +// record +// ); +// +// activityLogRepository.save(log1); +// activityLogRepository.save(log2); +// +// // Act och Assert +// mockMvc.perform(get("/api/activity-logs/record/" + record.getId()) +// .header("userId", owner.getId().toString())) +// .andExpect(status().isOk()) +// .andExpect(jsonPath("$.length()").value(2)); +// } + @Test + void should_return_logs_for_owner_only() throws Exception { + + // Arrange + Clinic clinic = TestDataFactory.createClinic(clinicRepository); + User owner = TestDataFactory.createOwner(userRepository, clinic); + Pet pet = TestDataFactory.createPet(petRepository, owner); + MedicalRecord record = TestDataFactory.createRecord( + medicalRecordRepository, owner, clinic, pet + ); + + TestDataFactory.createLog(activityLogRepository, owner, record, + ActivityType.CASE_CREATED, "First log"); + + TestDataFactory.createLog(activityLogRepository, owner, record, + ActivityType.UPDATED, "Second log"); + + // Act & Assert + mockMvc.perform(get("/api/activity-logs/record/" + record.getId()) + .header("userId", owner.getId().toString())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()").value(2)); + } + + @Test + void should_allow_vet_in_same_clinic_to_see_logs() throws Exception { + + Clinic clinic = TestDataFactory.createClinic(clinicRepository); + + User owner = TestDataFactory.createOwner(userRepository, clinic); + User vet = new User( + "Vet", + "vet@test.com", + "password", + Role.VET, + clinic + ); + vet = userRepository.save(vet); + + Pet pet = TestDataFactory.createPet(petRepository, owner); + MedicalRecord record = TestDataFactory.createRecord( + medicalRecordRepository, owner, clinic, pet + ); + + TestDataFactory.createLog(activityLogRepository, owner, record, + ActivityType.CASE_CREATED, "log"); + + mockMvc.perform(get("/api/activity-logs/record/" + record.getId()) + .header("userId", vet.getId().toString())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()").value(1)); + } + + @Test + void should_filter_out_logs_for_vet_in_other_clinic() throws Exception { + + Clinic clinicA = TestDataFactory.createClinic(clinicRepository); + Clinic clinicB = TestDataFactory.createClinic(clinicRepository); + + User owner = TestDataFactory.createOwner(userRepository, clinicA); + + User vetOtherClinic = new User( + "Vet", + "vet2@test.com", + "password", + Role.VET, + clinicB + ); + vetOtherClinic = userRepository.save(vetOtherClinic); + + Pet pet = TestDataFactory.createPet(petRepository, owner); + MedicalRecord record = TestDataFactory.createRecord( + medicalRecordRepository, owner, clinicA, pet + ); + + TestDataFactory.createLog(activityLogRepository, owner, record, + ActivityType.CASE_CREATED, "log"); + + mockMvc.perform(get("/api/activity-logs/record/" + record.getId()) + .header("userId", vetOtherClinic.getId().toString())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()").value(0)); + } + + @Test + void should_return_400_if_userId_missing() throws Exception { + + mockMvc.perform(get("/api/activity-logs/record/" + UUID.randomUUID())) + .andExpect(status().isBadRequest()); + } +} diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties index 9d05ac89..88e85bcc 100644 --- a/src/test/resources/application-test.properties +++ b/src/test/resources/application-test.properties @@ -4,15 +4,20 @@ # ============================================ # Separat testdatabas ? aldrig dev-databasen -spring.datasource.url=jdbc:postgresql://localhost:5432/vet1177_test -spring.datasource.username=vet1177_user -spring.datasource.password=vet1177_pass +spring.datasource.url=jdbc:h2:mem:testdb;MODE=PostgreSQL +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password= + +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect + +spring.datasource.hikari.driver-class-name=org.h2.Driver # Skapa schema från scratch och rensa efter tester spring.jpa.hibernate.ddl-auto=create-drop -# Kör schema.sql men INTE data.sql -spring.sql.init.mode=always +# Hibernate creates schema automatically (schema.sql is NOT used in tests) +spring.sql.init.mode=never spring.sql.init.data-locations= # Stäng av Docker Compose i tester @@ -20,7 +25,7 @@ spring.sql.init.data-locations= spring.docker.compose.lifecycle-management=none # MinIO ? testbucket -aws.s3.endpoint=http://localhost:9000 +aws.s3.endpoint= aws.s3.access-key=minioadmin aws.s3.secret-key=minioadmin aws.s3.bucket-name=vet1177-test-attachments