diff --git a/pom.xml b/pom.xml index ccb8de3bfd4dcbb34e522ade41b81e7bbe5125cd..915ccea3e9f6dfbca7d73078bb0a325dc625d97e 100644 --- a/pom.xml +++ b/pom.xml @@ -89,10 +89,6 @@ <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> - <dependency> - <groupId>net.devh</groupId> - <artifactId>grpc-client-spring-boot-starter</artifactId> - </dependency> <!-- ShedLock --> <dependency> diff --git a/src/main/java/de/ozgcloud/admin/keycloak/Group.java b/src/main/java/de/ozgcloud/admin/keycloak/Group.java index 0bf5a9e19b39f24637d9cdd8ef037f6110247609..005a7ce23d0709ade874e9fad35e7be17906ceee 100644 --- a/src/main/java/de/ozgcloud/admin/keycloak/Group.java +++ b/src/main/java/de/ozgcloud/admin/keycloak/Group.java @@ -7,7 +7,7 @@ import lombok.Getter; import lombok.Singular; @Getter -@Builder +@Builder(toBuilder = true) public class Group { private String id; diff --git a/src/main/java/de/ozgcloud/admin/organisationseinheit/SyncService.java b/src/main/java/de/ozgcloud/admin/organisationseinheit/SyncService.java index 80f607a3b81ae1f6aaa283536cebd6f81cee8a6a..2b3949a5436739f9fe8633a0373a803d060eb06d 100644 --- a/src/main/java/de/ozgcloud/admin/organisationseinheit/SyncService.java +++ b/src/main/java/de/ozgcloud/admin/organisationseinheit/SyncService.java @@ -24,14 +24,14 @@ class SyncService { private final OrganisationsEinheitMapper organisationsEinheitMapper; public void syncOrganisationsEinheitenFromKeycloak(long syncTimestamp) { - keycloakRemoteService.getGroupsWithOrganisationsEinheitId().forEach(group -> syncGroups(group, null, syncTimestamp)); + keycloakRemoteService.getGroupsWithOrganisationsEinheitId().forEach(group -> syncGroupsWithSubGroups(group, null, syncTimestamp)); repository.setUnsyncedAsDeleted(syncTimestamp); } - void syncGroups(Group group, OrganisationsEinheit parent, long syncTimestamp) { + void syncGroupsWithSubGroups(Group group, OrganisationsEinheit parent, long syncTimestamp) { var synced = syncGroup(group, parent, syncTimestamp); var saved = saveSyncedOrganisationsEinheit(synced); - group.getSubGroups().forEach(subGroup -> syncGroups(subGroup, saved, syncTimestamp)); + group.getSubGroups().forEach(subGroup -> syncGroupsWithSubGroups(subGroup, saved, syncTimestamp)); } OrganisationsEinheit syncGroup(Group group, OrganisationsEinheit parent, long syncTimestamp) { @@ -90,7 +90,8 @@ class SyncService { } public void syncAddedOrganisationsEinheiten(long syncTimestamp) { - sortInAdditionOrder(repository.findAllWithoutSyncResult()).forEach(organisationsEinheit -> syncAddedOrganisationsEinheit(syncTimestamp, organisationsEinheit)); + sortInAdditionOrder(repository.findAllWithoutSyncResult()).forEach( + organisationsEinheit -> syncAddedOrganisationsEinheit(syncTimestamp, organisationsEinheit)); } Stream<OrganisationsEinheit> sortInAdditionOrder(Stream<OrganisationsEinheit> organisationsEinheiten) { @@ -115,23 +116,30 @@ class SyncService { } void syncAddedOrganisationsEinheit(long syncTimestamp, OrganisationsEinheit organisationsEinheit) { - var addGroupData = organisationsEinheitMapper.toAddGroupData(organisationsEinheit, findParentKeycloakId(organisationsEinheit)); - var keycloakId = addGroupInKeycloak(addGroupData); - if (keycloakId.isPresent()) { - var updatedOrganisationsEinheit = organisationsEinheit.toBuilder() - .keycloakId(keycloakId.get()) - .syncResult(SyncResult.OK) - .lastSyncTimestamp(syncTimestamp) - .build(); - repository.save(updatedOrganisationsEinheit); + var parent = findParent(organisationsEinheit); + if (parent.isEmpty() || parent.get().getKeycloakId() != null) { + var parentKeycloakId = parent.map(OrganisationsEinheit::getKeycloakId).orElse(null); + addAsGroupInKeycloak(organisationsEinheit, parentKeycloakId).ifPresent( + addedGroupId -> updateAfterSuccessfulGroupCreation(organisationsEinheit, syncTimestamp, addedGroupId)); } } - String findParentKeycloakId(OrganisationsEinheit organisationsEinheit) { - if (organisationsEinheit.getParentId() == null) { - return null; - } - return repository.findById(organisationsEinheit.getParentId()).map(OrganisationsEinheit::getKeycloakId).orElse(null); + Optional<OrganisationsEinheit> findParent(OrganisationsEinheit organisationsEinheit) { + return Optional.ofNullable(organisationsEinheit.getParentId()).flatMap(repository::findById); + } + + Optional<String> addAsGroupInKeycloak(OrganisationsEinheit organisationsEinheit, String parentKeycloakId) { + var addGroupData = organisationsEinheitMapper.toAddGroupData(organisationsEinheit, parentKeycloakId); + return addGroupInKeycloak(addGroupData); + } + + void updateAfterSuccessfulGroupCreation(OrganisationsEinheit organisationsEinheit, long syncTimestamp, String keycloakId) { + var updatedOrganisationsEinheit = organisationsEinheit.toBuilder() + .keycloakId(keycloakId) + .syncResult(SyncResult.OK) + .lastSyncTimestamp(syncTimestamp) + .build(); + repository.save(updatedOrganisationsEinheit); } Optional<String> addGroupInKeycloak(AddGroupData addGroupData) { diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitControllerTest.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitControllerTest.java index 06f4ce43a415aaa113d9505bcf686942a9a8be30..fbf00adebbb5aeb5e7b8c5edd6f2a6ce4d76f09f 100644 --- a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitControllerTest.java +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitControllerTest.java @@ -96,10 +96,12 @@ class OrganisationsEinheitControllerTest { class TestGetById { private final OrganisationsEinheit organisationsEinheit = OrganisationsEinheitTestFactory.create(); + private final EntityModel<OrganisationsEinheit> entityModel = EntityModel.of(organisationsEinheit); @BeforeEach void setUp() { when(organisationsEinheitService.getOrganisationsEinheitById(OrganisationsEinheitTestFactory.ID)).thenReturn(organisationsEinheit); + when(assembler.toModel(organisationsEinheit)).thenReturn(entityModel); } @Test @@ -122,6 +124,13 @@ class OrganisationsEinheitControllerTest { doRequest().andExpect(status().isOk()); } + @Test + void shouldReturnEntityModel() { + var response = controller.getById(OrganisationsEinheitTestFactory.ID); + + assertThat(response).isEqualTo(entityModel); + } + @SneakyThrows private ResultActions doRequest() { return mockMvc.perform(get(OrganisationsEinheitController.PATH + "/" + OrganisationsEinheitTestFactory.ID)); @@ -131,11 +140,15 @@ class OrganisationsEinheitControllerTest { @Nested class TestGetChildren { - private final List<OrganisationsEinheit> children = List.of(OrganisationsEinheitTestFactory.create()); + private final OrganisationsEinheit organisationsEinheit = OrganisationsEinheitTestFactory.create(); + private final List<OrganisationsEinheit> children = List.of(organisationsEinheit); + private final CollectionModel<EntityModel<OrganisationsEinheit>> collectionModel = CollectionModel.of( + List.of(EntityModel.of(organisationsEinheit))); @BeforeEach void setUp() { when(organisationsEinheitService.getChildren(OrganisationsEinheitTestFactory.ID)).thenReturn(children); + when(assembler.toChildrenCollectionModel(OrganisationsEinheitTestFactory.ID, children)).thenReturn(collectionModel); } @Test @@ -158,6 +171,13 @@ class OrganisationsEinheitControllerTest { verify(assembler).toChildrenCollectionModel(OrganisationsEinheitTestFactory.ID, children); } + @Test + void shouldReturnCollectionModel() { + var response = controller.getChildren(OrganisationsEinheitTestFactory.ID); + + assertThat(response).isEqualTo(collectionModel); + } + @SneakyThrows private ResultActions doRequest() { return mockMvc.perform(get(OrganisationsEinheitController.PATH + "/" + OrganisationsEinheitTestFactory.ID + "/children")); diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitMapperTest.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitMapperTest.java index 3298e01121a50ad9858bec40e0828472e894e4a2..9192df6b2e1ac6545099bec61c84006758dadde3 100644 --- a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitMapperTest.java +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitMapperTest.java @@ -3,6 +3,7 @@ package de.ozgcloud.admin.organisationseinheit; import static org.assertj.core.api.Assertions.*; import java.util.Collections; +import java.util.Objects; import java.util.UUID; import org.junit.jupiter.api.Nested; @@ -22,23 +23,23 @@ class OrganisationsEinheitMapperTest { void shouldMap() { var mapped = mapper.fromGrpc(GrpcOrganisationsEinheitTestFactory.create()); - assertThat(mapped).extracting( - OrganisationsEinheit::getName, - OrganisationsEinheit::getOrganisationsEinheitId, - OrganisationsEinheit::getZufiId, - OrganisationsEinheit::getParentId, - OrganisationsEinheit::getSyncResult, - OrganisationsEinheit::getSettings, - OrganisationsEinheit::getChildren - ).containsExactly( - OrganisationsEinheitTestFactory.NAME, - OrganisationsEinheitTestFactory.ORGANISATIONS_EINHEIT_ID, - OrganisationsEinheitTestFactory.ZUFI_ID, - null, - null, - null, - Collections.emptyList() - ); + assertThat(mapped) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getChildren + ).containsExactly( + OrganisationsEinheitTestFactory.NAME, + OrganisationsEinheitTestFactory.ORGANISATIONS_EINHEIT_ID, + OrganisationsEinheitTestFactory.ZUFI_ID, + null, + null, + Collections.emptyList() + ); } } diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitModelAssemblerTest.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitModelAssemblerTest.java index d22dfcc1cec283ada2166c4402f1fd20fa711b0f..51c34967555f7f69218f28da26d4e48fd1a8701e 100644 --- a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitModelAssemblerTest.java +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitModelAssemblerTest.java @@ -11,6 +11,8 @@ import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Spy; import org.springframework.hateoas.CollectionModel; @@ -82,6 +84,10 @@ class OrganisationsEinheitModelAssemblerTest { private CollectionModel<EntityModel<OrganisationsEinheit>> childrenCollectionModel; @Spy private final HalModelBuilder halModelBuilder = HalModelBuilder.emptyHalModel(); + @Captor + private ArgumentCaptor<LinkRelation> linkRelationArgumentCaptor; + @Captor + private ArgumentCaptor<Collection<EntityModel<OrganisationsEinheit>>> collectionModelContentArgumentCaptor; @BeforeEach void setUp() { @@ -89,8 +95,7 @@ class OrganisationsEinheitModelAssemblerTest { childOrganisationsEinheit = OrganisationsEinheitTestFactory.createBuilder().id(UUID.randomUUID().toString()) .parentId(parentOrganisationsEinheit.getId()).build(); parentOrganisationsEinheit = parentOrganisationsEinheit.toBuilder().child(childOrganisationsEinheit).build(); - childrenCollectionModel = CollectionModel.of( - List.of(EntityModel.of(childOrganisationsEinheit))); + childrenCollectionModel = CollectionModel.of(List.of(EntityModel.of(childOrganisationsEinheit))); doReturn(childrenCollectionModel).when(assembler) .toChildrenCollectionModel(parentOrganisationsEinheit.getId(), parentOrganisationsEinheit.getChildren()); } @@ -106,7 +111,9 @@ class OrganisationsEinheitModelAssemblerTest { void shouldEmbedChildrenModels() { assembler.embedChildOrganisationsEinheiten(parentOrganisationsEinheit, halModelBuilder); - verify(halModelBuilder).embed(any(Collection.class), any(LinkRelation.class)); + verify(halModelBuilder).embed(collectionModelContentArgumentCaptor.capture(), linkRelationArgumentCaptor.capture()); + assertThat(collectionModelContentArgumentCaptor.getValue()).usingRecursiveComparison().isEqualTo(childrenCollectionModel.getContent()); + assertThat(linkRelationArgumentCaptor.getValue().toString()).isEqualTo(REL_CHILD_ORGANISATIONS_EINHEITEN); } @Test diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRepositoryITCase.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRepositoryITCase.java index ced0b42ccd586c5b3588ed5cb4927f805c243a25..dcfeebde1d6aa5bfb83944ec11c110f72dd833a8 100644 --- a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRepositoryITCase.java +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRepositoryITCase.java @@ -111,7 +111,7 @@ class OrganisationsEinheitRepositoryITCase { } @Test - void shouldReturnEmptyOnNotFoundSyncResultNull() { + void shouldReturnEmptyOnSyncResultNull() { operations.save(OrganisationsEinheitTestFactory.createBuilder().id(null).syncResult(null).build()); var organisationsEinheit = repository.findSyncedByKeycloakId(OrganisationsEinheitTestFactory.KEYCLOAK_ID); @@ -145,12 +145,12 @@ class OrganisationsEinheitRepositoryITCase { private final OrganisationsEinheit deleted1 = OrganisationsEinheitTestFactory.createBuilder() .id(null) .organisationsEinheitId(DELETED_ORGANISATIONS_EINHEIT_ID_1) - .lastSyncTimestamp(Instant.now().minusSeconds(300).toEpochMilli()) + .lastSyncTimestamp(OrganisationsEinheitTestFactory.LAST_SYNC_UPDATE - 1) .build(); private final OrganisationsEinheit deleted2 = OrganisationsEinheitTestFactory.createBuilder() .id(null) .organisationsEinheitId(DELETED_ORGANISATIONS_EINHEIT_ID_2) - .lastSyncTimestamp(Instant.now().minusSeconds(30000).toEpochMilli()) + .lastSyncTimestamp(OrganisationsEinheitTestFactory.LAST_SYNC_UPDATE - 2) .build(); private final OrganisationsEinheit synced1 = OrganisationsEinheitTestFactory.createBuilder() .id(null) @@ -235,7 +235,8 @@ class OrganisationsEinheitRepositoryITCase { void shouldFindAllWithoutSyncResult() { var allWithoutSyncResult = repository.findAllWithoutSyncResult(); - assertThat(allWithoutSyncResult).extracting(OrganisationsEinheit::getId).containsExactlyInAnyOrder(WITHOUT_SYNC_RESULT_ID_1, WITHOUT_SYNC_RESULT_ID_2); + assertThat(allWithoutSyncResult).extracting(OrganisationsEinheit::getId) + .containsExactlyInAnyOrder(WITHOUT_SYNC_RESULT_ID_1, WITHOUT_SYNC_RESULT_ID_2); } } diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitServiceTest.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitServiceTest.java index 822efd5c057a8b196deac785e1563848070d67c8..aaf92457a1ca0d94efb929733852e1dbf022b03b 100644 --- a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitServiceTest.java +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitServiceTest.java @@ -139,12 +139,26 @@ class OrganisationsEinheitServiceTest { @Nested class TestGetChildren { + private final OrganisationsEinheit organisationsEinheit = OrganisationsEinheitTestFactory.create(); + + @BeforeEach + void setUp() { + when(repository.findChildren(OrganisationsEinheitTestFactory.ID)).thenReturn(List.of(organisationsEinheit)); + } + @Test void shouldCallRepository() { service.getChildren(OrganisationsEinheitTestFactory.ID); verify(repository).findChildren(OrganisationsEinheitTestFactory.ID); } + + @Test + void shouldReturnChildren() { + var children = service.getChildren(OrganisationsEinheitTestFactory.ID); + + assertThat(children).containsExactly(organisationsEinheit); + } } @Nested diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitSettingsTestFactory.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitSettingsTestFactory.java index ababf88ba11eb1378cc4416c357098c48abd8bc7..c5028fb0e25bb8b6127e623a4e09b75794545d61 100644 --- a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitSettingsTestFactory.java +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitSettingsTestFactory.java @@ -15,4 +15,9 @@ public class OrganisationsEinheitSettingsTestFactory { .signatur(SIGNATUR); } + public static OrganisationsEinheitSettings.OrganisationsEinheitSettingsBuilder createNewBuilder() { + return OrganisationsEinheitSettings.builder() + .signatur(LoremIpsum.getInstance().getWords(3)); + } + } diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitTestFactory.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitTestFactory.java index 2feb3311fb010748e58beed1f48b9a132c24d015..756ec38fc6a01660b352924cebbd24176c4a9939 100644 --- a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitTestFactory.java +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitTestFactory.java @@ -34,4 +34,16 @@ public class OrganisationsEinheitTestFactory { .lastSyncTimestamp(LAST_SYNC_UPDATE); } + public static OrganisationsEinheit.OrganisationsEinheitBuilder createNewBuilder() { + return OrganisationsEinheit.builder() + .id(UUID.randomUUID().toString()) + .keycloakId(UUID.randomUUID().toString()) + .name(LoremIpsum.getInstance().getName()) + .organisationsEinheitId(UUID.randomUUID().toString()) + .parentId(UUID.randomUUID().toString()) + .zufiId(UUID.randomUUID().toString()) + .syncResult(SyncResult.DELETED) + .settings(OrganisationsEinheitSettingsTestFactory.createNewBuilder().build()) + .lastSyncTimestamp(LAST_SYNC_UPDATE - 10000); + } } diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/SyncAddedOrganisationsEinheitenITCase.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/SyncAddedOrganisationsEinheitenITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..ca1b0366500f5ea25e29cef7ebdaee5deff22484 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/SyncAddedOrganisationsEinheitenITCase.java @@ -0,0 +1,128 @@ +package de.ozgcloud.admin.organisationseinheit; + +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.groups.Tuple.tuple; +import static org.mockito.Mockito.*; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.test.context.ContextConfiguration; + +import de.ozgcloud.admin.common.KeycloakInitializer; +import de.ozgcloud.admin.keycloak.Group; +import de.ozgcloud.admin.keycloak.KeycloakRemoteService; +import de.ozgcloud.admin.keycloak.ResourceCreationException; +import de.ozgcloud.common.test.DbInitializer; +import de.ozgcloud.common.test.ITCase; + +@ITCase +@ContextConfiguration(initializers = { DbInitializer.class, KeycloakInitializer.class }) +class SyncAddedOrganisationsEinheitenITCase { + + @Autowired + private SyncService service; + @SpyBean + private KeycloakRemoteService keycloakRemoteService; + @Autowired + private MongoOperations operations; + + private final long syncTimestamp = Instant.now().toEpochMilli(); + + @BeforeEach + void clearDatabase() { + operations.dropCollection(OrganisationsEinheit.class); + } + + @Test + void shouldSynchronizeAddedTopLevelGroup() { + var topLevel = topLevel("shouldSynchronizeAddedTopLevelGroup"); + operations.save(topLevel); + + service.syncAddedOrganisationsEinheiten(syncTimestamp); + + assertThat(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()) + .extracting(Group::getName, Group::getOrganisationsEinheitId, Group::getSubGroups) + .contains(tuple(topLevel.getName(), topLevel.getOrganisationsEinheitId(), List.of())); + } + + @Test + void shouldSynchronizeAddedTopLevelGroupWithChild() { + var topLevel = topLevel("shouldSynchronizeAddedTopLevelGroupWithChild"); + var childLevel1 = childLevel("shouldSynchronizeAddedTopLevelGroupWithChild", topLevel); + operations.save(topLevel); + operations.save(childLevel1); + + service.syncAddedOrganisationsEinheiten(syncTimestamp); + + assertThat(findGroupInKeycloak(topLevel.getName())).isPresent().get() + .extracting(Group::getSubGroups).asList().extracting("name") + .containsExactly(childLevel1.getName()); + } + + @Test + void shouldSynchronizeChildAddedToAlreadySynchronizedParent() { + var parent = topLevel("shouldSynchronizeChildAddedToAlreadySynchronizedParent"); + syncOrganisationsEinheitToKeycloak(parent); + var childLevel1 = childLevel("shouldSynchronizeChildAddedToAlreadySynchronizedParent", parent); + operations.save(childLevel1); + + service.syncAddedOrganisationsEinheiten(syncTimestamp); + + assertThat(findGroupInKeycloak(parent.getName())).isPresent().get() + .extracting(Group::getSubGroups).asList().extracting("name") + .containsExactly(childLevel1.getName()); + } + + @Test + void shouldNotAddChildIfParentWasNotSynchronized() { + var topLevel = topLevel("shouldNotAddChildIfParentWasNotSynchronized"); + var childLevel1 = childLevel("shouldNotAddChildIfParentWasNotSynchronized", topLevel); + operations.save(topLevel); + operations.save(childLevel1); + doThrow(new ResourceCreationException("OMG!")).when(keycloakRemoteService) + .addGroup(argThat(addGroupData -> addGroupData.getName().equals(topLevel.getName()))); + + service.syncAddedOrganisationsEinheiten(syncTimestamp); + + assertThat(findGroupInKeycloak(childLevel1.getName())).isEmpty(); + } + + private static OrganisationsEinheit topLevel(String nameSuffix) { + return OrganisationsEinheitTestFactory.createBuilder() + .id(UUID.randomUUID().toString()) + .parentId(null) + .keycloakId(null) + .syncResult(null) + .name("topLevel (%s)".formatted(nameSuffix)) + .build(); + } + + private static OrganisationsEinheit childLevel(String nameSuffix, OrganisationsEinheit parent) { + return OrganisationsEinheitTestFactory.createBuilder() + .id(UUID.randomUUID().toString()) + .parentId(parent.getId()) + .keycloakId(null) + .syncResult(null) + .name("childLevel1 (%s)".formatted(nameSuffix)) + .build(); + } + + private Optional<Group> findGroupInKeycloak(String groupName) { + return keycloakRemoteService.getGroupsWithOrganisationsEinheitId() + .filter(group -> groupName.equals(group.getName())) + .findFirst(); + } + + private void syncOrganisationsEinheitToKeycloak(OrganisationsEinheit organisationsEinheit) { + operations.save(organisationsEinheit); + service.syncAddedOrganisationsEinheiten(syncTimestamp); + } +} diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/SyncServiceITCase.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/SyncServiceITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..c681785b71ea74380ac446ca736527d131579284 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/SyncServiceITCase.java @@ -0,0 +1,630 @@ +package de.ozgcloud.admin.organisationseinheit; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.Instant; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.mongodb.core.MongoOperations; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.admin.keycloak.GroupTestFactory; +import de.ozgcloud.admin.keycloak.KeycloakRemoteService; +import de.ozgcloud.common.test.DataITCase; + +@DataITCase +@EnableAutoConfiguration +class SyncServiceITCase { + + @Autowired + private MongoOperations operations; + + @Autowired + private SyncService service; + + @Autowired + private OrganisationsEinheitRepository repository; + + @MockBean + private KeycloakRemoteService keycloakRemoteService; + + @MockBean + private OrganisationsEinheitRemoteService organisationsEinheitRemoteService; + + @BeforeEach + void clearDatabase() { + operations.dropCollection(OrganisationsEinheit.class); + } + + @DisplayName("Organisationseinheit not found in PVOG") + @Test + void shouldSyncNotFoundInPvog() { + var syncTimestamp = Instant.now().toEpochMilli(); + var group = GroupTestFactory.createBuilder().clearSubGroups().build(); + when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn(Stream.of(group)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.ORGANISATIONS_EINHEIT_ID)).thenReturn(List.of()); + + service.syncOrganisationsEinheitenFromKeycloak(syncTimestamp); + + var synced = repository.findAll(); + + assertThat(synced). + hasSize(1) + .first() + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + group.getId(), + group.getName(), + group.getOrganisationsEinheitId(), + SyncResult.NOT_FOUND_IN_PVOG, + null, + null, + Collections.emptyList(), + syncTimestamp); + } + + @DisplayName("Organisationseinheit found in PVOG but group name is different") + @Test + void shouldSyncNameMismatch() { + var syncTimestamp = Instant.now().toEpochMilli(); + var group = GroupTestFactory.createBuilder().clearSubGroups().build(); + var pvogOranigastionsEinheit = OrganisationsEinheitTestFactory.createBuilder().organisationsEinheitId(group.getOrganisationsEinheitId()) + .build(); + when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn(Stream.of(group)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.ORGANISATIONS_EINHEIT_ID)).thenReturn( + List.of(pvogOranigastionsEinheit)); + + service.syncOrganisationsEinheitenFromKeycloak(syncTimestamp); + + var synced = repository.findAll(); + + assertThat(synced). + hasSize(1) + .first() + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + group.getId(), + pvogOranigastionsEinheit.getName(), + group.getOrganisationsEinheitId(), + SyncResult.NAME_MISMATCH, + pvogOranigastionsEinheit.getId(), + null, + Collections.emptyList(), + syncTimestamp); + } + + @DisplayName("Multiple Organisationseinheiten for same group found in PVOG") + @Test + void shouldSyncOrganisationsEinheitNotUnique() { + var syncTimestamp = Instant.now().toEpochMilli(); + var group = GroupTestFactory.createBuilder().clearSubGroups().build(); + var pvogOranigastionsEinheit1 = OrganisationsEinheitTestFactory.createBuilder().organisationsEinheitId(group.getOrganisationsEinheitId()) + .build(); + var pvogOranigastionsEinheit2 = OrganisationsEinheitTestFactory.createBuilder().organisationsEinheitId(group.getOrganisationsEinheitId()) + .build(); + when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn(Stream.of(group)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.ORGANISATIONS_EINHEIT_ID)).thenReturn( + List.of(pvogOranigastionsEinheit1, pvogOranigastionsEinheit2)); + + service.syncOrganisationsEinheitenFromKeycloak(syncTimestamp); + + var synced = repository.findAll(); + + assertThat(synced). + hasSize(1) + .first() + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + group.getId(), + group.getName(), + group.getOrganisationsEinheitId(), + SyncResult.ORGANISATIONSEINHEIT_ID_NOT_UNIQUE, + null, + null, + Collections.emptyList(), + syncTimestamp); + } + + @DisplayName("Organisationseinheit found in PVOG and have same data") + @Test + void shouldSyncOk() { + var syncTimestamp = Instant.now().toEpochMilli(); + var group = GroupTestFactory.createBuilder().clearSubGroups().build(); + var pvogOranigastionsEinheit = OrganisationsEinheitTestFactory.createBuilder().organisationsEinheitId(group.getOrganisationsEinheitId()) + .name(group.getName()).build(); + when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn(Stream.of(group)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.ORGANISATIONS_EINHEIT_ID)).thenReturn( + List.of(pvogOranigastionsEinheit)); + + service.syncOrganisationsEinheitenFromKeycloak(syncTimestamp); + + var synced = repository.findAll(); + + assertThat(synced). + hasSize(1) + .first() + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + group.getId(), + group.getName(), + group.getOrganisationsEinheitId(), + SyncResult.OK, + pvogOranigastionsEinheit.getId(), + null, + Collections.emptyList(), + syncTimestamp); + } + + @DisplayName("OrganisationseinheitId attribute of group changed") + @Test + void shouldSyncUpdateAttribute() { + var syncTimestamp = Instant.now().toEpochMilli(); + var group = GroupTestFactory.createBuilder().clearSubGroups().build(); + var pvogOranigastionsEinheit = OrganisationsEinheitTestFactory.createBuilder().organisationsEinheitId(group.getOrganisationsEinheitId()) + .name(group.getName()).build(); + var newOrganisationsEinheitId = UUID.randomUUID().toString(); + when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn(Stream.of(group)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.ORGANISATIONS_EINHEIT_ID)).thenReturn( + List.of(pvogOranigastionsEinheit)); + + service.syncOrganisationsEinheitenFromKeycloak(syncTimestamp); + when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn( + Stream.of(group.toBuilder().organisationsEinheitId(newOrganisationsEinheitId).build())); + service.syncOrganisationsEinheitenFromKeycloak(syncTimestamp + 1000); + + var synced = repository.findAll(); + + assertThat(synced). + hasSize(1) + .first() + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + group.getId(), + group.getName(), + newOrganisationsEinheitId, + SyncResult.NOT_FOUND_IN_PVOG, + null, + null, + Collections.emptyList(), + syncTimestamp + 1000); + } + + @DisplayName("Group deleted in Keycloak") + @Test + void shouldSyncDeleted() { + var syncTimestamp = Instant.now().toEpochMilli(); + var group = GroupTestFactory.createBuilder().clearSubGroups().build(); + var pvogOranigastionsEinheit = OrganisationsEinheitTestFactory.createBuilder().organisationsEinheitId(group.getOrganisationsEinheitId()) + .name(group.getName()).build(); + when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn(Stream.of(group)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.ORGANISATIONS_EINHEIT_ID)).thenReturn( + List.of(pvogOranigastionsEinheit)); + + service.syncOrganisationsEinheitenFromKeycloak(syncTimestamp); + when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn(Stream.of()); + service.syncOrganisationsEinheitenFromKeycloak(syncTimestamp + 1000); + + var synced = repository.findAll(); + + assertThat(synced). + hasSize(1) + .first() + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + group.getId(), + group.getName(), + group.getOrganisationsEinheitId(), + SyncResult.DELETED, + pvogOranigastionsEinheit.getId(), + null, + Collections.emptyList(), + syncTimestamp); + } + + @DisplayName("Organisationseinheit for parent group and sub group not found in PVOG") + @Test + void shouldSyncParentNotFoundInPvogSubGroupNotFoundInPvog() { + var syncTimestamp = Instant.now().toEpochMilli(); + var group = GroupTestFactory.createBuilder().build(); + var subGroup = group.getSubGroups().getFirst(); + when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn(Stream.of(group)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.ORGANISATIONS_EINHEIT_ID)).thenReturn(List.of()); + + service.syncOrganisationsEinheitenFromKeycloak(syncTimestamp); + + var parentSynced = repository.findSyncedByKeycloakId(group.getId()).get(); + var childSynced = repository.findSyncedByKeycloakId(subGroup.getId()).get(); + + assertThat(parentSynced) + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + group.getId(), + group.getName(), + group.getOrganisationsEinheitId(), + SyncResult.NOT_FOUND_IN_PVOG, + null, + null, + Collections.emptyList(), + syncTimestamp); + assertThat(childSynced) + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + subGroup.getId(), + subGroup.getName(), + subGroup.getOrganisationsEinheitId(), + SyncResult.NOT_FOUND_IN_PVOG, + null, + parentSynced.getId(), + Collections.emptyList(), + syncTimestamp); + } + + @DisplayName("Organisationseinheit for parent group and sub group found in PVOG but have different names") + @Test + void shouldSyncParentNameMismatchSubGroupNameMismatch() { + var syncTimestamp = Instant.now().toEpochMilli(); + var group = GroupTestFactory.createBuilder().build(); + var subGroup = group.getSubGroups().getFirst(); + var pvogOranigastionsEinheit = OrganisationsEinheitTestFactory.createBuilder().organisationsEinheitId(group.getOrganisationsEinheitId()) + .build(); + var pvogChildOranigastionsEinheit = OrganisationsEinheitTestFactory.createBuilder() + .organisationsEinheitId(subGroup.getOrganisationsEinheitId()).name( + LoremIpsum.getInstance().getWords(2)) + .build(); + when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn(Stream.of(group)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.ORGANISATIONS_EINHEIT_ID)).thenReturn( + List.of(pvogOranigastionsEinheit)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.SUB_GROUP_ORGANISATIONS_EINHEIT_ID)).thenReturn( + List.of(pvogChildOranigastionsEinheit)); + + service.syncOrganisationsEinheitenFromKeycloak(syncTimestamp); + + var parentSynced = repository.findSyncedByKeycloakId(group.getId()).get(); + var childSynced = repository.findSyncedByKeycloakId(subGroup.getId()).get(); + + assertThat(parentSynced) + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + group.getId(), + pvogOranigastionsEinheit.getName(), + group.getOrganisationsEinheitId(), + SyncResult.NAME_MISMATCH, + pvogOranigastionsEinheit.getId(), + null, + Collections.emptyList(), + syncTimestamp); + assertThat(childSynced) + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + subGroup.getId(), + pvogChildOranigastionsEinheit.getName(), + subGroup.getOrganisationsEinheitId(), + SyncResult.NAME_MISMATCH, + pvogChildOranigastionsEinheit.getId(), + parentSynced.getId(), + Collections.emptyList(), + syncTimestamp); + } + + @DisplayName("Multiple Organisationseinheiten for parent group and sub group found in PVOG") + @Test + void shouldSyncParentAndChildOrganisationsEinheitNotUnique() { + var syncTimestamp = Instant.now().toEpochMilli(); + var group = GroupTestFactory.createBuilder().build(); + var subGroup = group.getSubGroups().getFirst(); + var pvogOranigastionsEinheit1 = OrganisationsEinheitTestFactory.createBuilder().organisationsEinheitId(group.getOrganisationsEinheitId()) + .build(); + var pvogOranigastionsEinheit2 = OrganisationsEinheitTestFactory.createBuilder().organisationsEinheitId(group.getOrganisationsEinheitId()) + .build(); + var pvogChildOranigastionsEinheit1 = OrganisationsEinheitTestFactory.createBuilder() + .organisationsEinheitId(subGroup.getOrganisationsEinheitId()).name( + LoremIpsum.getInstance().getWords(2)) + .build(); + var pvogChildOranigastionsEinheit2 = OrganisationsEinheitTestFactory.createBuilder() + .organisationsEinheitId(subGroup.getOrganisationsEinheitId()).name( + LoremIpsum.getInstance().getWords(2)) + .build(); + when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn(Stream.of(group)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.ORGANISATIONS_EINHEIT_ID)).thenReturn( + List.of(pvogOranigastionsEinheit1, pvogOranigastionsEinheit2)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.SUB_GROUP_ORGANISATIONS_EINHEIT_ID)).thenReturn( + List.of(pvogChildOranigastionsEinheit1, pvogChildOranigastionsEinheit2)); + + service.syncOrganisationsEinheitenFromKeycloak(syncTimestamp); + + var parentSynced = repository.findSyncedByKeycloakId(group.getId()).get(); + var childSynced = repository.findSyncedByKeycloakId(subGroup.getId()).get(); + + assertThat(parentSynced) + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + group.getId(), + group.getName(), + group.getOrganisationsEinheitId(), + SyncResult.ORGANISATIONSEINHEIT_ID_NOT_UNIQUE, + null, + null, + Collections.emptyList(), + syncTimestamp); + assertThat(childSynced) + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + subGroup.getId(), + subGroup.getName(), + subGroup.getOrganisationsEinheitId(), + SyncResult.ORGANISATIONSEINHEIT_ID_NOT_UNIQUE, + null, + parentSynced.getId(), + Collections.emptyList(), + syncTimestamp); + } + + @DisplayName("Organisationseinheit and child Organisationseinheit found in PVOG and have same data") + @Test + void shouldSyncOkGroupWithSubGroup() { + var syncTimestamp = Instant.now().toEpochMilli(); + var group = GroupTestFactory.createBuilder().build(); + var subGroup = group.getSubGroups().getFirst(); + var pvogOranigastionsEinheit = OrganisationsEinheitTestFactory.createBuilder().organisationsEinheitId(group.getOrganisationsEinheitId()) + .name(group.getName()).build(); + var pvogChildOranigastionsEinheit = OrganisationsEinheitTestFactory.createBuilder() + .organisationsEinheitId(subGroup.getOrganisationsEinheitId()).name( + subGroup.getName()) + .build(); + when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn(Stream.of(group)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.ORGANISATIONS_EINHEIT_ID)).thenReturn( + List.of(pvogOranigastionsEinheit)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.SUB_GROUP_ORGANISATIONS_EINHEIT_ID)).thenReturn( + List.of(pvogChildOranigastionsEinheit)); + + service.syncOrganisationsEinheitenFromKeycloak(syncTimestamp); + + var parentSynced = repository.findSyncedByKeycloakId(group.getId()).get(); + var childSynced = repository.findSyncedByKeycloakId(subGroup.getId()).get(); + + assertThat(parentSynced) + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + group.getId(), + group.getName(), + group.getOrganisationsEinheitId(), + SyncResult.OK, + pvogOranigastionsEinheit.getId(), + null, + Collections.emptyList(), + syncTimestamp); + assertThat(childSynced) + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + subGroup.getId(), + subGroup.getName(), + subGroup.getOrganisationsEinheitId(), + SyncResult.OK, + pvogChildOranigastionsEinheit.getId(), + parentSynced.getId(), + Collections.emptyList(), + syncTimestamp); + } + + @DisplayName("Group with sub group deleted in Keycloak") + @Test + void shouldSyncGroupAndSubGroupDeleted() { + var syncTimestamp = Instant.now().toEpochMilli(); + var group = GroupTestFactory.createBuilder().build(); + var subGroup = group.getSubGroups().getFirst(); + var pvogOranigastionsEinheit = OrganisationsEinheitTestFactory.createBuilder().organisationsEinheitId(group.getOrganisationsEinheitId()) + .name(group.getName()).build(); + var pvogChildOranigastionsEinheit = OrganisationsEinheitTestFactory.createBuilder() + .organisationsEinheitId(subGroup.getOrganisationsEinheitId()).name( + subGroup.getName()) + .build(); + when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn(Stream.of(group)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.ORGANISATIONS_EINHEIT_ID)).thenReturn( + List.of(pvogOranigastionsEinheit)); + when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.SUB_GROUP_ORGANISATIONS_EINHEIT_ID)).thenReturn( + List.of(pvogChildOranigastionsEinheit)); + + service.syncOrganisationsEinheitenFromKeycloak(syncTimestamp); + when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn(Stream.of()); + service.syncOrganisationsEinheitenFromKeycloak(syncTimestamp + 1000); + + var parentSynced = repository.findSyncedByKeycloakId(group.getId()).get(); + var childSynced = repository.findSyncedByKeycloakId(subGroup.getId()).get(); + + assertThat(parentSynced) + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + group.getId(), + group.getName(), + group.getOrganisationsEinheitId(), + SyncResult.DELETED, + pvogOranigastionsEinheit.getId(), + null, + Collections.emptyList(), + syncTimestamp); + assertThat(childSynced) + .matches(organisationsEinheit -> Objects.nonNull(organisationsEinheit.getId())) + .matches(organisationsEinheit -> Objects.isNull(organisationsEinheit.getSettings().getSignatur())) + .extracting( + OrganisationsEinheit::getKeycloakId, + OrganisationsEinheit::getName, + OrganisationsEinheit::getOrganisationsEinheitId, + OrganisationsEinheit::getSyncResult, + OrganisationsEinheit::getZufiId, + OrganisationsEinheit::getParentId, + OrganisationsEinheit::getChildren, + OrganisationsEinheit::getLastSyncTimestamp + ).containsExactly( + subGroup.getId(), + subGroup.getName(), + subGroup.getOrganisationsEinheitId(), + SyncResult.DELETED, + pvogChildOranigastionsEinheit.getId(), + parentSynced.getId(), + Collections.emptyList(), + syncTimestamp); + } + +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/SyncServiceTest.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/SyncServiceTest.java index 875324ebf97e9efc9133b313ea8b41eda2a981fc..af78396ac96a80283352eb82a1aedd302b0f64ec 100644 --- a/src/test/java/de/ozgcloud/admin/organisationseinheit/SyncServiceTest.java +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/SyncServiceTest.java @@ -21,6 +21,8 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; +import com.thedeanda.lorem.LoremIpsum; + import de.ozgcloud.admin.keycloak.AddGroupData; import de.ozgcloud.admin.keycloak.AddGroupDataTestFactory; import de.ozgcloud.admin.keycloak.Group; @@ -55,7 +57,7 @@ class SyncServiceTest { @BeforeEach void setUp() { when(keycloakRemoteService.getGroupsWithOrganisationsEinheitId()).thenReturn(Stream.of(group)); - doNothing().when(service).syncGroups(any(), any(), anyLong()); + doNothing().when(service).syncGroupsWithSubGroups(any(), any(), anyLong()); } @Test @@ -69,7 +71,7 @@ class SyncServiceTest { void shouldSyncGroupsFromKeycloak() { callService(); - verify(service).syncGroups(group, null, syncTimestamp); + verify(service).syncGroupsWithSubGroups(group, null, syncTimestamp); } @Test @@ -86,7 +88,7 @@ class SyncServiceTest { } @Nested - class TestSyncGroups { + class TestSyncGroupsWithSubGroups { private final long syncTimestamp = Instant.now().toEpochMilli(); private final Group group = GroupTestFactory.create(); @@ -97,28 +99,28 @@ class SyncServiceTest { @BeforeEach void setUp() { doReturn(syncedOrganisationsEinheitGroup).when(service).syncGroup(group, null, syncTimestamp); - doReturn(savedOrganisationsEinheitGroup).when(service).saveSyncedOrganisationsEinheit(any()); + doReturn(savedOrganisationsEinheitGroup).when(service).saveSyncedOrganisationsEinheit(syncedOrganisationsEinheitGroup); } @Test void shouldSyncGroup() { - service.syncGroups(group, null, syncTimestamp); + service.syncGroupsWithSubGroups(group, null, syncTimestamp); verify(service).syncGroup(group, null, syncTimestamp); } @Test - void shouldSaveSyncedGroup() { - service.syncGroups(group, null, syncTimestamp); + void shouldSaveSyncedOrganisationsEinheit() { + service.syncGroupsWithSubGroups(group, null, syncTimestamp); verify(service).saveSyncedOrganisationsEinheit(syncedOrganisationsEinheitGroup); } @Test void shouldSyncSubGroupsWithSyncedOrganisationsEinheitAsParent() { - service.syncGroups(group, null, syncTimestamp); + service.syncGroupsWithSubGroups(group, null, syncTimestamp); - verify(service).syncGroups(group.getSubGroups().getFirst(), savedOrganisationsEinheitGroup, syncTimestamp); + verify(service).syncGroupsWithSubGroups(group.getSubGroups().getFirst(), savedOrganisationsEinheitGroup, syncTimestamp); } } @@ -130,14 +132,18 @@ class SyncServiceTest { private final OrganisationsEinheit parent = OrganisationsEinheitTestFactory.createBuilder().id(UUID.randomUUID().toString()).build(); private final OrganisationsEinheit pvogOrganisationsEinheit = OrganisationsEinheitTestFactory.createBuilder().zufiId(null).settings(null) .parentId(null).syncResult(null).build(); + private final String syncedName = LoremIpsum.getInstance().getWords(3); + private final SyncResult syncedSyncResult = SyncResult.OK; + private final String syncedZufiId = UUID.randomUUID().toString(); @Nested class ParentGroup { @BeforeEach void setUp() { - doReturn(OrganisationsEinheitTestFactory.NAME).when(service).syncName(anyList(), any()); - doReturn(SyncResult.OK).when(service).evaluateSyncResult(anyList(), any()); + doReturn(syncedName).when(service).syncName(List.of(pvogOrganisationsEinheit), group); + doReturn(syncedSyncResult).when(service).evaluateSyncResult(List.of(pvogOrganisationsEinheit), group); + doReturn(syncedZufiId).when(service).syncZufiId(List.of(pvogOrganisationsEinheit)); when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.ORGANISATIONS_EINHEIT_ID)).thenReturn( List.of(pvogOrganisationsEinheit)); } @@ -151,16 +157,18 @@ class SyncServiceTest { @Test void shouldSyncName() { - service.syncGroup(group, null, syncTimestamp); + var synced = service.syncGroup(group, null, syncTimestamp); verify(service).syncName(List.of(pvogOrganisationsEinheit), group); + assertThat(synced.getName()).isEqualTo(syncedName); } @Test void shouldEvaluateSyncResult() { - service.syncGroup(group, null, syncTimestamp); + var synced = service.syncGroup(group, null, syncTimestamp); verify(service).evaluateSyncResult(List.of(pvogOrganisationsEinheit), group); + assertThat(synced.getSyncResult()).isEqualTo(syncedSyncResult); } @Test @@ -170,11 +178,19 @@ class SyncServiceTest { verify(organisationsEinheitRemoteService).getByOrganisationsEinheitId(GroupTestFactory.ORGANISATIONS_EINHEIT_ID); } + @Test + void shouldSetOrganisationsEinheitId() { + var synced = service.syncGroup(group, null, syncTimestamp); + + assertThat(synced.getOrganisationsEinheitId()).isEqualTo(group.getOrganisationsEinheitId()); + } + @Test void shouldSyncZufiId() { - service.syncGroup(group, null, syncTimestamp); + var synced = service.syncGroup(group, null, syncTimestamp); verify(service).syncZufiId(List.of(pvogOrganisationsEinheit)); + assertThat(synced.getZufiId()).isEqualTo(syncedZufiId); } @Test @@ -190,8 +206,9 @@ class SyncServiceTest { @BeforeEach void setUp() { - doReturn(OrganisationsEinheitTestFactory.NAME).when(service).syncName(anyList(), any()); - doReturn(SyncResult.OK).when(service).evaluateSyncResult(anyList(), any()); + doReturn(syncedName).when(service).syncName(List.of(pvogOrganisationsEinheit), group.getSubGroups().getFirst()); + doReturn(syncedSyncResult).when(service).evaluateSyncResult(List.of(pvogOrganisationsEinheit), group.getSubGroups().getFirst()); + doReturn(syncedZufiId).when(service).syncZufiId(List.of(pvogOrganisationsEinheit)); when(organisationsEinheitRemoteService.getByOrganisationsEinheitId(GroupTestFactory.SUB_GROUP_ORGANISATIONS_EINHEIT_ID)).thenReturn( List.of(pvogOrganisationsEinheit)); } @@ -205,23 +222,26 @@ class SyncServiceTest { @Test void shouldSyncName() { - service.syncGroup(group.getSubGroups().getFirst(), parent, syncTimestamp); + var synced = service.syncGroup(group.getSubGroups().getFirst(), parent, syncTimestamp); verify(service).syncName(List.of(pvogOrganisationsEinheit), group.getSubGroups().getFirst()); + assertThat(synced.getName()).isEqualTo(syncedName); } @Test void shouldEvaluateSyncResult() { - service.syncGroup(group.getSubGroups().getFirst(), parent, syncTimestamp); + var synced = service.syncGroup(group.getSubGroups().getFirst(), parent, syncTimestamp); verify(service).evaluateSyncResult(List.of(pvogOrganisationsEinheit), group.getSubGroups().getFirst()); + assertThat(synced.getSyncResult()).isEqualTo(syncedSyncResult); } @Test void shouldGetOrganisationsEinheit() { - service.syncGroup(group.getSubGroups().getFirst(), parent, syncTimestamp); + var synced = service.syncGroup(group.getSubGroups().getFirst(), parent, syncTimestamp); verify(organisationsEinheitRemoteService).getByOrganisationsEinheitId(GroupTestFactory.SUB_GROUP_ORGANISATIONS_EINHEIT_ID); + assertThat(synced.getOrganisationsEinheitId()).isEqualTo(group.getSubGroups().getFirst().getOrganisationsEinheitId()); } @Test @@ -233,9 +253,10 @@ class SyncServiceTest { @Test void shouldSyncZufiId() { - service.syncGroup(group.getSubGroups().getFirst(), parent, syncTimestamp); + var synced = service.syncGroup(group.getSubGroups().getFirst(), parent, syncTimestamp); verify(service).syncZufiId(List.of(pvogOrganisationsEinheit)); + assertThat(synced.getZufiId()).isEqualTo(syncedZufiId); } @Test @@ -378,7 +399,7 @@ class SyncServiceTest { @Nested class SyncedOrganisationsEinheitExists { - private final OrganisationsEinheit existingOrganisationsEinheit = OrganisationsEinheitTestFactory.create(); + private final OrganisationsEinheit existingOrganisationsEinheit = OrganisationsEinheitTestFactory.createNewBuilder().build(); @Captor private ArgumentCaptor<OrganisationsEinheit> savedOrganisationsEinheitArgumentCaptor; @@ -422,8 +443,8 @@ class SyncServiceTest { private final long syncTimestamp = Instant.now().toEpochMilli(); private final OrganisationsEinheit withoutSyncResult1 = OrganisationsEinheitTestFactory.createBuilder().id("A").build(); private final OrganisationsEinheit withoutSyncResult2 = OrganisationsEinheitTestFactory.createBuilder().id("B").build(); - private final OrganisationsEinheit[] unsortedOrganisationsEinheiten = new OrganisationsEinheit[]{withoutSyncResult2, withoutSyncResult1}; - private final OrganisationsEinheit[] sortedOrganisationsEinheiten = new OrganisationsEinheit[]{withoutSyncResult1, withoutSyncResult2}; + private final OrganisationsEinheit[] unsortedOrganisationsEinheiten = new OrganisationsEinheit[] { withoutSyncResult2, withoutSyncResult1 }; + private final OrganisationsEinheit[] sortedOrganisationsEinheiten = new OrganisationsEinheit[] { withoutSyncResult1, withoutSyncResult2 }; @Captor private ArgumentCaptor<Stream<OrganisationsEinheit>> streamArgumentCaptor; @@ -517,86 +538,110 @@ class SyncServiceTest { class TestSyncAddedOrganisationsEinheit { private final long syncTimestamp = Instant.now().toEpochMilli(); - private final OrganisationsEinheit organisationsEinheit = OrganisationsEinheitTestFactory.create(); - private final AddGroupData addGroupData = AddGroupDataTestFactory.create(); - private final String keycloakId = GroupTestFactory.ID; + private final String addedGroupId = UUID.randomUUID().toString(); private final String parentKeycloakId = UUID.randomUUID().toString(); - - @BeforeEach - void setUp() { - doReturn(parentKeycloakId).when(service).findParentKeycloakId(organisationsEinheit); - when(organisationsEinheitMapper.toAddGroupData(organisationsEinheit, parentKeycloakId)).thenReturn(addGroupData); - } + private final OrganisationsEinheit organisationsEinheit = OrganisationsEinheitTestFactory.create(); + private final OrganisationsEinheit parentWithoutKeycloakId = OrganisationsEinheitTestFactory.createBuilder().keycloakId(null).build(); + private final OrganisationsEinheit parentWithKeycloakId = OrganisationsEinheitTestFactory.createBuilder().keycloakId(parentKeycloakId) + .build(); @Test void shouldFindParentKeycloakId() { - mockAddGroupInKeycloak(); + doReturn(Optional.of(parentWithoutKeycloakId)).when(service).findParent(organisationsEinheit); callService(); - verify(service).findParentKeycloakId(organisationsEinheit); + verify(service).findParent(organisationsEinheit); } - @Test - void shouldCreateAddGroupData() { - mockAddGroupInKeycloak(); - - callService(); - - verify(organisationsEinheitMapper).toAddGroupData(organisationsEinheit, parentKeycloakId); - } + @Nested + class OnParentHasNoKeycloakId { - @Test - void shouldAddGroupInKeycloak() { - mockAddGroupInKeycloak(); + @BeforeEach + void init() { + doReturn(Optional.of(parentWithoutKeycloakId)).when(service).findParent(organisationsEinheit); + } - callService(); + @Test + void shouldNotAddAsGroupInKeycloak() { + callService(); - verify(service).addGroupInKeycloak(addGroupData); + verify(service, never()).addAsGroupInKeycloak(any(), any()); + } } @Nested - class OnAddGroupInKeycloakSuccessful { - - @Captor - private ArgumentCaptor<OrganisationsEinheit> savedOrganisationsEinheitArgumentCaptor; + class OnOrganisationsEinheitIsTopLevel { @BeforeEach void init() { - doReturn(Optional.of(keycloakId)).when(service).addGroupInKeycloak(addGroupData); + doReturn(Optional.empty()).when(service).findParent(organisationsEinheit); } @Test - void shouldSaveIfAddGroupSuccessful() { + void shouldAddAsGroupInKeycloakWithNullParent() { + doReturn(Optional.empty()).when(service).addAsGroupInKeycloak(organisationsEinheit, null); + callService(); - verify(repository).save(savedOrganisationsEinheitArgumentCaptor.capture()); - assertThat(savedOrganisationsEinheitArgumentCaptor.getValue()).extracting( - OrganisationsEinheit::getKeycloakId, - OrganisationsEinheit::getSyncResult, - OrganisationsEinheit::getLastSyncTimestamp - ).containsExactly(keycloakId, SyncResult.OK, syncTimestamp); + verify(service).addAsGroupInKeycloak(organisationsEinheit, null); + } + + @Test + void shouldUpdateAfterSuccessfulGroupCreation() { + doReturn(Optional.of(addedGroupId)).when(service).addAsGroupInKeycloak(organisationsEinheit, null); + doNothing().when(service).updateAfterSuccessfulGroupCreation(organisationsEinheit, syncTimestamp, addedGroupId); + + callService(); + + verify(service).updateAfterSuccessfulGroupCreation(organisationsEinheit, syncTimestamp, addedGroupId); + } + + @Test + void shouldNotUpdate() { + doReturn(Optional.empty()).when(service).addAsGroupInKeycloak(organisationsEinheit, null); + + callService(); + + verify(service, never()).updateAfterSuccessfulGroupCreation(any(), anyLong(), any()); } } @Nested - class OnAddGroupInKeycloakFailed { + class OnParentHasKeycloakId { @BeforeEach void init() { - doReturn(Optional.empty()).when(service).addGroupInKeycloak(addGroupData); + doReturn(Optional.of(parentWithKeycloakId)).when(service).findParent(organisationsEinheit); } @Test - void shouldNotSaveIfAddGroupFailed() { + void shouldAddAsGroupInKeycloakWithNullParent() { + doReturn(Optional.empty()).when(service).addAsGroupInKeycloak(organisationsEinheit, parentKeycloakId); + callService(); - verify(repository, never()).save(any()); + verify(service).addAsGroupInKeycloak(organisationsEinheit, parentKeycloakId); } - } - private void mockAddGroupInKeycloak() { - doReturn(Optional.empty()).when(service).addGroupInKeycloak(addGroupData); + @Test + void shouldUpdateAfterSuccessfulGroupCreation() { + doReturn(Optional.of(addedGroupId)).when(service).addAsGroupInKeycloak(organisationsEinheit, parentKeycloakId); + doNothing().when(service).updateAfterSuccessfulGroupCreation(organisationsEinheit, syncTimestamp, addedGroupId); + + callService(); + + verify(service).updateAfterSuccessfulGroupCreation(organisationsEinheit, syncTimestamp, addedGroupId); + } + + @Test + void shouldNotUpdate() { + doReturn(Optional.empty()).when(service).addAsGroupInKeycloak(organisationsEinheit, parentKeycloakId); + + callService(); + + verify(service, never()).updateAfterSuccessfulGroupCreation(any(), anyLong(), any()); + } } private void callService() { @@ -605,15 +650,15 @@ class SyncServiceTest { } @Nested - class TestFindParentKeycloakId { + class TestFindParent { @Nested class OnParentIdIsNull { @Test - void shouldReturnNull() { - var parentKeycloakId = service.findParentKeycloakId(OrganisationsEinheitTestFactory.createBuilder().parentId(null).build()); + void shouldReturnEmpty() { + var parent = service.findParent(OrganisationsEinheitTestFactory.createBuilder().parentId(null).build()); - assertThat(parentKeycloakId).isNull(); + assertThat(parent).isEmpty(); } } @@ -631,20 +676,87 @@ class SyncServiceTest { @Test void shouldFindById() { - service.findParentKeycloakId(organisationsEinheit); + callService(); verify(repository).findById(OrganisationsEinheitTestFactory.PARENT_ID); } @Test - void shouldReturnParentKeycloakId() { - var parentKeycloakId = service.findParentKeycloakId(organisationsEinheit); + void shouldReturnParent() { + var found = callService(); - assertThat(parentKeycloakId).isEqualTo(PARENT_KEYCLOAK_ID); + assertThat(found).isPresent().get().isEqualTo(parent); + } + + private Optional<OrganisationsEinheit> callService() { + return service.findParent(organisationsEinheit); } } + } + + @Nested + class TestAddAsGroupInKeycloak { + private final OrganisationsEinheit organisationsEinheit = OrganisationsEinheitTestFactory.create(); + private final String parentKeycloakId = UUID.randomUUID().toString(); + private final AddGroupData addGroupData = AddGroupDataTestFactory.create(); + private final String addedGroupId = UUID.randomUUID().toString(); + @BeforeEach + void init() { + when(organisationsEinheitMapper.toAddGroupData(organisationsEinheit, parentKeycloakId)).thenReturn(addGroupData); + doReturn(Optional.of(addedGroupId)).when(service).addGroupInKeycloak(addGroupData); + } + + @Test + void shouldCreateAddGroupData() { + callService(); + + verify(organisationsEinheitMapper).toAddGroupData(organisationsEinheit, parentKeycloakId); + } + + @Test + void shouldAddGroupInKeycloak() { + callService(); + + verify(service).addGroupInKeycloak(addGroupData); + } + + @Test + void shouldReturnAddedGroupId() { + var returnedGroupId = callService(); + + assertThat(returnedGroupId).get().isEqualTo(addedGroupId); + } + + private Optional<String> callService() { + return service.addAsGroupInKeycloak(organisationsEinheit, parentKeycloakId); + } + } + + @Nested + class TestUpdateAfterSuccessfulGroupCreation { + + private final OrganisationsEinheit organisationsEinheit = OrganisationsEinheitTestFactory.createBuilder() + .keycloakId(null) + .syncResult(null) + .lastSyncTimestamp(null) + .build(); + private final long syncTimestamp = Instant.now().toEpochMilli(); + private final String keycloakId = UUID.randomUUID().toString(); + + @Captor + private ArgumentCaptor<OrganisationsEinheit> organisationsEinheitArgumentCaptor; + + @Test + void shouldSaveUpdatedOrganisationsEinheit() { + service.updateAfterSuccessfulGroupCreation(organisationsEinheit, syncTimestamp, keycloakId); + + verify(repository).save(organisationsEinheitArgumentCaptor.capture()); + assertThat(organisationsEinheitArgumentCaptor.getValue()) + .extracting(OrganisationsEinheit::getKeycloakId, OrganisationsEinheit::getSyncResult, OrganisationsEinheit::getLastSyncTimestamp) + .containsExactly(keycloakId, SyncResult.OK, syncTimestamp); + } } @Nested