diff --git a/pom.xml b/pom.xml index 11774d31e3a7e94b242b60584d061966167dc89d..ae94f89fffaf1a8fcdb6ad3f7349a46f32923d45 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ <inceptionYear>2024</inceptionYear> <properties> - <ozgcloud.api-lib.version>0.17.0</ozgcloud.api-lib.version> + <ozgcloud.api-lib.version>0.18.0-SNAPSHOT</ozgcloud.api-lib.version> <jslt.version>0.1.14</jslt.version> <hibernate-validator.version>8.0.2.Final</hibernate-validator.version> <spring-boot.build-image.imageName>docker.ozg-sh.de/aggregation-manager:build-latest</spring-boot.build-image.imageName> diff --git a/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java b/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java index c08af99ce4121083c767fdb8ba3adb4d5503c0ef..3629c317ecacf3a46c778bab7472af4c11fea46b 100644 --- a/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java +++ b/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java @@ -26,8 +26,12 @@ package de.ozgcloud.aggregation; import java.util.List; import java.util.Objects; 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; @@ -39,12 +43,15 @@ import org.springframework.stereotype.Component; 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.OzgCloudVorgangService; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgangStub; import de.ozgcloud.apilib.vorgang.Page; +import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -60,14 +67,13 @@ public class AggregationManagerRunner implements CommandLineRunner { private static final Predicate<Batch> HAS_NEXT_BATCH = x -> !x.items.isEmpty(); private final OzgCloudVorgangService vorgangService; - private final AggregationManagerConfiguration config; private final TransformationProperties transformationProperties; private final TransformationService transformationService; - private final WarehouseRepository repository; + private final VorgangMapper vorgangMapper; @Override public void run(String... args) throws TransformationException { @@ -85,34 +91,72 @@ public class AggregationManagerRunner implements CommandLineRunner { void runWithTransformation(Transformation transformation) { try (Execution execution = new Execution(transformation)) { ThreadContext.put(MDC_EXECUTION, execution.id.toString()); - extractBatchesFromDataSource(execution).map(this::transformBatches).forEach(this::loadBatchesIntoRepository); + loadVorgaengeIntoRepository(Stream.concat( + extractBatchesOfVorgaengeFromDataSource(execution), + extractBatchesOfDeletedVorgaengeFromDataSource(execution))); } finally { ThreadContext.remove(MDC_EXECUTION); } } - Stream<Batch> extractBatchesFromDataSource(Execution execution) { + void loadVorgaengeIntoRepository(Stream<Batch> batches) { + batches.map(this::transformBatchToDocumentEntries).forEach(this::loadDocumentEntriesIntoRepository); + } + + Stream<Batch> extractBatchesOfVorgaengeFromDataSource(Execution execution) { + return extractBatchesFromDataSource(execution, this::getVorgaengeFromDataSource); + } + + List<OzgCloudVorgang> getVorgaengeFromDataSource(Page page) { + return vorgangService.find(buildFindAllQuery(), page).stream() + .map(vorgangStub -> vorgangService.getById(vorgangStub.getId())) + .toList(); + } + + OzgCloudVorgangQuery buildFindAllQuery() { + return OzgCloudVorgangQuery.builder().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, 0, fetchSize); - UnaryOperator<Batch> nextBatch = x -> createBatch(execution, x.page.getOffset() + fetchSize, fetchSize); + 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, int offset, int size) { - var page = Page.builder().offset(offset).limit(size).build(); - var matchAll = OzgCloudVorgangQuery.builder().build(); - var data = vorgangService.find(matchAll, page).stream().map(e -> vorgangService.getById(e.getId())) - .toList(); - return new Batch(execution, UUID.randomUUID(), page, data); + 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 loadBatchesIntoRepository(Stream<DocumentEntry> entries) { + 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> transformBatches(Batch batch) { + Stream<DocumentEntry> transformBatchToDocumentEntries(Batch batch) { return batch.items.stream().map(vorgang -> transformWithinBatch(batch, vorgang)).filter(Objects::nonNull); } @@ -121,7 +165,7 @@ public class AggregationManagerRunner implements CommandLineRunner { 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); + vorgang::getId, () -> batch.id, () -> batch.execution.id); return null; } } @@ -129,6 +173,7 @@ public class AggregationManagerRunner implements CommandLineRunner { @RequiredArgsConstructor protected static class Execution implements AutoCloseable { private final UUID id = UUID.randomUUID(); + @Getter private final Transformation transformation; @Override diff --git a/src/main/java/de/ozgcloud/aggregation/transformation/JSLTransformationService.java b/src/main/java/de/ozgcloud/aggregation/transformation/JSLTransformationService.java index 8575a6b07fb001f35e6d289633ed9032506c5994..51a81b9f543d4beae28614c8e8f969a61ed26a6f 100644 --- a/src/main/java/de/ozgcloud/aggregation/transformation/JSLTransformationService.java +++ b/src/main/java/de/ozgcloud/aggregation/transformation/JSLTransformationService.java @@ -47,12 +47,12 @@ import com.schibsted.spt.data.jslt.impl.PairExpression; import de.ozgcloud.aggregation.transformation.AggregationMapping.FieldMapping; import lombok.RequiredArgsConstructor; -@Service -@RequiredArgsConstructor /** * JSTL implementation of the {@link TransformationService}. This service * internally uses JSLT {@link Expression} for {@link Transformation}s */ +@Service +@RequiredArgsConstructor public class JSLTransformationService implements TransformationService { private static final LetExpression[] EMPTY_TEMPLATES = new LetExpression[] {}; @@ -103,7 +103,7 @@ public class JSLTransformationService implements TransformationService { for (var field : fields) { current = new DotExpression(field, current, null); } - return Optional.of(current); + return Optional.ofNullable(current); } } diff --git a/src/main/java/de/ozgcloud/aggregation/transformation/Transformation.java b/src/main/java/de/ozgcloud/aggregation/transformation/Transformation.java index 6420f11e79f95875e349a43930c7e197476b56d2..1616f30665b1b9b223aba9204354e7a7bc9a57eb 100644 --- a/src/main/java/de/ozgcloud/aggregation/transformation/Transformation.java +++ b/src/main/java/de/ozgcloud/aggregation/transformation/Transformation.java @@ -41,6 +41,6 @@ public interface Transformation { * @throws TransformationException when the {@link Transformation} could not be * applied to the given {@link OzgCloudVorgang} */ - public DocumentEntry apply(OzgCloudVorgang vorgang) throws TransformationException; + DocumentEntry apply(OzgCloudVorgang vorgang) throws TransformationException; } diff --git a/src/main/java/de/ozgcloud/aggregation/transformation/VorgangMapper.java b/src/main/java/de/ozgcloud/aggregation/transformation/VorgangMapper.java index 606596aa88d2dbf4239272539862b3b4d3bcdb0f..04c6f87748d292fda01b599086eb5b9005c1920e 100644 --- a/src/main/java/de/ozgcloud/aggregation/transformation/VorgangMapper.java +++ b/src/main/java/de/ozgcloud/aggregation/transformation/VorgangMapper.java @@ -31,6 +31,7 @@ import de.ozgcloud.aggregation.warehouse.DocumentEntry.DocumentEntryBuilder; import de.ozgcloud.apilib.vorgang.OzgCloudVorgang; import de.ozgcloud.apilib.vorgang.OzgCloudVorgangId; import de.ozgcloud.apilib.vorgang.OzgCloudVorgangStatus; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgangStub; @Mapper public interface VorgangMapper { @@ -41,6 +42,9 @@ public interface VorgangMapper { @Mapping(target = "vorgangsname", source = "vorgangName") DocumentEntryBuilder toDocumentEntryBuilder(OzgCloudVorgang vorgang, @MappingTarget DocumentEntryBuilder builder); + @Mapping(target = "eingangs", ignore = true) + OzgCloudVorgang fromVorgangStub(OzgCloudVorgangStub vorgangStub); + default String idToString(OzgCloudVorgangId id) { return id.toString(); } diff --git a/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerITCase.java b/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerITCase.java index c108972abc8bfcb4041a02a9a10a8d3cf0d5ed96..c7ebb19305b6318b97af1787a0acb02456d4c084 100644 --- a/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerITCase.java +++ b/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerITCase.java @@ -27,6 +27,7 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.util.Collections; @@ -44,6 +45,8 @@ import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import de.ozgcloud.aggregation.warehouse.DocumentEntry; +import de.ozgcloud.aggregation.warehouse.DocumentEntryTestFactory; +import de.ozgcloud.aggregation.warehouse.ObjectNodeTestFactory; import de.ozgcloud.aggregation.warehouse.WarehouseRepository; import de.ozgcloud.apilib.vorgang.OzgCloudVorgang; import de.ozgcloud.apilib.vorgang.OzgCloudVorgangHeader; @@ -51,6 +54,7 @@ import de.ozgcloud.apilib.vorgang.OzgCloudVorgangHeaderTestFactory; import de.ozgcloud.apilib.vorgang.OzgCloudVorgangId; 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; import de.ozgcloud.common.test.DataITCase; @@ -73,8 +77,6 @@ class AggregationManagerRunnerITCase { @Nested class TestRun { - private static final OzgCloudVorgang VORGANG = TestConfig.VORGANGS.get(0); - @BeforeEach void setUp() { repository.deleteAll(); @@ -84,43 +86,73 @@ class AggregationManagerRunnerITCase { void shouldProcessAllData() { runner.run(); - assertThat(repository.count()).isEqualTo(TestConfig.NUMBER_OF_VORGAENGE); + assertThat(repository.count()).isEqualTo(TestConfig.NUMBER_OF_VORGAENGE + TestConfig.NUMBER_OF_VORGANG_STUBS); } - @Test - void shouldHaveEingangsdatum() { - runner.run(); + @Nested + class TestVorgangDocument { - var document = findVorgangDocument(); - assertThat(document.getEingangsdatum()).isCloseTo(VORGANG.getHeader().getCreatedAt(), within(1, ChronoUnit.MILLIS)); - } + private static final OzgCloudVorgang VORGANG = TestConfig.VORGANGS.getFirst(); - @Test - void shouldHaveVorgangName() { - runner.run(); + @Test + void shouldHaveEingangsdatum() { + runner.run(); - var document = findVorgangDocument(); - assertThat(document.getVorgangsname()).isEqualTo(VORGANG.getVorgangName()); - } + var document = findVorgangDocument(); + assertThat(document.getEingangsdatum()).isCloseTo(VORGANG.getHeader().getCreatedAt(), within(1, ChronoUnit.MILLIS)); + } - @Test - void shouldHaveStatus() { - runner.run(); + @Test + void shouldHaveVorgangName() { + runner.run(); - var document = findVorgangDocument(); - assertThat(document.getStatus()).isEqualTo(VORGANG.getHeader().getStatus().toString()); - } + var document = findVorgangDocument(); + assertThat(document.getVorgangsname()).isEqualTo(VORGANG.getVorgangName()); + } - @Test - void shouldNotHaveAnyPayload() { - runner.run(); + @Test + void shouldHaveStatus() { + runner.run(); + + var document = findVorgangDocument(); + assertThat(document.getStatus()).isEqualTo(VORGANG.getHeader().getStatus().toString()); + } + + @Test + void shouldNotHaveAnyPayload() { + runner.run(); - var document = findVorgangDocument(); - assertThat(document.getPayload()).isEmpty(); + var document = findVorgangDocument(); + assertThat(document.getPayload()).isEmpty(); + } + + private DocumentEntry findVorgangDocument() { + return repository.findById(VORGANG.getId().toString()).get(); + } } - private DocumentEntry findVorgangDocument() { - return repository.findById(VORGANG.getId().toString()).get(); + @Nested + class TestVorgangStubDocument { + + private static final OzgCloudVorgangStub VORGANG_STUB = TestConfig.VORGANG_STUBS.getFirst(); + + @Test + void shouldHaveValues() { + var expected = DocumentEntryTestFactory.createBuilder() + .id(VORGANG_STUB.getId().toString()) + .eingangsdatum(DocumentEntryTestFactory.EINGANGSDATUM.withZoneSameInstant(ZoneId.systemDefault())) + .payload(ObjectNodeTestFactory.create().removeAll()) + .build(); + + runner.run(); + + assertThat(findVorgangStubDocument()).usingRecursiveComparison().isEqualTo(expected); + + } + + private DocumentEntry findVorgangStubDocument() { + return repository.findById(VORGANG_STUB.getId().toString()).get(); + } } } @@ -130,9 +162,12 @@ class AggregationManagerRunnerITCase { private static final int NUMBER_OF_VORGAENGE = 10; public static final List<OzgCloudVorgang> VORGANGS = createVorgaenge(); + private static final int NUMBER_OF_VORGANG_STUBS = 5; + public static final List<OzgCloudVorgangStub> VORGANG_STUBS = createVorgangStubs(); + private static List<OzgCloudVorgang> createVorgaenge() { return IntStream.range(0, NUMBER_OF_VORGAENGE) - .mapToObj(i -> createVorgang(i)) + .mapToObj(TestConfig::createVorgang) .toList(); } @@ -147,7 +182,7 @@ class AggregationManagerRunnerITCase { private static OzgCloudVorgangHeader createHeader(int i) { return OzgCloudVorgangHeaderTestFactory.createBuilder() - .createdAt(ZonedDateTime.now().minus(1, ChronoUnit.DAYS)) + .createdAt(ZonedDateTime.now().minusDays(1)) .aktenzeichen("akt-" + i).build(); } @@ -156,6 +191,16 @@ class AggregationManagerRunnerITCase { .vorgangNummer(vorgang.getVorgangNummer()).header(vorgang.getHeader()).build(); } + private static List<OzgCloudVorgangStub> createVorgangStubs() { + return IntStream.range(0, NUMBER_OF_VORGANG_STUBS).mapToObj(TestConfig::createVorgangStub).toList(); + } + + private static OzgCloudVorgangStub createVorgangStub(int i) { + return OzgCloudVorgangStubTestFactory.createBuilder() + .id(OzgCloudVorgangId.from("stub-id" + i)) + .build(); + } + @Bean @Primary OzgCloudVorgangService vorgangService() { @@ -176,6 +221,8 @@ class AggregationManagerRunnerITCase { .thenAnswer(i -> VORGANGS.stream().filter(vorgang -> vorgang.getId().equals(i.getArgument(0))) .findFirst().orElse(null)); + when(vorgangService.findDeleted()).thenAnswer(i -> VORGANG_STUBS.stream()); + return vorgangService; } diff --git a/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java b/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java index 95e5495c5c4aed3efe3bcc4e2c51281e7c1643e5..b9e7f4c3aa06073458d6485d9281d0af9d427ca2 100644 --- a/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java +++ b/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java @@ -23,36 +23,63 @@ */ 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.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; 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.AggregationMappingTestFactory; 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 { - @InjectMocks - @Spy - private AggregationManagerRunner runner; - + @Mock + private OzgCloudVorgangService vorgangService; + @Mock + private AggregationManagerConfiguration config; @Mock private TransformationProperties transformationProperties; @Mock private TransformationService transformationService; + @Mock + private WarehouseRepository repository; + @Mock + private VorgangMapper vorgangMapper; + @Spy + @InjectMocks + private AggregationManagerRunner runner; @Nested class TestRun { @@ -162,4 +189,310 @@ class AggregationManagerRunnerTest { } } } + + @Nested + class TestRunWithTransformation { + + @Mock + private Transformation transformation; + private final ArgumentMatcher<Execution> hasTransformation = execution -> execution.getTransformation().equals(transformation); + @Mock + private Batch batchOfVorgaenge; + @Mock + private Batch batchOfDeletedVorgaenge; + @Captor + private ArgumentCaptor<Stream<Batch>> batchStreamCaptor; + + @BeforeEach + void init() { + doReturn(Stream.of(batchOfVorgaenge)).when(runner).extractBatchesOfVorgaengeFromDataSource(any()); + doReturn(Stream.of(batchOfDeletedVorgaenge)).when(runner).extractBatchesOfDeletedVorgaengeFromDataSource(any()); + doNothing().when(runner).loadVorgaengeIntoRepository(any()); + } + + @Test + void shouldExtractBatchesOfVorgaengeFromDataSource() { + runner.runWithTransformation(transformation); + + verify(runner).extractBatchesOfVorgaengeFromDataSource(argThat(hasTransformation)); + } + + @Test + void shouldExtractBatchesOfDeletedVorgaengeFromDataSource() { + runner.runWithTransformation(transformation); + + verify(runner).extractBatchesOfDeletedVorgaengeFromDataSource(argThat(hasTransformation)); + } + + @Test + void shouldLoadVorgaengeIntoRepository() { + runner.runWithTransformation(transformation); + + verify(runner).loadVorgaengeIntoRepository(batchStreamCaptor.capture()); + assertThat(batchStreamCaptor.getValue()).containsExactly(batchOfVorgaenge, batchOfDeletedVorgaenge); + } + } + + @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 shouldTransform() { + loadVorgaengeIntoRepository(); + + verify(runner).transformBatchToDocumentEntries(batch); + } + + @Test + void shouldLoadIntoRepository() { + loadVorgaengeIntoRepository(); + + verify(runner).loadDocumentEntriesIntoRepository(documentEntriesCaptor.capture()); + assertThat(documentEntriesCaptor.getValue()).containsExactly(documentEntry); + } + + private void loadVorgaengeIntoRepository() { + runner.loadVorgaengeIntoRepository(Stream.of(batch)); + } + } + + @Nested + class TestExtractBatchesOfVorgaengeFromDataSource { + + @Mock + private Execution execution; + @Captor + private ArgumentCaptor<Function<Page, List<OzgCloudVorgang>>> functionToRetrieveDataCaptor; + @Mock + private Batch batch; + @Mock + private Page page; + + @BeforeEach + void init() { + doReturn(Stream.of(batch)).when(runner).extractBatchesFromDataSource(any(), any()); + } + + @Test + void shouldExtract() { + runner.extractBatchesOfVorgaengeFromDataSource(execution); + + verify(runner).extractBatchesFromDataSource(eq(execution), any()); + } + + @Test + void shouldExtractWithDataRetrievalFunction() { + runner.extractBatchesOfVorgaengeFromDataSource(execution); + + verify(runner).extractBatchesFromDataSource(eq(execution), functionToRetrieveDataCaptor.capture()); + functionToRetrieveDataCaptor.getValue().apply(page); + verify(runner).getVorgaengeFromDataSource(page); + } + + @Test + void shouldReturnExtractedBatches() { + var extracted = runner.extractBatchesOfVorgaengeFromDataSource(execution); + + assertThat(extracted).containsExactly(batch); + } + } + + @Nested + class TestGetVorgaengeFromDataSource { + + private final Page page = Page.builder().offset(10).limit(2).build(); + private final OzgCloudVorgangQuery query = OzgCloudVorgangQuery.builder().build(); + + @BeforeEach + void init() { + doReturn(query).when(runner).buildFindAllQuery(); + when(vorgangService.find(any(), any())).thenReturn(List.of(OzgCloudVorgangStubTestFactory.create())); + when(vorgangService.getById(any())).thenReturn(OzgCloudVorgangTestFactory.create()); + } + + @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 runner.getVorgaengeFromDataSource(page); + } + } + + @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()); + } + + @Test + void shouldFindDeleted() { + runner.extractBatchesOfDeletedVorgaengeFromDataSource(execution); + + verify(vorgangService).findDeleted(); + } + + @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); + + var vorgaenge = getFirstPage(pagingFunction); + + assertThat(vorgaenge).hasSize(PAGE_SIZE); + } + + @Test + void shouldReturnSecondPageWithOneElement() { + var pagingFunction = runner.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 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/VorgangMapperTest.java b/src/test/java/de/ozgcloud/aggregation/transformation/VorgangMapperTest.java index cfba8feb1ffb4fc61eb59d4dd3c171a4288e042c..12c67ca70ca6101ec0ea09d3a04aa451e8322ced 100644 --- a/src/test/java/de/ozgcloud/aggregation/transformation/VorgangMapperTest.java +++ b/src/test/java/de/ozgcloud/aggregation/transformation/VorgangMapperTest.java @@ -32,6 +32,7 @@ import org.mockito.InjectMocks; import de.ozgcloud.aggregation.warehouse.DocumentEntry; import de.ozgcloud.aggregation.warehouse.DocumentEntryTestFactory; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgangStubTestFactory; import de.ozgcloud.apilib.vorgang.OzgCloudVorgangTestFactory; public class VorgangMapperTest { @@ -51,4 +52,19 @@ public class VorgangMapperTest { assertThat(documentEntry).usingRecursiveComparison().isEqualTo(expectedEntry); } } + + @Nested + class TestFromVorgangStub { + + @Test + void shouldMapAllFieldsButEingangs() { + var expected = OzgCloudVorgangTestFactory.createBuilder() + .clearEingangs() + .build(); + + var mapped = mapper.fromVorgangStub(OzgCloudVorgangStubTestFactory.create()); + + assertThat(mapped).usingRecursiveComparison().isEqualTo(expected); + } + } }