From 258d89d08ce57b592b46d8740e0d4f5191d7ac05 Mon Sep 17 00:00:00 2001
From: Jan Zickermann <jan.zickermann@dataport.de>
Date: Tue, 18 Feb 2025 13:37:55 +0100
Subject: [PATCH] OZG-4097 send-attachment: End transfer with empty chunk

---
 .../postfach/osiv2/model/FileChunkInfo.java       |  8 ++++++--
 .../osiv2/transfer/Osi2QuarantineService.java     |  2 +-
 .../osiv2/OsiPostfachRemoteServiceITCase.java     | 15 ++++++++++-----
 .../extension/AttachmentExampleUploadUtil.java    |  4 +++-
 .../osiv2/transfer/Osi2QuarantineServiceTest.java |  6 +++++-
 5 files changed, 25 insertions(+), 10 deletions(-)

diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/FileChunkInfo.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/FileChunkInfo.java
index b1e049b..d6c6167 100644
--- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/FileChunkInfo.java
+++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/model/FileChunkInfo.java
@@ -18,16 +18,20 @@ public record FileChunkInfo(
 		return new AbstractResource() {
 			@Override
 			public String getDescription() {
-				return FileChunkInfo.this.toString();
+				return "File chunk " + chunkIndex + " of " + upload.getLoggableString();
 			}
 
 			@Override
 			public InputStream getInputStream() {
-				return new LimitedInputStream(fileInputStream, CHUNK_SIZE);
+				return new LimitedInputStream(fileInputStream, contentLength());
 			}
 
 			@Override
 			public long contentLength() {
+				return chunkIndex == upload.numberOfChunks() ? 0 : getChunkContentLength();
+			}
+
+			private long getChunkContentLength() {
 				return chunkIndex == upload.numberOfChunks() - 1
 						? upload.file().getSize() % CHUNK_SIZE
 						: CHUNK_SIZE;
diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2QuarantineService.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2QuarantineService.java
index 2c5253e..50a5a52 100644
--- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2QuarantineService.java
+++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2QuarantineService.java
@@ -80,7 +80,7 @@ public class Osi2QuarantineService {
 	}
 
 	Stream<FileChunkInfo> streamFileChunkInfos(Osi2FileUpload upload) {
-		return LongStream.range(0, upload.numberOfChunks())
+		return LongStream.range(0, upload.numberOfChunks() + 1)
 				.mapToObj(chunkIndex -> buildFileChunkInfo(upload, chunkIndex));
 	}
 
diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java
index 71c3be1..82b3ca2 100644
--- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java
+++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java
@@ -8,6 +8,7 @@ import static org.assertj.core.api.Assertions.*;
 import java.time.OffsetDateTime;
 import java.util.List;
 import java.util.UUID;
+import java.util.function.Function;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
@@ -115,13 +116,17 @@ class OsiPostfachRemoteServiceITCase {
 
 		osiPostfachRemoteService.sendMessage(postfachNachrichtWithAttachment);
 
+		var requests = postfachFacadeMockServer.findAll(
+				postRequestedFor(urlPathTemplate("/Quarantine/v1/Upload/Chunked")));
+		assertThat(requests).hasSize(2);
+		Function<Integer, byte[]> requestBodyBytes = requestIndex -> requests.get(requestIndex).getPart("file").getBody().asBytes();
+		// should send file in one chunk
+		assertThat(requestBodyBytes.apply(0)).isEqualTo(AttachmentExampleUploadUtil.EXAMPLE_TEXT_DATA);
+		// should send empty chunk to complete transfer
+		assertThat(requestBodyBytes.apply(1)).isEmpty();
 		postfachFacadeMockServer.verify(
 				exactly(1),
-				postRequestedFor(urlPathTemplate("/Quarantine/v1/Upload/Chunked"))
-		);
-		postfachFacadeMockServer.verify(
-				exactly(1),
-				postRequestedFor(urlPathTemplate("/Quarantine/v1/Upload/{guid}"))
+				getRequestedFor(urlPathTemplate("/Quarantine/v1/Upload/{guid}"))
 		);
 		postfachFacadeMockServer.verify(
 				exactly(1),
diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/AttachmentExampleUploadUtil.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/AttachmentExampleUploadUtil.java
index 3b3f4ba..bde195f 100644
--- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/AttachmentExampleUploadUtil.java
+++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/AttachmentExampleUploadUtil.java
@@ -12,13 +12,15 @@ import lombok.extern.log4j.Log4j2;
 @Log4j2
 public class AttachmentExampleUploadUtil {
 
+	public static final byte[] EXAMPLE_TEXT_DATA = LoremIpsum.getInstance().getParagraphs(5,100).getBytes();
+
 	public static String uploadTextFile(Osi2AttachmentFileService remoteService) {
 
 		return remoteService.uploadFileAndReturnId(AttachmentFile.builder()
 						.contentType("test/plain")
 						.name("test.txt")
 						.vorgangId(UUID.randomUUID().toString())
-				.build(), () -> new ByteArrayInputStream(LoremIpsum.getInstance().getParagraphs(5,100).getBytes()));
+				.build(), () -> new ByteArrayInputStream(EXAMPLE_TEXT_DATA));
 	}
 
 }
diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2QuarantineServiceTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2QuarantineServiceTest.java
index 0b90398..8e6e0a1 100644
--- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2QuarantineServiceTest.java
+++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/transfer/Osi2QuarantineServiceTest.java
@@ -329,13 +329,17 @@ class Osi2QuarantineServiceTest {
 				.upload(upload)
 				.chunkIndex(1)
 				.build();
+		private final FileChunkInfo emptyChunkInfo = FileChunkInfo.builder()
+				.upload(upload)
+				.chunkIndex(2)
+				.build();
 
 		@DisplayName("should stream chunk infos")
 		@Test
 		void shouldStreamChunkInfos() {
 			var result = service.streamFileChunkInfos(upload).toList();
 
-			assertThat(result).containsExactly(chunkInfo1, chunkInfo2);
+			assertThat(result).containsExactly(chunkInfo1, chunkInfo2, emptyChunkInfo);
 		}
 	}
 
-- 
GitLab