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