From 5a0dd75b641345d9c3f882c3e2a8b09556a39bcd Mon Sep 17 00:00:00 2001 From: Kess Plasmeier Date: Mon, 2 Feb 2026 11:52:03 -0800 Subject: [PATCH 1/2] fix: set instruction file content length --- .../s3/internal/InstructionFileConfig.java | 14 ++-- .../InstructionFileConfigUploadTest.java | 75 +++++++++++++++++++ 2 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 src/test/java/software/amazon/encryption/s3/internal/InstructionFileConfigUploadTest.java diff --git a/src/main/java/software/amazon/encryption/s3/internal/InstructionFileConfig.java b/src/main/java/software/amazon/encryption/s3/internal/InstructionFileConfig.java index e5b4ab015..5479fec0c 100644 --- a/src/main/java/software/amazon/encryption/s3/internal/InstructionFileConfig.java +++ b/src/main/java/software/amazon/encryption/s3/internal/InstructionFileConfig.java @@ -1,11 +1,5 @@ package software.amazon.encryption.s3.internal; -import static software.amazon.encryption.s3.S3EncryptionClientUtilities.DEFAULT_INSTRUCTION_FILE_SUFFIX; -import static software.amazon.encryption.s3.internal.MetadataKeyConstants.INSTRUCTION_FILE; - -import java.util.HashMap; -import java.util.Map; - import software.amazon.awssdk.core.ResponseInputStream; import software.amazon.awssdk.core.async.AsyncRequestBody; import software.amazon.awssdk.core.async.AsyncResponseTransformer; @@ -18,6 +12,12 @@ import software.amazon.awssdk.services.s3.model.PutObjectResponse; import software.amazon.encryption.s3.S3EncryptionClientException; +import java.util.HashMap; +import java.util.Map; + +import static software.amazon.encryption.s3.S3EncryptionClientUtilities.DEFAULT_INSTRUCTION_FILE_SUFFIX; +import static software.amazon.encryption.s3.internal.MetadataKeyConstants.INSTRUCTION_FILE; + /** * Provides configuration options for instruction file behaviors. */ @@ -65,8 +65,10 @@ PutObjectResponse putInstructionFile(PutObjectRequest request, String instructio instFileMetadata.put(INSTRUCTION_FILE, ""); // Use toBuilder to keep all other fields the same as the actual request + // but set the content length, key, and metadata appropriately for the instruction file final PutObjectRequest instPutRequest = request.toBuilder() .key(request.key() + instructionFileSuffix) + .contentLength((long) instructionFileContent.getBytes().length) .metadata(instFileMetadata) .build(); switch (_clientType) { diff --git a/src/test/java/software/amazon/encryption/s3/internal/InstructionFileConfigUploadTest.java b/src/test/java/software/amazon/encryption/s3/internal/InstructionFileConfigUploadTest.java new file mode 100644 index 000000000..24a02c4a2 --- /dev/null +++ b/src/test/java/software/amazon/encryption/s3/internal/InstructionFileConfigUploadTest.java @@ -0,0 +1,75 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package software.amazon.encryption.s3.internal; + +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; + +import java.util.concurrent.CompletableFuture; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +class InstructionFileConfigUploadTest { + + @Test + void uploadInstructionFileWithSetContentLengthSyncClient() { + // Create a mock for the S3 client + S3Client mockedS3Client = mock(S3Client.class); + // The argument captor is used to capture the PutObjectRequest passed to the putObject method + ArgumentCaptor instructionFilePutCaptor = ArgumentCaptor.forClass(PutObjectRequest.class); + + // Create the InstructionFileConfig with the mocked S3 client + InstructionFileConfig instructionFileConfig = InstructionFileConfig.builder() + .instructionFileClient(mockedS3Client) + .enableInstructionFilePutObject(true) + .build(); + + // Build some data for the test + PutObjectRequest putObjectRequest = PutObjectRequest.builder() + .key("someKey").build(); + String instructionFileContent = "some content that fakes an instruction file"; + + // call the actual method under test + instructionFileConfig.putInstructionFile(putObjectRequest, instructionFileContent); + + // Verify that the putObject method was called and the captured request has the correct content length + verify(mockedS3Client).putObject(instructionFilePutCaptor.capture(), any(RequestBody.class)); + assertEquals(instructionFileContent.getBytes().length, instructionFilePutCaptor.getValue().contentLength()); + } + + @Test + void uploadInstructionFileWithSetContentLengthAsyncClient() { + // Create a mock for the S3 client + S3AsyncClient mockedS3Client = mock(S3AsyncClient.class); + // The async putObject method returns a CompletableFuture, so we need to mock that behavior + when(mockedS3Client.putObject(any(PutObjectRequest.class), any(AsyncRequestBody.class))) + .thenReturn(CompletableFuture.completedFuture(null)); + // The argument captor is used to capture the PutObjectRequest passed to the putObject method + ArgumentCaptor instructionFilePutCaptor = ArgumentCaptor.forClass(PutObjectRequest.class); + + // Create the InstructionFileConfig with the mocked S3 async client + InstructionFileConfig instructionFileConfig = InstructionFileConfig.builder() + .instructionFileAsyncClient(mockedS3Client) + .enableInstructionFilePutObject(true) + .build(); + + // Build some data for the test + PutObjectRequest putObjectRequest = PutObjectRequest.builder() + .key("someKey").build(); + String instructionFileContent = "some content that fakes an instruction file"; + + // call the actual method under test + instructionFileConfig.putInstructionFile(putObjectRequest, instructionFileContent); + + // Verify that the putObject method was called and the captured request has the correct content length + verify(mockedS3Client).putObject(instructionFilePutCaptor.capture(), any(AsyncRequestBody.class)); + assertEquals(instructionFileContent.getBytes().length, instructionFilePutCaptor.getValue().contentLength()); + } +} From 4405597aa65e91797ee2d66313fa117a26c8b420 Mon Sep 17 00:00:00 2001 From: Kess Plasmeier Date: Tue, 3 Feb 2026 08:57:58 -0800 Subject: [PATCH 2/2] rerun ci --- .../encryption/s3/internal/InstructionFileConfigUploadTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/software/amazon/encryption/s3/internal/InstructionFileConfigUploadTest.java b/src/test/java/software/amazon/encryption/s3/internal/InstructionFileConfigUploadTest.java index 24a02c4a2..56b67bf7c 100644 --- a/src/test/java/software/amazon/encryption/s3/internal/InstructionFileConfigUploadTest.java +++ b/src/test/java/software/amazon/encryption/s3/internal/InstructionFileConfigUploadTest.java @@ -65,7 +65,7 @@ void uploadInstructionFileWithSetContentLengthAsyncClient() { .key("someKey").build(); String instructionFileContent = "some content that fakes an instruction file"; - // call the actual method under test + // Call the actual method under test instructionFileConfig.putInstructionFile(putObjectRequest, instructionFileContent); // Verify that the putObject method was called and the captured request has the correct content length