From f897ea34166a5f67859e2bd11284b16f16ed4106 Mon Sep 17 00:00:00 2001
From: Felix Reichenbach <felix.reichenbach@mgm-tp.com>
Date: Mon, 17 Mar 2025 10:33:32 +0100
Subject: [PATCH] OZG-7573 imlemet RouteForwardingGrpcService with
 EingangStubReceiverStreamObserver

---
 .../eingang/forwarder/FileService.java        |  19 ++
 .../forwarder/RouteForwardingGrpcService.java |  37 +++-
 .../forwarder/RouteForwardingService.java     |   5 +
 .../RouteForwardingGrpcServiceTest.java       | 172 +++++++++++++++---
 4 files changed, 195 insertions(+), 38 deletions(-)
 create mode 100644 forwarder/src/main/java/de/ozgcloud/eingang/forwarder/FileService.java

diff --git a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/FileService.java b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/FileService.java
new file mode 100644
index 000000000..812748eb1
--- /dev/null
+++ b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/FileService.java
@@ -0,0 +1,19 @@
+package de.ozgcloud.eingang.forwarder;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.concurrent.CompletableFuture;
+
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+@Service
+class FileService {
+
+	@Async
+	public CompletableFuture<File> saveToFile(InputStream inputStream) {
+		// TODO Auto-generated method stub
+		throw new UnsupportedOperationException("Unimplemented method 'saveToFile'");
+	}
+
+}
diff --git a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcService.java b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcService.java
index 82f8ae332..322e7361f 100644
--- a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcService.java
+++ b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcService.java
@@ -23,9 +23,12 @@
  */
 package de.ozgcloud.eingang.forwarder;
 
+import java.io.File;
+import java.io.InputStream;
+import java.util.concurrent.CompletableFuture;
+
 import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingRequest;
 import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingResponse;
-import de.ozgcloud.eingang.router.GrpcEingangMapper;
 import io.grpc.stub.StreamObserver;
 import lombok.RequiredArgsConstructor;
 import net.devh.boot.grpc.server.service.GrpcService;
@@ -34,17 +37,31 @@ import net.devh.boot.grpc.server.service.GrpcService;
 @RequiredArgsConstructor
 public class RouteForwardingGrpcService extends RouteForwardingServiceGrpc.RouteForwardingServiceImplBase {
 
-	private final RouteForwardingService service;
-	private final RouteCriteriaMapper criteriaMapper;
-
-	private final GrpcEingangMapper eingangMapper;
+	private final RouteForwardingMapper routeForwardingMapper;
+	private final IncomingFileMapper incomingFileMapper;
+	private final IncomingFileGroupMapper incomingFileGroupMapper;
+	private final RouteForwardingService routeForwardingService;
+	private final FileService fileService;
 
 	@Override
 	public StreamObserver<GrpcRouteForwardingRequest> routeForwarding(StreamObserver<GrpcRouteForwardingResponse> responseObserver) {
-		// public void routeForwarding(GrpcRouteForwardingRequest request,
-		// StreamObserver<GrpcRouteForwardingResponse> responseObserver) {
-		// service.route(criteriaMapper.fromGrpc(request.getRouteCriteria()),
-		// eingangMapper.toFormData(request.getEingang()));
-		return null;
+		return EingangStubReceiverStreamObserver.builder()
+				.fileSaver(this::saveFile)
+				.routeForwardingMapper(routeForwardingMapper)
+				.incomingFileMapper(incomingFileMapper)
+				.incomingFileGroupMapper(incomingFileGroupMapper)
+				.formDataConsumer(routeForwardingService::route)
+				.responseConsumer(repsonse -> respondWith(responseObserver, repsonse))
+				.build();
+	}
+
+	public CompletableFuture<File> saveFile(InputStream inputStream) {
+		return fileService.saveToFile(inputStream);
 	}
+
+	public void respondWith(StreamObserver<GrpcRouteForwardingResponse> responseObserver, GrpcRouteForwardingResponse response) {
+		responseObserver.onNext(response);
+		responseObserver.onCompleted();
+	}
+
 }
diff --git a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingService.java b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingService.java
index 8df1ca5ae..13058871d 100644
--- a/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingService.java
+++ b/forwarder/src/main/java/de/ozgcloud/eingang/forwarder/RouteForwardingService.java
@@ -45,4 +45,9 @@ class RouteForwardingService {
 						.build())
 				.build());
 	}
+
+	public void route(FormData formData) {
+		// TODO Auto-generated method stub
+		throw new UnsupportedOperationException("Unimplemented method 'route'");
+	}
 }
diff --git a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcServiceTest.java b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcServiceTest.java
index f4f0b4f64..35ac8140c 100644
--- a/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcServiceTest.java
+++ b/forwarder/src/test/java/de/ozgcloud/eingang/forwarder/RouteForwardingGrpcServiceTest.java
@@ -23,70 +23,186 @@
  */
 package de.ozgcloud.eingang.forwarder;
 
+import static org.assertj.core.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
-import org.junit.jupiter.api.BeforeEach;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
 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.test.util.ReflectionTestUtils;
+
+import com.thedeanda.lorem.LoremIpsum;
 
 import de.ozgcloud.eingang.common.formdata.FormData;
 import de.ozgcloud.eingang.common.formdata.FormDataTestFactory;
+import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingRequest;
 import de.ozgcloud.eingang.forwarding.GrpcRouteForwardingResponse;
-import de.ozgcloud.eingang.router.GrpcEingangMapper;
 import io.grpc.stub.StreamObserver;
 
 class RouteForwardingGrpcServiceTest {
 
 	@InjectMocks
+	@Spy
 	private RouteForwardingGrpcService service;
 
 	@Mock
 	private StreamObserver<GrpcRouteForwardingResponse> responseObserver;
 
 	@Mock
-	private RouteForwardingService routeService;
+	private RouteForwardingMapper routeForwardingMapper;
+	@Mock
+	private IncomingFileMapper incomingFileMapper;
 	@Mock
-	private RouteCriteriaMapper criteriaMapper;
+	private IncomingFileGroupMapper incomingFileGroupMapper;
 	@Mock
-	private GrpcEingangMapper eingangMapper;
+	private RouteForwardingService routeForwardingService;
+	@Mock
+	private FileService fileService;
 
 	@Nested
 	class TestRouteForwarding {
+		private static final byte[] CONTENT = LoremIpsum.getInstance().getWords(5).getBytes();
+
+		private static final ByteArrayInputStream INPUT_STREAM = new ByteArrayInputStream(CONTENT);
 
-		private final RouteCriteria routeCriteria = RouteCriteriaTestFactory.create();
 		private final FormData formData = FormDataTestFactory.create();
 
-		@BeforeEach
-		void mock() {
-			when(criteriaMapper.fromGrpc(any())).thenReturn(routeCriteria);
-			when(eingangMapper.toFormData(any())).thenReturn(formData);
+		@Test
+		void shouldSetFileSaver() {
+			var observer = service.routeForwarding(responseObserver);
+
+			callFileSaver(observer);
+			verify(service).saveFile(INPUT_STREAM);
+		}
+
+		@Test
+		void shouldSetRouteForwardingMapper() {
+			var observer = service.routeForwarding(responseObserver);
+
+			assertThat(getRouteForwardingMapper(observer)).isEqualTo(routeForwardingMapper);
+		}
+
+		@Test
+		void shouldSetIncomingFileMapper() {
+			var observer = service.routeForwarding(responseObserver);
+
+			assertThat(getIncomingFileMapper(observer)).isEqualTo(incomingFileMapper);
+		}
+
+		@Test
+		void shouldSetIncomingFileGroupMapper() {
+			var observer = service.routeForwarding(responseObserver);
+
+			assertThat(getIncomingFileGroupMapper(observer)).isEqualTo(incomingFileGroupMapper);
+		}
+
+		@Test
+		void shouldSetFormDataConsumer() {
+			var observer = service.routeForwarding(responseObserver);
+
+			callFormDataConsumer(observer, formData);
+			verify(routeForwardingService).route(formData);
+		}
+
+		@Test
+		void shouldSetResponseConsumer() {
+			var observer = service.routeForwarding(responseObserver);
+
+			callResponseConsumer(observer, GrpcRouteForwardingResponse.getDefaultInstance());
+			verify(service).respondWith(responseObserver, GrpcRouteForwardingResponse.getDefaultInstance());
+		}
+
+		@SuppressWarnings("unchecked")
+		private void callFileSaver(StreamObserver<GrpcRouteForwardingRequest> uploadObserver) {
+			var fileSaver = (Function<InputStream, CompletableFuture<File>>) ReflectionTestUtils.getField(uploadObserver, "fileSaver");
+			fileSaver.apply(INPUT_STREAM);
+		}
+
+		private RouteForwardingMapper getRouteForwardingMapper(StreamObserver<GrpcRouteForwardingRequest> uploadObserver) {
+			var routeForwardingMapper = (RouteForwardingMapper) ReflectionTestUtils.getField(uploadObserver, "routeForwardingMapper");
+			return routeForwardingMapper;
+		}
+
+		private IncomingFileMapper getIncomingFileMapper(StreamObserver<GrpcRouteForwardingRequest> uploadObserver) {
+			var incomingFileMapper = (IncomingFileMapper) ReflectionTestUtils.getField(uploadObserver, "incomingFileMapper");
+			return incomingFileMapper;
+		}
+
+		private IncomingFileGroupMapper getIncomingFileGroupMapper(StreamObserver<GrpcRouteForwardingRequest> uploadObserver) {
+			var incomingFileGroupMapper = (IncomingFileGroupMapper) ReflectionTestUtils.getField(uploadObserver, "incomingFileGroupMapper");
+			return incomingFileGroupMapper;
+		}
+
+		@SuppressWarnings("unchecked")
+		private void callFormDataConsumer(StreamObserver<GrpcRouteForwardingRequest> uploadObserver, FormData formData) {
+			var formDataConsumer = (Consumer<FormData>) ReflectionTestUtils.getField(uploadObserver, "formDataConsumer");
+			formDataConsumer.accept(formData);
+		}
+
+		@SuppressWarnings("unchecked")
+		private void callResponseConsumer(StreamObserver<GrpcRouteForwardingRequest> uploadObserver, GrpcRouteForwardingResponse response) {
+			var responseConsumer = (Consumer<GrpcRouteForwardingResponse>) ReflectionTestUtils.getField(uploadObserver, "responseConsumer");
+			responseConsumer.accept(response);
 		}
+	}
+
+	@Nested
+	class TestSaveFile {
 
-		// @Test
-		// void shouldMapCriteria() {
-		// service.routeForwarding(GrpcRouteForwardingRequestTestFactory.create(),
-		// responseObserver);
+		@Mock
+		private InputStream inputStream;
+		@Mock
+		private CompletableFuture<File> fileFuture;
 
-		// verify(criteriaMapper).fromGrpc(GrpcRouteForwardingRequestTestFactory.CRITERIA);
-		// }
+		@Test
+		void shouldReturnNull() {
+			service.saveFile(inputStream);
 
-		// @Test
-		// void shouldMapEingang() {
-		// service.routeForwarding(GrpcRouteForwardingRequestTestFactory.create(),
-		// responseObserver);
+			verify(fileService).saveToFile(inputStream);
+		}
 
-		// verify(eingangMapper).toFormData(GrpcRouteForwardingRequestTestFactory.EINGANG);
-		// }
+		@Test
+		void shouldReturnFileFuture() {
+			when(fileService.saveToFile(any())).thenReturn(fileFuture);
 
-		// @Test
-		// void shouldCallService() {
-		// service.routeForwarding(GrpcRouteForwardingRequestTestFactory.create(),
-		// responseObserver);
+			var result = service.saveFile(inputStream);
 
-		// verify(routeService).route(routeCriteria, formData);
-		// }
+			assertThat(result).isSameAs(fileFuture);
+		}
 	}
 
+	@Nested
+	class TestRespondWith {
+
+		private GrpcRouteForwardingResponse response = GrpcRouteForwardingResponse.newBuilder().build();
+		@Mock
+		private StreamObserver<GrpcRouteForwardingResponse> responseObserver;
+
+		@Test
+		void shouldCallOnNext() {
+			service.respondWith(responseObserver, response);
+
+			verify(responseObserver).onNext(response);
+		}
+
+		@Test
+		void shouldCallOnCompletedAfterOnNext() {
+			var inOrder = inOrder(responseObserver);
+
+			service.respondWith(responseObserver, response);
+
+			inOrder.verify(responseObserver).onNext(response);
+			inOrder.verify(responseObserver).onCompleted();
+		}
+	}
 }
-- 
GitLab