diff --git a/backend/src/test/java/org/raddatz/familienarchiv/audit/UserManagementAuditIntegrationTest.java b/backend/src/test/java/org/raddatz/familienarchiv/audit/UserManagementAuditIntegrationTest.java index 806b6ff6..f827d186 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/audit/UserManagementAuditIntegrationTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/audit/UserManagementAuditIntegrationTest.java @@ -3,8 +3,11 @@ package org.raddatz.familienarchiv.audit; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.raddatz.familienarchiv.PostgresContainerConfig; +import org.raddatz.familienarchiv.dto.AdminUpdateUserRequest; import org.raddatz.familienarchiv.dto.CreateUserRequest; +import org.raddatz.familienarchiv.dto.GroupDTO; import org.raddatz.familienarchiv.model.AppUser; +import org.raddatz.familienarchiv.model.UserGroup; import org.raddatz.familienarchiv.repository.AppUserRepository; import org.raddatz.familienarchiv.service.UserService; import org.springframework.beans.factory.annotation.Autowired; @@ -16,6 +19,7 @@ import org.springframework.transaction.support.TransactionTemplate; import software.amazon.awssdk.services.s3.S3Client; import java.util.List; +import java.util.Set; import java.util.UUID; import static java.util.concurrent.TimeUnit.SECONDS; @@ -76,4 +80,51 @@ class UserManagementAuditIntegrationTest { assertThat(events.get(0).getKind()).isEqualTo(AuditKind.USER_DELETED); assertThat(events.get(1).getKind()).isEqualTo(AuditKind.USER_CREATED); } + + @Test + void updateUserGroups_producesGroupMembershipChangedEvent() { + // Create groups before creating users — required for group assignment on creation + GroupDTO groupADto = new GroupDTO(); groupADto.setName("Viewers"); groupADto.setPermissions(Set.of("READ_ALL")); + GroupDTO groupBDto = new GroupDTO(); groupBDto.setName("Editors"); groupBDto.setPermissions(Set.of("WRITE_ALL")); + UserGroup gA = transactionTemplate.execute(status -> userService.createGroup(groupADto)); + UserGroup gB = transactionTemplate.execute(status -> userService.createGroup(groupBDto)); + + // Create actor (bootstrap — null actorId, event not relevant) + CreateUserRequest actorReq = new CreateUserRequest(); + actorReq.setEmail("actor-group-test@test.example.com"); + actorReq.setInitialPassword("secret"); + AppUser actor = transactionTemplate.execute(status -> userService.createUserOrUpdate(null, actorReq)); + await().atMost(5, SECONDS).until(() -> auditLogRepository.existsByKind(AuditKind.USER_CREATED)); + transactionTemplate.execute(status -> { auditLogRepository.deleteAll(); return null; }); + + // Create target user pre-assigned to gA + CreateUserRequest targetReq = new CreateUserRequest(); + targetReq.setEmail("target-group-test@test.example.com"); + targetReq.setInitialPassword("secret"); + targetReq.setGroupIds(List.of(gA.getId())); + transactionTemplate.execute(status -> userService.createUserOrUpdate(actor.getId(), targetReq)); + await().atMost(5, SECONDS).until(() -> auditLogRepository.existsByKind(AuditKind.USER_CREATED)); + transactionTemplate.execute(status -> { auditLogRepository.deleteAll(); return null; }); + + AppUser target = userRepository.findByEmail("target-group-test@test.example.com").orElseThrow(); + + // Change groups: Viewers → Editors + AdminUpdateUserRequest dto = new AdminUpdateUserRequest(); + dto.setGroupIds(List.of(gB.getId())); + transactionTemplate.execute(status -> userService.adminUpdateUser(actor.getId(), target.getId(), dto)); + + await().atMost(5, SECONDS).until(() -> auditLogRepository.existsByKind(AuditKind.GROUP_MEMBERSHIP_CHANGED)); + + List events = auditLogQueryService.findRecentUserManagementEvents(10); + assertThat(events).hasSize(1); + AuditLog event = events.get(0); + assertThat(event.getKind()).isEqualTo(AuditKind.GROUP_MEMBERSHIP_CHANGED); + assertThat(event.getPayload()).containsEntry("email", "target-group-test@test.example.com"); + @SuppressWarnings("unchecked") + List added = (List) event.getPayload().get("addedGroups"); + @SuppressWarnings("unchecked") + List removed = (List) event.getPayload().get("removedGroups"); + assertThat(added).containsExactlyInAnyOrder("Editors"); + assertThat(removed).containsExactlyInAnyOrder("Viewers"); + } }