diff --git a/src/main/java/de/ozgcloud/aggregation/AggregationLoader.java b/src/main/java/de/ozgcloud/aggregation/AggregationLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..3e8111b3be4c7cbc9af241b7fc6518a11e153032 --- /dev/null +++ b/src/main/java/de/ozgcloud/aggregation/AggregationLoader.java @@ -0,0 +1,139 @@ +package de.ozgcloud.aggregation; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.IntPredicate; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.apache.logging.log4j.CloseableThreadContext; + +import de.ozgcloud.aggregation.AggregationManagerRunner.Execution; +import de.ozgcloud.aggregation.transformation.AggregationMapping.FormIdentifier; +import de.ozgcloud.aggregation.transformation.TransformationException; +import de.ozgcloud.aggregation.transformation.VorgangMapper; +import de.ozgcloud.aggregation.warehouse.DocumentEntry; +import de.ozgcloud.aggregation.warehouse.WarehouseRepository; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgang; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgangQuery; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgangQuery.FormIdentification; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgangService; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgangStub; +import de.ozgcloud.apilib.vorgang.Page; +import lombok.Builder; +import lombok.extern.log4j.Log4j2; + +@Builder +@Log4j2 +class AggregationLoader { + + private static final String MDC_VORGANG = "vorgang"; + private static final Predicate<Batch> HAS_NEXT_BATCH = x -> !x.items.isEmpty(); + + private final Execution execution; + private final FormIdentifier formIdentifier; + @Builder.Default + private final String collectionName = DocumentEntry.COLLECTION; + private final OzgCloudVorgangService vorgangService; + private final WarehouseRepository repository; + private final VorgangMapper vorgangMapper; + @Builder.Default + private final int batchSize = 100; + + public void load() { + Stream<Batch> vorgaenge = extractBatchesOfVorgaengeFromDataSource(); + Stream<Batch> deletedVorgaenge = Objects.isNull(formIdentifier) ? extractBatchesOfDeletedVorgaengeFromDataSource() : Stream.<Batch>empty(); + loadVorgaengeIntoRepository(Stream.concat(vorgaenge, deletedVorgaenge)); + } + + Stream<Batch> extractBatchesOfVorgaengeFromDataSource() { + return extractBatchesFromDataSource(page -> getVorgaengeFromDataSource(page)); + } + + List<OzgCloudVorgang> getVorgaengeFromDataSource(Page page) { + return vorgangService.find(buildFindByFormEngineQuery(), page).stream() + .map(vorgangStub -> vorgangService.getById(vorgangStub.getId())) + .toList(); + } + + OzgCloudVorgangQuery buildFindByFormEngineQuery() { + return OzgCloudVorgangQuery.builder() + .form(mapToFormIdentification()) + .build(); + } + + private Optional<FormIdentification> mapToFormIdentification() { + return Optional.ofNullable(formIdentifier) + .map(identifier -> FormIdentification.builder() + .formId(identifier.getFormId()) + .formEngineName(identifier.getFormEngineName()) + .build()); + } + + Stream<Batch> extractBatchesOfDeletedVorgaengeFromDataSource() { + return extractBatchesFromDataSource(getPagedDeletedVorgaenge(vorgangService.findDeleted())); + } + + Function<Page, List<OzgCloudVorgang>> getPagedDeletedVorgaenge(Stream<OzgCloudVorgangStub> allDeletedVorgaenge) { + var it = allDeletedVorgaenge.iterator(); + IntPredicate hasNextVorgangStub = ignored -> it.hasNext(); + IntFunction<OzgCloudVorgangStub> nextVorgangStub = ignored -> it.next(); + return page -> IntStream.range(page.getOffset(), page.getOffset() + page.getLimit()) + .takeWhile(hasNextVorgangStub) + .mapToObj(nextVorgangStub) + .map(vorgangMapper::fromVorgangStub) + .toList(); + } + + Stream<Batch> extractBatchesFromDataSource(Function<Page, List<OzgCloudVorgang>> getFromDataSource) { + var initialBatch = createBatch(execution, Page.builder().offset(0).limit(batchSize).build(), getFromDataSource); + UnaryOperator<Batch> nextBatch = x -> createBatch( + execution, + Page.builder().offset(x.page.getOffset() + batchSize).limit(batchSize).build(), + getFromDataSource); + return Stream.iterate(initialBatch, HAS_NEXT_BATCH, nextBatch).filter(x -> !x.items.isEmpty()); + } + + private Batch createBatch(Execution execution, Page page, Function<Page, List<OzgCloudVorgang>> getFromDataSource) { + return new Batch(execution, createBatchUUID(), page, getFromDataSource.apply(page)); + } + + UUID createBatchUUID() { + return UUID.randomUUID(); + } + + void loadVorgaengeIntoRepository(Stream<Batch> batches) { + repository.clearCollection(collectionName); + batches.map(this::transformBatchToDocumentEntries).forEach(this::loadDocumentEntriesIntoRepository); + } + + List<DocumentEntry> transformBatchToDocumentEntries(Batch batch) { + return batch.items.stream().map(vorgang -> transformWithinBatch(batch, vorgang)).filter(Objects::nonNull).toList(); + } + + DocumentEntry transformWithinBatch(Batch batch, OzgCloudVorgang vorgang) { + try (var instance = CloseableThreadContext.put(MDC_VORGANG, vorgang.getId().toString())) { + return batch.execution.getTransformation().apply(vorgang); + } catch (TransformationException e) { + LOG.error( + "failed to transform vorgang [%s] in batch [%s] of execution [%s]".formatted(vorgang.getId(), batch.id, batch.execution.getId()), + e); + return null; + } + } + + void loadDocumentEntriesIntoRepository(List<DocumentEntry> entries) { + LOG.atDebug().log("store documents: {}", () -> entries.stream().map(DocumentEntry::getId).toList()); + repository.saveAllInCollection(entries, collectionName); + } + + record Batch(Execution execution, UUID id, Page page, List<OzgCloudVorgang> items) { + } + +} diff --git a/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java b/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java index 9584200a47f784405c35ea92172122bd7068e455..33159fba127c9cd79a03ee26abd0937fa3e1cc0b 100644 --- a/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java +++ b/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java @@ -23,37 +23,21 @@ */ package de.ozgcloud.aggregation; -import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.UUID; -import java.util.function.Function; -import java.util.function.IntFunction; -import java.util.function.IntPredicate; -import java.util.function.Predicate; -import java.util.function.UnaryOperator; -import java.util.stream.IntStream; -import java.util.stream.Stream; -import org.apache.logging.log4j.CloseableThreadContext; import org.apache.logging.log4j.ThreadContext; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; -import de.ozgcloud.aggregation.transformation.AggregationMapping.FormIdentifier; +import de.ozgcloud.aggregation.transformation.AggregationMapping; import de.ozgcloud.aggregation.transformation.Transformation; import de.ozgcloud.aggregation.transformation.TransformationException; import de.ozgcloud.aggregation.transformation.TransformationService; import de.ozgcloud.aggregation.transformation.VorgangMapper; -import de.ozgcloud.aggregation.warehouse.DocumentEntry; import de.ozgcloud.aggregation.warehouse.WarehouseRepository; -import de.ozgcloud.apilib.vorgang.OzgCloudVorgang; -import de.ozgcloud.apilib.vorgang.OzgCloudVorgangQuery; -import de.ozgcloud.apilib.vorgang.OzgCloudVorgangQuery.FormIdentification; import de.ozgcloud.apilib.vorgang.OzgCloudVorgangService; -import de.ozgcloud.apilib.vorgang.OzgCloudVorgangStub; -import de.ozgcloud.apilib.vorgang.Page; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -65,9 +49,6 @@ import lombok.extern.log4j.Log4j2; public class AggregationManagerRunner implements CommandLineRunner { private static final String MDC_EXECUTION = "execution"; - private static final String MDC_VORGANG = "vorgang"; - - private static final Predicate<Batch> HAS_NEXT_BATCH = x -> !x.items.isEmpty(); private final OzgCloudVorgangService vorgangService; private final AggregationManagerConfiguration config; @@ -87,108 +68,34 @@ public class AggregationManagerRunner implements CommandLineRunner { } else { aggregationMappings.stream() .forEach(aggregationMapping -> runWithTransformation(transformationService.load(identifier, aggregationMapping), - aggregationMapping.getFormIdentifier())); + aggregationMapping)); } } - void runWithTransformation(Transformation transformation, FormIdentifier formIdentifier) { + void runWithTransformation(Transformation transformation, AggregationMapping aggregationMapping) { try (Execution execution = new Execution(transformation)) { ThreadContext.put(MDC_EXECUTION, execution.id.toString()); - var vorgaenge = extractBatchesOfVorgaengeFromDataSource(execution, formIdentifier); - var deletedVorgaenge = Objects.isNull(formIdentifier) ? extractBatchesOfDeletedVorgaengeFromDataSource(execution) - : Stream.<Batch>empty(); - loadVorgaengeIntoRepository(Stream.concat(vorgaenge, deletedVorgaenge)); + buildAggregationLoader(transformation, aggregationMapping).load(); } finally { ThreadContext.remove(MDC_EXECUTION); } } - void loadVorgaengeIntoRepository(Stream<Batch> batches) { - repository.deleteAll(); - batches.map(this::transformBatchToDocumentEntries).forEach(this::loadDocumentEntriesIntoRepository); - } - - Stream<Batch> extractBatchesOfVorgaengeFromDataSource(Execution execution, FormIdentifier formIdentifier) { - return extractBatchesFromDataSource(execution, page -> getVorgaengeFromDataSource(page, formIdentifier)); - } - - List<OzgCloudVorgang> getVorgaengeFromDataSource(Page page, FormIdentifier formIdentifier) { - return vorgangService.find(buildFindByFormEngineQuery(formIdentifier), page).stream() - .map(vorgangStub -> vorgangService.getById(vorgangStub.getId())) - .toList(); - } - - OzgCloudVorgangQuery buildFindByFormEngineQuery(FormIdentifier formIdentifier) { - return OzgCloudVorgangQuery.builder() - .form(mapToFormIdentification(formIdentifier)) - .build(); - } - - private Optional<FormIdentification> mapToFormIdentification(FormIdentifier formIdentifier) { - return Optional.ofNullable(formIdentifier) - .map(identifier -> FormIdentification.builder() - .formId(identifier.getFormId()) - .formEngineName(identifier.getFormEngineName()) - .build()); - } - - Stream<Batch> extractBatchesOfDeletedVorgaengeFromDataSource(Execution execution) { - return extractBatchesFromDataSource(execution, getPagedDeletedVorgaenge(vorgangService.findDeleted())); - } - - Function<Page, List<OzgCloudVorgang>> getPagedDeletedVorgaenge(Stream<OzgCloudVorgangStub> allDeletedVorgaenge) { - var it = allDeletedVorgaenge.iterator(); - IntPredicate hasNextVorgangStub = ignored -> it.hasNext(); - IntFunction<OzgCloudVorgangStub> nextVorgangStub = ignored -> it.next(); - return page -> IntStream.range(page.getOffset(), page.getOffset() + page.getLimit()) - .takeWhile(hasNextVorgangStub) - .mapToObj(nextVorgangStub) - .map(vorgangMapper::fromVorgangStub) - .toList(); - } - - Stream<Batch> extractBatchesFromDataSource(Execution execution, Function<Page, List<OzgCloudVorgang>> getFromDataSource) { - var fetchSize = config.getFetchingBatchSize(); - var initialBatch = createBatch(execution, Page.builder().offset(0).limit(fetchSize).build(), getFromDataSource); - UnaryOperator<Batch> nextBatch = x -> createBatch( - execution, - Page.builder().offset(x.page.getOffset() + fetchSize).limit(fetchSize).build(), - getFromDataSource); - return Stream.iterate(initialBatch, HAS_NEXT_BATCH, nextBatch).filter(x -> !x.items.isEmpty()); - } - - private Batch createBatch(Execution execution, Page page, Function<Page, List<OzgCloudVorgang>> getFromDataSource) { - return new Batch(execution, createBatchUUID(), page, getFromDataSource.apply(page)); - } - - UUID createBatchUUID() { - return UUID.randomUUID(); - } - - void loadDocumentEntriesIntoRepository(Stream<DocumentEntry> entries) { - final List<DocumentEntry> documents = entries.toList(); - LOG.atDebug().log("store documents: {}", () -> documents.stream().map(DocumentEntry::getId).toList()); - repository.saveAll(documents); - } - - Stream<DocumentEntry> transformBatchToDocumentEntries(Batch batch) { - return batch.items.stream().map(vorgang -> transformWithinBatch(batch, vorgang)).filter(Objects::nonNull); - } - - DocumentEntry transformWithinBatch(Batch batch, OzgCloudVorgang vorgang) { - try (var instance = CloseableThreadContext.put(MDC_VORGANG, vorgang.getId().toString())) { - return batch.execution.transformation.apply(vorgang); - } catch (TransformationException e) { - LOG.atError().withThrowable(e).log("failed to transform vorgang [{}] in batch [{}] of execution [{}]", - vorgang::getId, () -> batch.id, () -> batch.execution.id); - return null; - } + AggregationLoader buildAggregationLoader(Transformation transformation, AggregationMapping aggregationMapping) { + var builder = AggregationLoader.builder() + .execution(new Execution(transformation)) + .vorgangService(vorgangService) + .repository(repository) + .vorgangMapper(vorgangMapper) + .batchSize(config.getFetchingBatchSize()); + return Objects.isNull(aggregationMapping) ? builder.build() + : builder.formIdentifier(aggregationMapping.getFormIdentifier()).collectionName(aggregationMapping.getName()).build(); } + @Getter @RequiredArgsConstructor protected static class Execution implements AutoCloseable { private final UUID id = UUID.randomUUID(); - @Getter private final Transformation transformation; @Override @@ -197,7 +104,4 @@ public class AggregationManagerRunner implements CommandLineRunner { } } - record Batch(Execution execution, UUID id, Page page, List<OzgCloudVorgang> items) { - } - } diff --git a/src/main/java/de/ozgcloud/aggregation/TransformationProperties.java b/src/main/java/de/ozgcloud/aggregation/TransformationProperties.java index b639b4bd91893f94bef366a4f16122a7a8265103..aadc1cac0a74c6ceec10de99559e7c6862b2a84c 100644 --- a/src/main/java/de/ozgcloud/aggregation/TransformationProperties.java +++ b/src/main/java/de/ozgcloud/aggregation/TransformationProperties.java @@ -25,8 +25,11 @@ package de.ozgcloud.aggregation; import java.util.List; +import jakarta.validation.Valid; + import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; +import org.springframework.validation.annotation.Validated; import de.ozgcloud.aggregation.transformation.AggregationMapping; import lombok.Getter; @@ -35,6 +38,7 @@ import lombok.extern.log4j.Log4j2; @ConfigurationProperties(prefix = "ozgcloud.aggregation") @Configuration +@Validated @Getter @Setter @Log4j2 @@ -44,6 +48,7 @@ public class TransformationProperties { * List of field mapping definitions with a form specification to which the * field mappings should be applied */ + @Valid private List<AggregationMapping> aggregationMappings; /* diff --git a/src/main/java/de/ozgcloud/aggregation/transformation/AggregationMapping.java b/src/main/java/de/ozgcloud/aggregation/transformation/AggregationMapping.java index a6bceee0d0ad3d227ea44d58efdeeabbc67a4b40..c2416541030debc21b8c36e86606841ea92ac5eb 100644 --- a/src/main/java/de/ozgcloud/aggregation/transformation/AggregationMapping.java +++ b/src/main/java/de/ozgcloud/aggregation/transformation/AggregationMapping.java @@ -25,6 +25,10 @@ package de.ozgcloud.aggregation.transformation; import java.util.List; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + import lombok.Builder; import lombok.Getter; import lombok.Singular; @@ -35,23 +39,31 @@ import lombok.ToString; @ToString public class AggregationMapping { + @NotNull + @Valid private FormIdentifier formIdentifier; + @NotBlank private String name; @Singular + @Valid private List<FieldMapping> fieldMappings; @Getter @Builder public static class FormIdentifier { + @NotBlank private String formEngineName; + @NotBlank private String formId; } @Getter @Builder public static class FieldMapping { + @NotBlank private String sourcePath; + @NotBlank private String targetPath; } } diff --git a/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepository.java b/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepository.java index b191dca31c37a63afb0bcda2f2dc13ecd4a58125..0a312165a18ef10eab5ce9178e3c4ba6f4efc396 100644 --- a/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepository.java +++ b/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepository.java @@ -1,8 +1,12 @@ package de.ozgcloud.aggregation.warehouse; +import java.util.List; + interface CustomWarehouseRepository { DocumentEntry saveInCollection(DocumentEntry documentEntry, String collectionName); + List<DocumentEntry> saveAllInCollection(Iterable<DocumentEntry> documentEntries, String collectionName); + void clearCollection(String collectionName); } diff --git a/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImpl.java b/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImpl.java index 7b6a1c09ad7c5d5cc15d30f531a6b4e031a59b19..a5169d606f6a6055943538d8ac02f8a0477716b1 100644 --- a/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImpl.java +++ b/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImpl.java @@ -1,5 +1,8 @@ package de.ozgcloud.aggregation.warehouse; +import java.util.List; +import java.util.stream.StreamSupport; + import org.springframework.data.mongodb.core.MongoTemplate; import lombok.RequiredArgsConstructor; @@ -9,6 +12,11 @@ class CustomWarehouseRepositoryImpl implements CustomWarehouseRepository { private final MongoTemplate mongoTemplate; + @Override + public List<DocumentEntry> saveAllInCollection(Iterable<DocumentEntry> documentEntries, String collectionName) { + return StreamSupport.stream(documentEntries.spliterator(), false).map(entry -> saveInCollection(entry, collectionName)).toList(); + } + @Override public DocumentEntry saveInCollection(DocumentEntry documentEntry, String collectionName) { return mongoTemplate.save(documentEntry, collectionName); diff --git a/src/main/java/de/ozgcloud/aggregation/warehouse/DocumentEntry.java b/src/main/java/de/ozgcloud/aggregation/warehouse/DocumentEntry.java index 0b17e778ea3b1874c261d40a2110961a21dd0029..8f368ac3845ce1d37eedfc408cf1e0b0b2ab7da3 100644 --- a/src/main/java/de/ozgcloud/aggregation/warehouse/DocumentEntry.java +++ b/src/main/java/de/ozgcloud/aggregation/warehouse/DocumentEntry.java @@ -40,7 +40,7 @@ import lombok.Getter; @TypeAlias("Vorgang") public class DocumentEntry { - static final String COLLECTION = "vorgang"; + public static final String COLLECTION = "vorgang"; @Id private String id; diff --git a/src/test/java/de/ozgcloud/aggregation/AggregationLoaderTest.java b/src/test/java/de/ozgcloud/aggregation/AggregationLoaderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..dadcadf47a6df34ae4b299043e8f821b5d6db413 --- /dev/null +++ b/src/test/java/de/ozgcloud/aggregation/AggregationLoaderTest.java @@ -0,0 +1,480 @@ +package de.ozgcloud.aggregation; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Stream; + +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.ArgumentMatcher; +import org.mockito.Captor; +import org.mockito.Mock; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.aggregation.AggregationLoader.Batch; +import de.ozgcloud.aggregation.AggregationManagerRunner.Execution; +import de.ozgcloud.aggregation.transformation.AggregationMapping.FormIdentifier; +import de.ozgcloud.aggregation.transformation.FormIdentifierTestFactory; +import de.ozgcloud.aggregation.transformation.Transformation; +import de.ozgcloud.aggregation.transformation.VorgangMapper; +import de.ozgcloud.aggregation.warehouse.DocumentEntry; +import de.ozgcloud.aggregation.warehouse.DocumentEntryTestFactory; +import de.ozgcloud.aggregation.warehouse.WarehouseRepository; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgang; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgangQuery; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgangService; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgangStub; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgangStubTestFactory; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgangTestFactory; +import de.ozgcloud.apilib.vorgang.Page; + +class AggregationLoaderTest { + + private AggregationLoader aggregationLoader; + @Mock + private Transformation transformation; + private final Execution execution = new Execution(transformation); + private final FormIdentifier formIdentifier = FormIdentifierTestFactory.create(); + @Mock + private WarehouseRepository repository; + @Mock + private VorgangMapper vorgangMapper; + @Mock + private OzgCloudVorgangService vorgangService; + private AggregationLoader.AggregationLoaderBuilder aggregationLoaderBuilder; + + @BeforeEach + void setUp() { + aggregationLoaderBuilder = AggregationLoader.builder() + .execution(execution) + .repository(repository) + .vorgangMapper(vorgangMapper) + .vorgangService(vorgangService); + } + + @Nested + class TestLoad { + @Mock + private Batch batchOfVorgaenge; + @Mock + private Batch batchOfDeletedVorgaenge; + @Captor + private ArgumentCaptor<Stream<Batch>> batchStreamCaptor; + + @Nested + class TestOnFormIdentifierIsNull { + + @BeforeEach + void setUp() { + aggregationLoader = spy(aggregationLoaderBuilder.formIdentifier(null).build()); + doReturn(Stream.of(batchOfVorgaenge)).when(aggregationLoader).extractBatchesOfVorgaengeFromDataSource(); + doReturn(Stream.of(batchOfDeletedVorgaenge)).when(aggregationLoader).extractBatchesOfDeletedVorgaengeFromDataSource(); + doNothing().when(aggregationLoader).loadVorgaengeIntoRepository(any()); + } + + @Test + void shouldExtractBatchesOfVorgaengeFromDataSource() { + aggregationLoader.load(); + + verify(aggregationLoader).extractBatchesOfVorgaengeFromDataSource(); + } + + @Test + void shouldExtractBatchesOfDeletedVorgaengeFromDataSource() { + aggregationLoader.load(); + + verify(aggregationLoader).extractBatchesOfDeletedVorgaengeFromDataSource(); + } + + @Test + void shouldLoadVorgaengeIntoRepository() { + aggregationLoader.load(); + + verify(aggregationLoader).loadVorgaengeIntoRepository(batchStreamCaptor.capture()); + assertThat(batchStreamCaptor.getValue()).containsExactly(batchOfVorgaenge, batchOfDeletedVorgaenge); + } + } + + @Nested + class TestOnFormIdentifierIsNotNull { + + @BeforeEach + void setUp() { + aggregationLoader = spy(aggregationLoaderBuilder.formIdentifier(formIdentifier).build()); + doReturn(Stream.of(batchOfVorgaenge)).when(aggregationLoader).extractBatchesOfVorgaengeFromDataSource(); + doNothing().when(aggregationLoader).loadVorgaengeIntoRepository(any()); + } + + @Test + void shouldExtractBatchesOfVorgaengeFromDataSource() { + aggregationLoader.load(); + + verify(aggregationLoader).extractBatchesOfVorgaengeFromDataSource(); + } + + @Test + void shouldNotExtractBatchesOfDeletedVorgaengeFromDataSource() { + aggregationLoader.load(); + + verify(aggregationLoader, never()).extractBatchesOfDeletedVorgaengeFromDataSource(); + } + + @Test + void shouldLoadVorgaengeIntoRepository() { + aggregationLoader.load(); + + verify(aggregationLoader).loadVorgaengeIntoRepository(batchStreamCaptor.capture()); + assertThat(batchStreamCaptor.getValue()).containsExactly(batchOfVorgaenge); + } + } + } + + @Nested + class TestExtractBatchesOfVorgaengeFromDataSource { + @Captor + private ArgumentCaptor<Function<Page, List<OzgCloudVorgang>>> functionToRetrieveDataCaptor; + @Mock + private Batch batch; + @Mock + private Page page; + + @BeforeEach + void init() { + aggregationLoader = spy(aggregationLoaderBuilder.formIdentifier(formIdentifier).build()); + doReturn(Stream.of(batch)).when(aggregationLoader).extractBatchesFromDataSource(any()); + } + + @Test + void shouldExtractWithDataRetrievalFunction() { + doReturn(Collections.emptyList()).when(aggregationLoader).getVorgaengeFromDataSource(any()); + + aggregationLoader.extractBatchesOfVorgaengeFromDataSource(); + + verify(aggregationLoader).extractBatchesFromDataSource(functionToRetrieveDataCaptor.capture()); + functionToRetrieveDataCaptor.getValue().apply(page); + verify(aggregationLoader).getVorgaengeFromDataSource(page); + } + + @Test + void shouldReturnExtractedBatches() { + var extracted = aggregationLoader.extractBatchesOfVorgaengeFromDataSource(); + + assertThat(extracted).containsExactly(batch); + } + } + + @Nested + class TestGetVorgaengeFromDataSource { + + private final Page page = Page.builder().offset(10).limit(2).build(); + private final FormIdentifier formIdentifier = FormIdentifierTestFactory.create(); + private final OzgCloudVorgangQuery query = OzgCloudVorgangQuery.builder().build(); + + @BeforeEach + void init() { + aggregationLoader = spy(aggregationLoaderBuilder.formIdentifier(formIdentifier).build()); + doReturn(query).when(aggregationLoader).buildFindByFormEngineQuery(); + when(vorgangService.find(any(), any())).thenReturn(List.of(OzgCloudVorgangStubTestFactory.create())); + when(vorgangService.getById(any())).thenReturn(OzgCloudVorgangTestFactory.create()); + } + + @Test + void shouldCallBuildFindByFormEngineQuery() { + getVorgaengeFromDataSource(); + + verify(aggregationLoader).buildFindByFormEngineQuery(); + } + + @Test + void shouldCallVorgangService() { + getVorgaengeFromDataSource(); + + verify(vorgangService).find(query, page); + } + + @Test + void shouldGetVorgangDetails() { + getVorgaengeFromDataSource(); + + verify(vorgangService).getById(OzgCloudVorgangTestFactory.ID); + } + + @Test + void shouldReturnVorgangDetails() { + var vorgaenge = getVorgaengeFromDataSource(); + + assertThat(vorgaenge).usingRecursiveFieldByFieldElementComparator().containsExactly(OzgCloudVorgangTestFactory.create()); + } + + private List<OzgCloudVorgang> getVorgaengeFromDataSource() { + return aggregationLoader.getVorgaengeFromDataSource(page); + } + } + + @Nested + class TestBuildFindByFormEngineQuery { + + @Nested + class TestOnFormIdentifierNull { + + @BeforeEach + void init() { + aggregationLoader = spy(aggregationLoaderBuilder.formIdentifier(null).build()); + } + + @Test + void shouldReturnFindAllQueryOnNullFormIdentifier() { + var query = aggregationLoader.buildFindByFormEngineQuery(); + + assertThat(query).usingRecursiveComparison().isEqualTo(OzgCloudVorgangQuery.builder().build()); + } + } + + @Nested + class TestOnFormIdentifierNotNull { + + @BeforeEach + void init() { + aggregationLoader = spy(aggregationLoaderBuilder.formIdentifier(formIdentifier).build()); + } + + @Test + void shouldSetFormIdInQuery() { + var query = aggregationLoader.buildFindByFormEngineQuery(); + + assertThat(query.getForm()).get().extracting("formId").isEqualTo(FormIdentifierTestFactory.FORM_ID); + } + + @Test + void shouldSetFormEngineNameInQuery() { + var query = aggregationLoader.buildFindByFormEngineQuery(); + + assertThat(query.getForm()).get().extracting("formEngineName").isEqualTo(FormIdentifierTestFactory.FORM_ENGINE_NAME); + } + } + } + + @Nested + class TestExtractBatchesOfDeletedVorgaengeFromDataSource { + + @Mock + private Execution execution; + @Mock + private Function<Page, List<OzgCloudVorgang>> functionToRetrieveData; + private final List<OzgCloudVorgangStub> deletedVorgaenge = List.of(OzgCloudVorgangStubTestFactory.create()); + @Captor + private ArgumentCaptor<Stream<OzgCloudVorgangStub>> deletedVorgaengeCaptor; + @Mock + private Batch batch; + + @BeforeEach + void init() { + aggregationLoader = spy(aggregationLoaderBuilder.formIdentifier(null).build()); + when(vorgangService.findDeleted()).thenReturn(deletedVorgaenge.stream()); + doReturn(functionToRetrieveData).when(aggregationLoader).getPagedDeletedVorgaenge(any()); + doReturn(Stream.of(batch)).when(aggregationLoader).extractBatchesFromDataSource(any()); + } + + @Test + void shouldFindDeleted() { + aggregationLoader.extractBatchesOfDeletedVorgaengeFromDataSource(); + + verify(vorgangService).findDeleted(); + } + + @Test + void shouldGetPagedDeletedVorgaenge() { + aggregationLoader.extractBatchesOfDeletedVorgaengeFromDataSource(); + + verify(aggregationLoader).getPagedDeletedVorgaenge(deletedVorgaengeCaptor.capture()); + assertThat(deletedVorgaengeCaptor.getValue()).usingRecursiveFieldByFieldElementComparator().containsExactlyElementsOf(deletedVorgaenge); + } + + @Test + void shouldExtractWithDataRetrievalFunction() { + aggregationLoader.extractBatchesOfDeletedVorgaengeFromDataSource(); + + verify(aggregationLoader).extractBatchesFromDataSource(functionToRetrieveData); + } + + @Test + void shouldReturnExtractedBatches() { + var extracted = aggregationLoader.extractBatchesOfDeletedVorgaengeFromDataSource(); + + assertThat(extracted).containsExactly(batch); + } + } + + @Nested + class TestGetPagedDeletedVorgaenge { + + private static final int PAGE_SIZE = 2; + + private final Stream<OzgCloudVorgangStub> vorgangStubs = Stream.generate(OzgCloudVorgangStubTestFactory::create).limit(PAGE_SIZE + 1); + + @BeforeEach + void init() { + aggregationLoader = spy(aggregationLoaderBuilder.formIdentifier(null).build()); + } + + @Test + void shouldReturnFirstFullPage() { + var pagingFunction = aggregationLoader.getPagedDeletedVorgaenge(vorgangStubs); + + var vorgaenge = getFirstPage(pagingFunction); + + assertThat(vorgaenge).hasSize(PAGE_SIZE); + } + + @Test + void shouldReturnSecondPageWithOneElement() { + var pagingFunction = aggregationLoader.getPagedDeletedVorgaenge(vorgangStubs); + getFirstPage(pagingFunction); + + var vorgaenge = getSecondPage(pagingFunction); + + assertThat(vorgaenge).hasSize(1); + } + + private List<OzgCloudVorgang> getFirstPage(Function<Page, List<OzgCloudVorgang>> pagingFunction) { + return pagingFunction.apply(Page.builder().offset(0).limit(PAGE_SIZE).build()); + } + + private List<OzgCloudVorgang> getSecondPage(Function<Page, List<OzgCloudVorgang>> pagingFunction) { + return pagingFunction.apply(Page.builder().offset(PAGE_SIZE).limit(PAGE_SIZE).build()); + } + } + + @Nested + class TestExtractBatchesFromDataSource { + + private static final int BATCH_SIZE = 2; + private static final List<OzgCloudVorgang> VORGAENGE_1 = List.of(OzgCloudVorgangTestFactory.create(), OzgCloudVorgangTestFactory.create()); + private static final List<OzgCloudVorgang> VORGAENGE_2 = List.of(OzgCloudVorgangTestFactory.create()); + private static final UUID BATCH_1_UUID = UUID.randomUUID(); + private static final UUID BATCH_2_UUID = UUID.randomUUID(); + + @Mock + private Function<Page, List<OzgCloudVorgang>> functionToRetrieveData; + + @SuppressWarnings("unchecked") + @BeforeEach + void init() { + aggregationLoader = spy(aggregationLoaderBuilder.batchSize(BATCH_SIZE).build()); + when(functionToRetrieveData.apply(any())).thenReturn(VORGAENGE_1, VORGAENGE_2, Collections.emptyList()); + when(aggregationLoader.createBatchUUID()).thenReturn(BATCH_1_UUID, BATCH_2_UUID); + } + + @Test + void shouldCallFunctionToRetrieveDataForFirstPage() { + extractBatchesFromDataSource(); + + verify(functionToRetrieveData).apply(argThat(hasExpectedOffsetAndConfiguredLimit(0))); + } + + @Test + void shouldCallFunctionToRetrieveDataForSecondPage() { + extractBatchesFromDataSource().toList(); + + verify(functionToRetrieveData).apply(argThat(hasExpectedOffsetAndConfiguredLimit(BATCH_SIZE))); + } + + @Test + void shouldCallFunctionToRetrieveDataForThirdPage() { + extractBatchesFromDataSource().toList(); + + verify(functionToRetrieveData).apply(argThat(hasExpectedOffsetAndConfiguredLimit(BATCH_SIZE * 2))); + } + + @Test + void shouldReturnBatches() { + var batches = extractBatchesFromDataSource().toList(); + + assertThat(batches).hasSize(2).usingRecursiveFieldByFieldElementComparator().containsExactly( + new Batch(execution, BATCH_1_UUID, Page.builder().offset(0).limit(BATCH_SIZE).build(), VORGAENGE_1), + new Batch(execution, BATCH_2_UUID, Page.builder().offset(BATCH_SIZE).limit(BATCH_SIZE).build(), VORGAENGE_2)); + } + + private ArgumentMatcher<Page> hasExpectedOffsetAndConfiguredLimit(int expectedOffset) { + return page -> page.getOffset() == expectedOffset && page.getLimit() == BATCH_SIZE; + } + + private Stream<Batch> extractBatchesFromDataSource() { + return aggregationLoader.extractBatchesFromDataSource(functionToRetrieveData); + } + } + + @Nested + class TestLoadVorgaengeIntoRepository { + + @Mock + private Execution execution; + @Mock + private Batch batch; + private final DocumentEntry documentEntry = DocumentEntryTestFactory.create(); + private final String collectionName = LoremIpsum.getInstance().getWords(1); + @Captor + private ArgumentCaptor<List<DocumentEntry>> documentEntriesCaptor; + + @BeforeEach + void init() { + aggregationLoader = spy(aggregationLoaderBuilder.collectionName(collectionName).build()); + doReturn(List.of(documentEntry)).when(aggregationLoader).transformBatchToDocumentEntries(any()); + doNothing().when(aggregationLoader).loadDocumentEntriesIntoRepository(any()); + } + + @Test + void shouldDropCollection() { + loadVorgaengeIntoRepository(); + + verify(repository).clearCollection(collectionName); + } + + @Test + void shouldTransform() { + loadVorgaengeIntoRepository(); + + verify(aggregationLoader).transformBatchToDocumentEntries(batch); + } + + @Test + void shouldLoadIntoRepository() { + loadVorgaengeIntoRepository(); + + verify(aggregationLoader).loadDocumentEntriesIntoRepository(documentEntriesCaptor.capture()); + assertThat(documentEntriesCaptor.getValue()).containsExactly(documentEntry); + } + + private void loadVorgaengeIntoRepository() { + aggregationLoader.loadVorgaengeIntoRepository(Stream.of(batch)); + } + } + + @Nested + class TestLoadDocumentEntriesIntoRepository { + + private final List<DocumentEntry> documentEntries = List.of(DocumentEntryTestFactory.create()); + private final String collectionName = LoremIpsum.getInstance().getWords(1); + + @BeforeEach + void init() { + aggregationLoader = spy(aggregationLoaderBuilder.collectionName(collectionName).build()); + } + + @Test + void shouldSaveDocumentEntriesInCollection() { + aggregationLoader.loadDocumentEntriesIntoRepository(documentEntries); + + verify(repository).saveAllInCollection(documentEntries, collectionName); + } + } +} diff --git a/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java b/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java index fde2d1ad0d8cc5ec8e87d724c7ea4a76fae3bb4a..5579862b69c7b721a0ef0a878be1456b3ba9383e 100644 --- a/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java +++ b/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java @@ -29,41 +29,26 @@ import static org.mockito.Mockito.*; import java.util.Collections; import java.util.List; -import java.util.UUID; -import java.util.function.Function; -import java.util.stream.Stream; +import org.apache.commons.lang3.RandomUtils; 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.ArgumentMatcher; -import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; +import org.springframework.test.util.ReflectionTestUtils; import com.thedeanda.lorem.LoremIpsum; -import de.ozgcloud.aggregation.AggregationManagerRunner.Batch; -import de.ozgcloud.aggregation.AggregationManagerRunner.Execution; import de.ozgcloud.aggregation.transformation.AggregationMapping; -import de.ozgcloud.aggregation.transformation.AggregationMapping.FormIdentifier; import de.ozgcloud.aggregation.transformation.AggregationMappingTestFactory; -import de.ozgcloud.aggregation.transformation.FormIdentifierTestFactory; import de.ozgcloud.aggregation.transformation.Transformation; import de.ozgcloud.aggregation.transformation.TransformationService; import de.ozgcloud.aggregation.transformation.VorgangMapper; import de.ozgcloud.aggregation.warehouse.DocumentEntry; -import de.ozgcloud.aggregation.warehouse.DocumentEntryTestFactory; import de.ozgcloud.aggregation.warehouse.WarehouseRepository; -import de.ozgcloud.apilib.vorgang.OzgCloudVorgang; -import de.ozgcloud.apilib.vorgang.OzgCloudVorgangQuery; import de.ozgcloud.apilib.vorgang.OzgCloudVorgangService; -import de.ozgcloud.apilib.vorgang.OzgCloudVorgangStub; -import de.ozgcloud.apilib.vorgang.OzgCloudVorgangStubTestFactory; -import de.ozgcloud.apilib.vorgang.OzgCloudVorgangTestFactory; -import de.ozgcloud.apilib.vorgang.Page; class AggregationManagerRunnerTest { @@ -134,8 +119,8 @@ class AggregationManagerRunnerTest { void shouldRunWithTransformationForEachTransformation() { runner.run(); - verify(runner).runWithTransformation(firstTransformation, AggregationMappingTestFactory.FORM_IDENTIFIER); - verify(runner).runWithTransformation(secondTransformation, AggregationMappingTestFactory.FORM_IDENTIFIER); + verify(runner).runWithTransformation(firstTransformation, aggregationMappings.get(0)); + verify(runner).runWithTransformation(secondTransformation, aggregationMappings.get(1)); } } @@ -197,381 +182,116 @@ class AggregationManagerRunnerTest { @Mock private Transformation transformation; - private final ArgumentMatcher<Execution> hasTransformation = execution -> execution.getTransformation().equals(transformation); - private final FormIdentifier formIdentifier = FormIdentifierTestFactory.create(); + private final AggregationMapping aggregationMapping = AggregationMappingTestFactory.create(); @Mock - private Batch batchOfVorgaenge; - @Captor - private ArgumentCaptor<Stream<Batch>> batchStreamCaptor; + private AggregationLoader aggregationLoader; @BeforeEach - void init() { - doReturn(Stream.of(batchOfVorgaenge)).when(runner).extractBatchesOfVorgaengeFromDataSource(any(), any()); - doNothing().when(runner).loadVorgaengeIntoRepository(any()); - } - - @Test - void shouldExtractBatchesOfVorgaengeFromDataSource() { - runner.runWithTransformation(transformation, formIdentifier); - - verify(runner).extractBatchesOfVorgaengeFromDataSource(argThat(hasTransformation), eq(formIdentifier)); - } - - @Nested - class TestOnFormIdentifierIsNull { - - @Mock - private Batch batchOfDeletedVorgaenge; - - @BeforeEach - void mock() { - doReturn(Stream.of(batchOfDeletedVorgaenge)).when(runner).extractBatchesOfDeletedVorgaengeFromDataSource(any()); - } - - @Test - void shouldExtractBatchesOfDeletedVorgaengeFromDataSource() { - runner.runWithTransformation(transformation, null); - - verify(runner).extractBatchesOfDeletedVorgaengeFromDataSource(argThat(hasTransformation)); - } - - @Test - void shouldLoadVorgaengeIntoRepository() { - runner.runWithTransformation(transformation, null); - - verify(runner).loadVorgaengeIntoRepository(batchStreamCaptor.capture()); - assertThat(batchStreamCaptor.getValue()).containsExactly(batchOfVorgaenge, batchOfDeletedVorgaenge); - } - } - - @Nested - class TestOnFormIdentifierIsNotNull { - - @Test - void shouldNotExtractBatchesOfDeletedVorgaengeFromDataSource() { - runner.runWithTransformation(transformation, formIdentifier); - - verify(runner, never()).extractBatchesOfDeletedVorgaengeFromDataSource(any()); - } - - @Test - void shouldLoadVorgaengeIntoRepository() { - runner.runWithTransformation(transformation, formIdentifier); - - verify(runner).loadVorgaengeIntoRepository(batchStreamCaptor.capture()); - assertThat(batchStreamCaptor.getValue()).containsExactly(batchOfVorgaenge); - } - } - } - - @Nested - class TestLoadVorgaengeIntoRepository { - - @Mock - private Execution execution; - @Mock - private Batch batch; - private final DocumentEntry documentEntry = DocumentEntryTestFactory.create(); - @Captor - private ArgumentCaptor<Stream<DocumentEntry>> documentEntriesCaptor; - - @BeforeEach - void init() { - doReturn(Stream.of(documentEntry)).when(runner).transformBatchToDocumentEntries(any()); - doNothing().when(runner).loadDocumentEntriesIntoRepository(any()); - } - - @Test - void shouldDropCollection() { - loadVorgaengeIntoRepository(); - - verify(repository).deleteAll(); + void mock() { + when(runner.buildAggregationLoader(transformation, aggregationMapping)).thenReturn(aggregationLoader); } @Test - void shouldTransform() { - loadVorgaengeIntoRepository(); + void shouldCallBuildAggregationLoader() { + runWithTransformation(); - verify(runner).transformBatchToDocumentEntries(batch); + verify(runner).buildAggregationLoader(transformation, aggregationMapping); } @Test - void shouldLoadIntoRepository() { - loadVorgaengeIntoRepository(); + void shouldLoadAggregation() { + runWithTransformation(); - verify(runner).loadDocumentEntriesIntoRepository(documentEntriesCaptor.capture()); - assertThat(documentEntriesCaptor.getValue()).containsExactly(documentEntry); + verify(aggregationLoader).load(); } - private void loadVorgaengeIntoRepository() { - runner.loadVorgaengeIntoRepository(Stream.of(batch)); + private void runWithTransformation() { + runner.runWithTransformation(transformation, aggregationMapping); } } @Nested - class TestExtractBatchesOfVorgaengeFromDataSource { + class TestBuildAggregationLoader { - private final FormIdentifier formIdentifier = FormIdentifierTestFactory.create(); - @Mock - private Execution execution; - @Captor - private ArgumentCaptor<Function<Page, List<OzgCloudVorgang>>> functionToRetrieveDataCaptor; - @Mock - private Batch batch; @Mock - private Page page; + private Transformation transformation; + private final AggregationMapping aggregationMapping = AggregationMappingTestFactory.create(); + private final int batchSize = RandomUtils.insecure().randomInt(); @BeforeEach - void init() { - doReturn(Stream.of(batch)).when(runner).extractBatchesFromDataSource(any(), any()); + void mock() { + when(config.getFetchingBatchSize()).thenReturn(batchSize); } @Test - void shouldExtract() { - runner.extractBatchesOfVorgaengeFromDataSource(execution, formIdentifier); + void shouldGetBatchSize() { + runner.buildAggregationLoader(transformation, aggregationMapping); - verify(runner).extractBatchesFromDataSource(eq(execution), any()); + verify(config).getFetchingBatchSize(); } @Test - void shouldExtractWithDataRetrievalFunction() { - runner.extractBatchesOfVorgaengeFromDataSource(execution, formIdentifier); + void shouldReturnAggregationLoaderWithExecution() { + var loader = runner.buildAggregationLoader(transformation, aggregationMapping); - verify(runner).extractBatchesFromDataSource(eq(execution), functionToRetrieveDataCaptor.capture()); - functionToRetrieveDataCaptor.getValue().apply(page); - verify(runner).getVorgaengeFromDataSource(page, formIdentifier); + assertThat(ReflectionTestUtils.getField(loader, "execution")).extracting("transformation").isEqualTo(transformation); } @Test - void shouldReturnExtractedBatches() { - var extracted = runner.extractBatchesOfVorgaengeFromDataSource(execution, formIdentifier); + void shouldReturnAggregationLoaderWithFormIdentifier() { + var loader = runner.buildAggregationLoader(transformation, aggregationMapping); - assertThat(extracted).containsExactly(batch); - } - } - - @Nested - class TestGetVorgaengeFromDataSource { - - private final Page page = Page.builder().offset(10).limit(2).build(); - private final FormIdentifier formIdentifier = FormIdentifierTestFactory.create(); - private final OzgCloudVorgangQuery query = OzgCloudVorgangQuery.builder().build(); - - @BeforeEach - void init() { - doReturn(query).when(runner).buildFindByFormEngineQuery(any()); - when(vorgangService.find(any(), any())).thenReturn(List.of(OzgCloudVorgangStubTestFactory.create())); - when(vorgangService.getById(any())).thenReturn(OzgCloudVorgangTestFactory.create()); + assertThat(ReflectionTestUtils.getField(loader, "formIdentifier")).isEqualTo(AggregationMappingTestFactory.FORM_IDENTIFIER); } @Test - void shouldCallBuildFindByFormEngineQuery() { - getVorgaengeFromDataSource(); + void shouldReturnAggregationLoaderWithCollectionName() { + var loader = runner.buildAggregationLoader(transformation, aggregationMapping); - verify(runner).buildFindByFormEngineQuery(formIdentifier); + assertThat(ReflectionTestUtils.getField(loader, "collectionName")).isEqualTo(AggregationMappingTestFactory.NAME); } @Test - void shouldCallVorgangService() { - getVorgaengeFromDataSource(); + void shouldReturnAggregationLoaderWithVorgangService() { + var loader = runner.buildAggregationLoader(transformation, aggregationMapping); - verify(vorgangService).find(query, page); + assertThat(ReflectionTestUtils.getField(loader, "vorgangService")).isEqualTo(vorgangService); } @Test - void shouldGetVorgangDetails() { - getVorgaengeFromDataSource(); + void shouldReturnAggregationLoaderWithRepository() { + var loader = runner.buildAggregationLoader(transformation, aggregationMapping); - verify(vorgangService).getById(OzgCloudVorgangTestFactory.ID); + assertThat(ReflectionTestUtils.getField(loader, "repository")).isEqualTo(repository); } @Test - void shouldReturnVorgangDetails() { - var vorgaenge = getVorgaengeFromDataSource(); + void shouldReturnAggregationLoaderWithVorgangMapper() { + var loader = runner.buildAggregationLoader(transformation, aggregationMapping); - assertThat(vorgaenge).usingRecursiveFieldByFieldElementComparator().containsExactly(OzgCloudVorgangTestFactory.create()); - } - - private List<OzgCloudVorgang> getVorgaengeFromDataSource() { - return runner.getVorgaengeFromDataSource(page, formIdentifier); - } - } - - @Nested - class TestBuildFindByFormEngineQuery { - - @Nested - class TestOnFormIdentifierNull { - @Test - void shouldReturnFindAllQueryOnNullFormIdentifier() { - var query = runner.buildFindByFormEngineQuery(null); - - assertThat(query).usingRecursiveComparison().isEqualTo(OzgCloudVorgangQuery.builder().build()); - } - } - - @Nested - class TestOnFormIdentifierNotNull { - private final FormIdentifier formIdentifier = FormIdentifierTestFactory.create(); - - @Test - void shouldSetFormIdInQuery() { - var query = runner.buildFindByFormEngineQuery(formIdentifier); - - assertThat(query.getForm()).get().extracting("formId").isEqualTo(FormIdentifierTestFactory.FORM_ID); - } - - @Test - void shouldSetFormEngineNameInQuery() { - var query = runner.buildFindByFormEngineQuery(formIdentifier); - - assertThat(query.getForm()).get().extracting("formEngineName").isEqualTo(FormIdentifierTestFactory.FORM_ENGINE_NAME); - } - } - } - - @Nested - class TestExtractBatchesOfDeletedVorgaengeFromDataSource { - - @Mock - private Execution execution; - @Mock - private Function<Page, List<OzgCloudVorgang>> functionToRetrieveData; - private final List<OzgCloudVorgangStub> deletedVorgaenge = List.of(OzgCloudVorgangStubTestFactory.create()); - @Captor - private ArgumentCaptor<Stream<OzgCloudVorgangStub>> deletedVorgaengeCaptor; - @Mock - private Batch batch; - - @BeforeEach - void init() { - when(vorgangService.findDeleted()).thenReturn(deletedVorgaenge.stream()); - doReturn(functionToRetrieveData).when(runner).getPagedDeletedVorgaenge(any()); - doReturn(Stream.of(batch)).when(runner).extractBatchesFromDataSource(any(), any()); + assertThat(ReflectionTestUtils.getField(loader, "vorgangMapper")).isEqualTo(vorgangMapper); } @Test - void shouldFindDeleted() { - runner.extractBatchesOfDeletedVorgaengeFromDataSource(execution); + void shouldReturnAggregationLoaderWithBatchSize() { + var loader = runner.buildAggregationLoader(transformation, aggregationMapping); - verify(vorgangService).findDeleted(); + assertThat(ReflectionTestUtils.getField(loader, "batchSize")).isEqualTo(batchSize); } @Test - void shouldGetPagedDeletedVorgaenge() { - runner.extractBatchesOfDeletedVorgaengeFromDataSource(execution); - - verify(runner).getPagedDeletedVorgaenge(deletedVorgaengeCaptor.capture()); - assertThat(deletedVorgaengeCaptor.getValue()).usingRecursiveFieldByFieldElementComparator().containsExactlyElementsOf(deletedVorgaenge); - } - - @Test - void shouldExtractWithDataRetrievalFunction() { - runner.extractBatchesOfDeletedVorgaengeFromDataSource(execution); - - verify(runner).extractBatchesFromDataSource(execution, functionToRetrieveData); - } - - @Test - void shouldReturnExtractedBatches() { - var extracted = runner.extractBatchesOfDeletedVorgaengeFromDataSource(execution); - - assertThat(extracted).containsExactly(batch); - } - } - - @Nested - class TestGetPagedDeletedVorgaenge { - - private static final int PAGE_SIZE = 2; - - private final Stream<OzgCloudVorgangStub> vorgangStubs = Stream.generate(OzgCloudVorgangStubTestFactory::create).limit(PAGE_SIZE + 1); - - @Test - void shouldReturnFirstFullPage() { - var pagingFunction = runner.getPagedDeletedVorgaenge(vorgangStubs); + void shouldReturnAggregationLoaderWithNullFormIdentifier() { + var loader = runner.buildAggregationLoader(transformation, null); - var vorgaenge = getFirstPage(pagingFunction); - - assertThat(vorgaenge).hasSize(PAGE_SIZE); + assertThat(ReflectionTestUtils.getField(loader, "formIdentifier")).isNull(); } @Test - void shouldReturnSecondPageWithOneElement() { - var pagingFunction = runner.getPagedDeletedVorgaenge(vorgangStubs); - getFirstPage(pagingFunction); - - var vorgaenge = getSecondPage(pagingFunction); + void shouldReturnAggregationLoaderWithDefaultCollectionName() { + var loader = runner.buildAggregationLoader(transformation, null); - assertThat(vorgaenge).hasSize(1); - } - - private List<OzgCloudVorgang> getFirstPage(Function<Page, List<OzgCloudVorgang>> pagingFunction) { - return pagingFunction.apply(Page.builder().offset(0).limit(PAGE_SIZE).build()); - } - - private List<OzgCloudVorgang> getSecondPage(Function<Page, List<OzgCloudVorgang>> pagingFunction) { - return pagingFunction.apply(Page.builder().offset(PAGE_SIZE).limit(PAGE_SIZE).build()); + assertThat(ReflectionTestUtils.getField(loader, "collectionName")).isEqualTo(DocumentEntry.COLLECTION); } } - @Nested - class TestExtractBatchesFromDataSource { - - private static final int BATCH_SIZE = 2; - private static final List<OzgCloudVorgang> VORGAENGE_1 = List.of(OzgCloudVorgangTestFactory.create(), OzgCloudVorgangTestFactory.create()); - private static final List<OzgCloudVorgang> VORGAENGE_2 = List.of(OzgCloudVorgangTestFactory.create()); - private static final UUID BATCH_1_UUID = UUID.randomUUID(); - private static final UUID BATCH_2_UUID = UUID.randomUUID(); - - @Mock - private Execution execution; - @Mock - private Function<Page, List<OzgCloudVorgang>> functionToRetrieveData; - - @BeforeEach - void init() { - when(config.getFetchingBatchSize()).thenReturn(BATCH_SIZE); - when(functionToRetrieveData.apply(any())).thenReturn(VORGAENGE_1, VORGAENGE_2, List.of()); - when(runner.createBatchUUID()).thenReturn(BATCH_1_UUID, BATCH_2_UUID); - } - - @Test - void shouldCallFunctionToRetrieveDataForFirstPage() { - extractBatchesFromDataSource(); - - verify(functionToRetrieveData).apply(argThat(hasExpectedOffsetAndConfiguredLimit(0))); - } - - @Test - void shouldCallFunctionToRetrieveDataForSecondPage() { - extractBatchesFromDataSource().toList(); - - verify(functionToRetrieveData).apply(argThat(hasExpectedOffsetAndConfiguredLimit(BATCH_SIZE))); - } - - @Test - void shouldCallFunctionToRetrieveDataForThirdPage() { - extractBatchesFromDataSource().toList(); - - verify(functionToRetrieveData).apply(argThat(hasExpectedOffsetAndConfiguredLimit(BATCH_SIZE * 2))); - } - - @Test - void shouldReturnBatches() { - var batches = extractBatchesFromDataSource().toList(); - - assertThat(batches).hasSize(2).usingRecursiveFieldByFieldElementComparator().containsExactly( - new Batch(execution, BATCH_1_UUID, Page.builder().offset(0).limit(BATCH_SIZE).build(), VORGAENGE_1), - new Batch(execution, BATCH_2_UUID, Page.builder().offset(BATCH_SIZE).limit(BATCH_SIZE).build(), VORGAENGE_2)); - } - - private ArgumentMatcher<Page> hasExpectedOffsetAndConfiguredLimit(int expectedOffset) { - return page -> page.getOffset() == expectedOffset && page.getLimit() == BATCH_SIZE; - } - - private Stream<Batch> extractBatchesFromDataSource() { - return runner.extractBatchesFromDataSource(execution, functionToRetrieveData); - } - } } diff --git a/src/test/java/de/ozgcloud/aggregation/transformation/AggregationMappingTestFactory.java b/src/test/java/de/ozgcloud/aggregation/transformation/AggregationMappingTestFactory.java index 4be105f1284cafaef138f184ae54625242ad50e5..44a5c25ce128edf4625a5b95ab27d772bb621c46 100644 --- a/src/test/java/de/ozgcloud/aggregation/transformation/AggregationMappingTestFactory.java +++ b/src/test/java/de/ozgcloud/aggregation/transformation/AggregationMappingTestFactory.java @@ -23,6 +23,8 @@ */ package de.ozgcloud.aggregation.transformation; +import com.thedeanda.lorem.LoremIpsum; + import de.ozgcloud.aggregation.transformation.AggregationMapping.AggregationMappingBuilder; import de.ozgcloud.aggregation.transformation.AggregationMapping.FieldMapping; import de.ozgcloud.aggregation.transformation.AggregationMapping.FormIdentifier; @@ -31,6 +33,7 @@ public class AggregationMappingTestFactory { public static final FormIdentifier FORM_IDENTIFIER = FormIdentifierTestFactory.create(); public static final FieldMapping MAPPING = FieldMappingTestFactory.create(); + public static final String NAME = LoremIpsum.getInstance().getWords(1); public static AggregationMapping create() { return createBuilder().build(); @@ -39,6 +42,7 @@ public class AggregationMappingTestFactory { public static AggregationMappingBuilder createBuilder() { return AggregationMapping.builder() .formIdentifier(FORM_IDENTIFIER) - .fieldMapping(MAPPING); + .fieldMapping(MAPPING) + .name(NAME); } } diff --git a/src/test/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImplTest.java b/src/test/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImplTest.java index d3ea33cdd1f5ee322284fac11efb5377c9e459b0..194a372bc2330c39c339baab737c0bf879fba26e 100644 --- a/src/test/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImplTest.java +++ b/src/test/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImplTest.java @@ -1,24 +1,54 @@ package de.ozgcloud.aggregation.warehouse; import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.util.List; + import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.springframework.data.mongodb.core.MongoTemplate; import com.thedeanda.lorem.LoremIpsum; class CustomWarehouseRepositoryImplTest { + @Spy @InjectMocks private CustomWarehouseRepositoryImpl repository; @Mock private MongoTemplate mongoTemplate; + @Nested + class TestSaveAllInCollection { + + private final DocumentEntry documentEntry = DocumentEntryTestFactory.create(); + private final List<DocumentEntry> documentEntries = List.of(documentEntry); + private final String collectionName = LoremIpsum.getInstance().getWords(1); + + @Test + void shouldCallSaveInCollection() { + repository.saveAllInCollection(documentEntries, collectionName); + + verify(repository).saveInCollection(documentEntry, collectionName); + } + + @Test + void shouldReturnSavedDocumentEntry() { + var savedDocumentEntry = DocumentEntryTestFactory.create(); + doReturn(savedDocumentEntry).when(repository).saveInCollection(any(), any()); + + var returnedDocumentEntries = repository.saveAllInCollection(documentEntries, collectionName); + + assertThat(returnedDocumentEntries).containsExactly(savedDocumentEntry); + } + } + @Nested class TestSaveInCollection {