Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3b6f01d
Strike Task 27
afranken Aug 17, 2025
755a075
Changelog for 4.8.0 and Star History
afranken Aug 17, 2025
7714719
More Junie guidelines
afranken Aug 17, 2025
daa49e9
Changelog for 4.8.0
afranken Aug 22, 2025
a26d665
Use Kotlin stdlib
afranken Aug 22, 2025
7613b5e
Let Junie write detailed test TODOs.
afranken Aug 23, 2025
3c5f939
Add get object range test.
afranken Aug 23, 2025
557deab
MockBean -> MockitoBean
afranken Aug 23, 2025
45a1d77
Add more unit tests
afranken Aug 23, 2025
c9a0b3c
Add more unit tests
afranken Aug 24, 2025
c2e2d90
Add more unit tests
afranken Aug 24, 2025
29bbc71
Add more unit tests
afranken Aug 24, 2025
2b2d673
Add more unit tests
afranken Aug 24, 2025
3ce25b3
Add more unit tests
afranken Aug 24, 2025
a835adf
Add more unit tests
afranken Aug 24, 2025
b93544e
Increase readability.
afranken Aug 24, 2025
e20d5f4
Add more unit tests
afranken Aug 24, 2025
2a5764a
Add more unit tests
afranken Aug 24, 2025
c4872ff
Add more unit tests
afranken Aug 24, 2025
9212342
Add more unit tests
afranken Aug 24, 2025
508c951
Add more unit tests
afranken Aug 24, 2025
335fadb
Add more unit tests
afranken Aug 24, 2025
4fc120c
Add more unit tests
afranken Aug 24, 2025
a14e7ce
Bug in LocationConstraint: fix us-east-1 handling
afranken Aug 24, 2025
ab96e9c
Fix controller test XMLMapper setup
afranken Aug 24, 2025
32c062f
Add more unit tests
afranken Aug 24, 2025
bad1e4a
Add more unit tests
afranken Aug 24, 2025
30d175f
Add more unit tests
afranken Aug 25, 2025
7826dbf
Add more unit tests
afranken Aug 25, 2025
b3fcd06
Add more unit tests
afranken Aug 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 37 additions & 11 deletions .junie/guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,28 @@ Troubleshooting
- SSL errors: trust self‑signed cert or switch to HTTP.
- Docker errors: ensure Docker is running and you have permissions.

## Junie Operations Playbook (Critical)
To ensure tests execute successfully in this environment, follow these strict rules:

- Default test scope: server module only. Do NOT run full project builds by default.
- Use the test tool, not shell, to run tests:
- Preferred: run_test on specific test files, e.g., "server/src/test/kotlin/com/adobe/testing/s3mock/store/ObjectStoreTest.kt".
- One test by name: use run_test with full path and the test method name parameter.
- Note: Directory-wide runs via run_test may not be supported in this environment. If you need to run all server tests, use Maven with: ./mvnw -pl server -DskipDocker test.
- Avoid integration tests unless explicitly requested and Docker availability is confirmed. If requested, run via Maven lifecycle only.
- If a build is required, prefer fast builds:
- Use ./mvnw -pl server -am -DskipDocker clean test or rely on run_test which compiles as needed.
- Only run ./mvnw clean install (full) when the user explicitly asks for a full build or cross-module changes demand it.
- Never run mvnw verify without confirming Docker is available; if not available, add -DskipDocker.
- Java 17+ required; if build fails due to JDK, report and stop, do not retry with different commands.
- Decision tree:
1) Need to validate changes in server module? -> run_test on one or more specific test files (fast path). If you truly need all server tests, use: ./mvnw -pl server -DskipDocker test.
2) Need a specific server test? -> run_test on that file.
3) Need ITs and Docker is confirmed? -> mvnw -pl integration-tests -am verify; otherwise skip.
4) Need a build artifact quickly? -> mvnw clean install -DskipDocker.

Note: Always summarize which scope you ran and why.


## Build and Configuration Instructions
Expand Down Expand Up @@ -95,7 +117,7 @@ The main test base class for integration tests is `S3TestBase` which provides ut

The server module contains several types of tests:

1. **Controller Tests**: Use `@SpringBootTest` with `WebEnvironment.RANDOM_PORT` and `TestRestTemplate` to test HTTP endpoints. These tests mock the service layer using `@MockBean`.
1. **Controller Tests**: Use `@SpringBootTest` with `WebEnvironment.RANDOM_PORT` and `TestRestTemplate` to test HTTP endpoints. These tests mock the service layer using `@MockitoBean`.

2. **Store Tests**: Use `@SpringBootTest` with `WebEnvironment.NONE` to test the data storage layer. These tests often use `@Autowired` to inject the component under test.

Expand Down Expand Up @@ -213,20 +235,20 @@ The server module uses different testing approaches depending on what's being te
1. **Controller Tests**:
- Extend `BaseControllerTest` to inherit XML serialization setup
- Use `@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)`
- Use `@MockBean` to mock service dependencies
- Use `@MockitoBean` to mock service dependencies
- Inject `TestRestTemplate` to make HTTP requests to the controller

Example controller test:

```kotlin
// BucketControllerTest.kt
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@MockBean(classes = [BucketService::class, ObjectService::class, MultipartService::class])
@MockitoBean(classes = [BucketService::class, ObjectService::class, MultipartService::class])
internal class BucketControllerTest : BaseControllerTest() {
@Autowired
private lateinit var restTemplate: TestRestTemplate

@MockBean
@MockitoBean
private lateinit var bucketService: BucketService

@Test
Expand Down Expand Up @@ -254,7 +276,7 @@ Example store test:
```kotlin
// ObjectStoreTest.kt
@SpringBootTest(classes = [StoreConfiguration::class], webEnvironment = SpringBootTest.WebEnvironment.NONE)
@MockBean(classes = [KmsKeyStore::class, BucketStore::class])
@MockitoBean(classes = [KmsKeyStore::class, BucketStore::class])
internal class ObjectStoreTest : StoreTestBase() {
@Autowired
private lateinit var objectStore: ObjectStore
Expand Down Expand Up @@ -352,9 +374,13 @@ docker run -p 9090:9090 -p 9191:9191 -e debug=true -t adobe/s3mock
### Recommended Development Workflow

1. Make changes to the code
2. Run unit tests frequently to verify basic functionality
- Unit tests should be run very frequently during development
- No task can be declared as done without running a full Maven build successfully
3. Run integration tests to verify end-to-end functionality
4. Build the Docker image to verify packaging
5. Test with your application to verify real-world usage
2. Validate changes with server module tests first (fast path)
- Use the run_test tool on "server/src/test" or on a specific test file/method.
- Prefer this over invoking Maven directly; run_test compiles as needed.
3. Only run a full Maven build when explicitly requested or when cross-module changes demand it
- If building in this environment, prefer fast builds: ./mvnw -pl server -am -DskipDocker clean test
- Do not run mvnw verify unless Docker is confirmed; otherwise add -DskipDocker
4. Run integration tests only when Docker availability is confirmed and when explicitly requested
- Execute via Maven lifecycle: ./mvnw -pl integration-tests -am verify (or add -DskipDocker to skip ITs)
5. Optionally build the Docker image to verify packaging when needed
6. Test with your application to verify real-world usage
16 changes: 11 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ Whenever a 3rd party library is updated, S3Mock will update it's MINOR version.
* [PLANNED - 5.x - RELEASE TBD](#planned---5x---release-tbd)
* [Planned changes](#planned-changes)
* [CURRENT - 4.x - THIS VERSION IS UNDER ACTIVE DEVELOPMENT](#current---4x---this-version-is-under-active-development)
* [4.8.0 - PLANNED](#480---planned)
* [4.9.0 - PLANNED](#490---planned)
* [4.8.0](#480)
* [4.7.0](#470)
* [4.6.0](#460)
* [4.5.1](#451)
Expand Down Expand Up @@ -160,7 +161,7 @@ Version 4.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Jav
* Version updates (build dependencies)
* TBD

## 4.8.0 - PLANNED
## 4.8.0
Version 4.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Java integration.

* Features and fixes
Expand All @@ -171,14 +172,17 @@ Version 4.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Jav
* Force convergence on the newest available transitive dependency versions.
* Optimize file storage for large objects by using buffered streams.
* Version updates (deliverable dependencies)
*
* Bump spring-boot.version from 3.5.4 to 3.5.5
* Bump aws-v2.version from 2.32.7 to 2.32.23
* Bump org.apache.commons:commons-compress from 1.27.1 to 1.28.0
* Version updates (build dependencies)
* Bump kotlin.version from 2.2.0 to 2.2.10
* Bump aws.sdk.kotlin:s3-jvm from 1.4.125 to 1.5.19
* Bump digital.pragmatech.testing:spring-test-profiler from 0.0.5 to 0.0.11
* Bump com.puppycrawl.tools:checkstyle from 10.26.1 to 11.0.0
* Bump github/codeql-action from 3.29.4 to 3.29.9
* Bump github/codeql-action from 3.29.4 to 3.29.11
* Bump actions/checkout from 4.2.2 to 5.0.0
* Bump actions/setup-java from 4.7.1 to 5.0.0

## 4.7.0
Version 4.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Java integration.
Expand All @@ -197,9 +201,11 @@ Version 4.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Jav
* Version updates (build dependencies)
* Bump aws.sdk.kotlin:s3-jvm from 1.4.109 to 1.4.125
* Bump org.apache.maven.plugins:maven-enforcer-plugin from 3.6.0 to 3.6.1
* Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.11.2 to 3.11.3
* Bump org.mockito.kotlin:mockito-kotlin from 5.4.0 to 6.0.0
* Bump step-security/harden-runner from 2.12.2 to 2.13.0
* Bump github/codeql-action from 3.29.2 to 3.29.4
* Bump github/codeql-action from 3.29.2 to 3.29.10
* Bump actions/dependency-review-action from 4.7.1 to 4.7.2

## 4.6.0
Version 4.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Java integration.
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -837,3 +837,6 @@ This project is licensed under the Apache V2 License. See [LICENSE](LICENSE) for

## Powered by
[![IntelliJ IDEA logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/IntelliJ_IDEA.svg)](https://jb.gg/OpenSourceSupport)

## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=adobe/S3Mock&type=Date)](https://www.star-history.com/#adobe/S3Mock&Date)
72 changes: 70 additions & 2 deletions docs/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,75 @@ This document contains a list of potential improvements for the S3Mock project.

## Code Quality Improvements

11. [ ] Increase unit test coverage for service and store layers
11. [ ] Increase unit test coverage for controller, service and store layers

### Task 11 – Test Coverage Plan (Components and Actions)

Scope and components to cover:
- Controllers (HTTP): BucketController, ObjectController, MultipartController
- Services (business logic): BucketService, ObjectService, MultipartService, Kms* services
- Stores (persistence): BucketStore, ObjectStore, MultipartStore, KmsKeyStore
- XML/DTOs and mappers: request/response XML models, serialization utils
- Utilities: digest, ETag, headers, Range requests, SSE
- Configuration: StoreConfiguration, controller advice/error mapping

Priorities (short-to-long horizon):
1) High-value happy-path and error-path coverage for controllers with mocked services (fast feedback).
2) Store layer correctness with Spring Boot WebEnvironment.NONE tests (file IO, metadata persistence, edge cases).
3) Service layer behavior with mocked stores (parameter validation, branching, SSE/KMS interactions).
4) XML serialization/deserialization fidelity for commonly used operations.
5) Regression tests for known corner cases (range requests, conditional headers, multipart completion ordering, KMS key validation).

Concrete test additions (incremental):
- Controllers
- BucketController
- listBuckets returns empty and non-empty results; XML schema shape
- createBucket duplicate name -> proper S3 error code
- deleteBucket non-empty -> proper error
- ObjectController
- putObject with/without Content-MD5; mismatched MD5 -> error
- getObject with Range header (single range) -> 206 + Content-Range
- getObject nonexistent -> 404 S3-style error
- headObject verifies metadata and headers (ETag, Content-Length)
- MultipartController
- initiateMultipartUpload returns uploadId
- uploadPart with invalid partNumber -> error mapping
- completeMultipartUpload out-of-order parts -> consistent ETag behavior
- Services
- ObjectService.storeObject validates metadata, handles SSE headers routing to KMS
- BucketService.deleteBucket checks emptiness guard
- Stores
- ObjectStore
- storeS3ObjectMetadata and getS3ObjectMetadata roundtrip
- list with prefix/delimiter, max-keys, continuation
- delete removes metadata and data file
- BucketStore
- create, list, delete, exist checks
- MultipartStore
- init, addPart, complete, abort state transitions
- XML/DTOs
- Serialize/deserialize ListAllMyBucketsResult, CompleteMultipartUploadResult

Suggested file locations (server module):
- Controllers: server/src/test/kotlin/com/adobe/testing/s3mock/itlike/controller/*Test.kt (extending BaseControllerTest)
- Services: server/src/test/kotlin/com/adobe/testing/s3mock/service/*Test.kt
- Stores: server/src/test/kotlin/com/adobe/testing/s3mock/store/*Test.kt (extend StoreTestBase)
- DTOs: server/src/test/kotlin/com/adobe/testing/s3mock/xml/*Test.kt

Execution (fast path per repo guidelines):
- One test class: ./mvnw -pl server test -Dtest=ObjectStoreTest
- One method: ./mvnw -pl server test -Dtest=ObjectStoreTest#testStoreAndGetObject
- Or via tool: run_test server/src/test/kotlin/com/adobe/testing/s3mock/store/ObjectStoreTest.kt

Acceptance targets for Task 11 completion:
- +10–15% line coverage increase in server module, focusing on controllers and stores
- At least one new test per component category listed above
- Error-path assertions include correct HTTP status and S3 error codes/messages

Notes:
- Avoid ITs unless Docker available; prefer WebEnvironment.RANDOM_PORT controller tests with mocked services.
- Use provided test bases: BaseControllerTest, StoreTestBase, ServiceTestBase.
- Reuse existing sample files: server/src/test/resources/sampleFile.txt, sampleFile_large.txt, sampleKMSFile.txt.
12. [ ] Refactor synchronization mechanisms in store classes to improve concurrency handling
13. [ ] Implement more comprehensive input validation for S3 API parameters
14. [ ] Add more detailed logging throughout the application for better debugging
Expand All @@ -36,7 +104,7 @@ This document contains a list of potential improvements for the S3Mock project.
24. [ ] Improve multipart upload performance
25. [x] Reduce memory usage when handling large files
26. [ ] Optimize XML serialization/deserialization
27. [ ] Implement more efficient storage of object metadata
27. [x] Keep object metadata storage as plain text (JSON) for inspectability (decided against more efficient/binary storage)
28. [ ] Add support for conditional requests to reduce unnecessary data transfer
29. [ ] Optimize concurrent access patterns
30. [ ] Implement more efficient bucket and object locking mechanisms
Expand Down
6 changes: 0 additions & 6 deletions integration-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,10 @@
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<scope>test</scope>
</dependency>
<!-- Force Kotlin stdlib alignment to match aws-sdk-kotlin 1.5.x requirements -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
Expand Down
9 changes: 9 additions & 0 deletions server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>regions</artifactId>
Expand Down Expand Up @@ -83,6 +88,10 @@
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
<!-- Test Dependencies -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,16 @@ public ResponseEntity<ListMultipartUploadsResult> listMultipartUploads(
@RequestParam(name = UPLOAD_ID_MARKER, required = false) String uploadIdMarker) {
bucketService.verifyBucketExists(bucketName);

return ResponseEntity.ok(multipartService.listMultipartUploads(
bucketName,
delimiter,
encodingType,
keyMarker,
maxUploads,
prefix,
uploadIdMarker
)
var result = multipartService.listMultipartUploads(
bucketName,
delimiter,
encodingType,
keyMarker,
maxUploads,
prefix,
uploadIdMarker
);
return ResponseEntity.ok(result);
}

//================================================================================================
Expand Down Expand Up @@ -198,14 +198,15 @@ public ResponseEntity<ListPartsResult> listParts(
bucketService.verifyBucketExists(bucketName);
multipartService.verifyMultipartUploadExists(bucketName, uploadId);

var result = multipartService.getMultipartUploadParts(
bucketName,
key.key(),
maxParts,
partNumberMarker,
uploadId
);
return ResponseEntity
.ok(multipartService.getMultipartUploadParts(
bucketName,
key.key(),
maxParts,
partNumberMarker,
uploadId)
);
.ok(result);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public void serialize(Region value, JsonGenerator gen, SerializerProvider serial
//API doc says to return "null" for the us-east-1 region.
if ("us-east-1".equals(regionString)) {
gen.writeString("null");
} else {
Copy link

Copilot AI Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The else block is missing proper braces. The current code will write the region string for all cases (not just the else case) because the else block doesn't have braces around line 37.

Copilot uses AI. Check for mistakes.
gen.writeString(regionString);
}
gen.writeString(regionString);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2024 Adobe.
* Copyright 2017-2025 Adobe.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -20,23 +20,20 @@ import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.dataformat.xml.XmlMapper
import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator
import org.junit.jupiter.api.BeforeAll

internal abstract class BaseControllerTest {
companion object {
@JvmStatic
protected lateinit var MAPPER: XmlMapper
val MAPPER: XmlMapper = XmlMapper.builder()
.findAndAddModules()
.enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)
.enable(ToXmlGenerator.Feature.AUTO_DETECT_XSI_TYPE)
.enable(FromXmlParser.Feature.AUTO_DETECT_XSI_TYPE)
.build()

@JvmStatic
@BeforeAll
fun setup() {
MAPPER = XmlMapper().enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION).apply {
this as XmlMapper
this.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
this.enable(ToXmlGenerator.Feature.AUTO_DETECT_XSI_TYPE)
this.enable(FromXmlParser.Feature.AUTO_DETECT_XSI_TYPE)
this.factory.xmlOutputFactory.setProperty(WstxOutputProperties.P_USE_DOUBLE_QUOTES_IN_XML_DECL, true)
} as XmlMapper
init {
MAPPER.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
MAPPER.factory.xmlOutputFactory
.setProperty(WstxOutputProperties.P_USE_DOUBLE_QUOTES_IN_XML_DECL, true)
}
}
}
Loading
Loading