Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
8c8da44
Update to Spring-Boot 4.0.0-SNAPSHOT
afranken Jun 13, 2025
dc4670d
Remove AWS SDK v1 and JUnit4
afranken Jun 13, 2025
930b1b0
Enable Kotlin usage for Spring
afranken Jun 15, 2025
218e92f
Migrate "util" package to Kotlin
afranken Jun 19, 2025
c43ffe8
Prepare 5.0.0
afranken Jun 19, 2025
ae39951
Update to Spring-Boot 4.0.0-M1
afranken Jul 25, 2025
bdacd89
Update to Spring-Boot 4.4.0-SNAPSHOT / Kotlin 2.2.0-RC
afranken Jun 13, 2025
535ae3a
Update to Spring-Boot 4.0.0-M2
afranken Aug 22, 2025
ee4cc69
Idiomatic Kotlin for util package.
afranken Sep 2, 2025
bcd2747
Remove legacy properties.
afranken Sep 9, 2025
ef32222
Move controllers to controller package.
afranken Sep 9, 2025
46f414f
Enable Kotlin Spring Beans
afranken Sep 9, 2025
1f3b215
Migrate "store" package to Kotlin
afranken Sep 9, 2025
9fa1609
Migrate "service" package to Kotlin
afranken Sep 10, 2025
73dd95b
Kotlin optimizations, StringUtils and Java API removal
afranken Sep 10, 2025
47b299e
Rename .java to .kt
afranken Sep 10, 2025
427033b
Migrate "controller" package to Kotlin
afranken Sep 10, 2025
79aebfa
Rename .java to .kt
afranken Sep 11, 2025
a2527ea
Migrate "dto" package to Kotlin
afranken Sep 11, 2025
28982f8
Rename .java to .kt
afranken Sep 11, 2025
c99a0ec
Migrate "s3mock" and "testing" package to Kotlin
afranken Sep 11, 2025
2002998
Rename .java to .kt
afranken Sep 11, 2025
62b40c6
Migrate "testsupport/common" package to Kotlin
afranken Sep 11, 2025
50bcff2
Rename .java to .kt
afranken Sep 11, 2025
bb6847f
Migrate "junit5" package to Kotlin, add Java tests
afranken Sep 11, 2025
dd2dc28
Rename .java to .kt
afranken Sep 11, 2025
70905ef
Migrate "testng" package to Kotlin, add Java tests
afranken Sep 11, 2025
8df1d6e
Rename .java to .kt
afranken Sep 11, 2025
f82462d
Migrate "testcontainers" package to Kotlin, add Java tests
afranken Sep 11, 2025
94f00e3
Idiomatic Kotlin 1
afranken Sep 11, 2025
4239b5f
Idiomatic Kotlin 2
afranken Sep 11, 2025
a2eada4
Use distinct bucket names in tests.
afranken Sep 11, 2025
8ada3ce
Idiomatic Kotlin 3
afranken Sep 11, 2025
c50b11d
Idiomatic Kotlin 4
afranken Sep 12, 2025
56f0135
Idiomatic Kotlin 5
afranken Sep 12, 2025
d678cca
Idiomatic Kotlin 6
afranken Sep 12, 2025
175dc31
Idiomatic Kotlin 7
afranken Sep 12, 2025
70f6074
DTO simplification 1
afranken Sep 12, 2025
10593d0
DTO simplification 2
afranken Sep 14, 2025
a28720a
DTO simplification 3
afranken Sep 14, 2025
de6e432
DTO simplification 4
afranken Sep 14, 2025
8c8fabe
Private mutable properties
afranken Sep 15, 2025
47d3074
Simplification, readability, immutability
afranken Sep 15, 2025
7cb0b8b
Formatting, naming, sorting
afranken Sep 17, 2025
a6edc0c
Reimplement ConcurrencyIT in coroutines
afranken Sep 17, 2025
760f127
Extension functions for call-flow, fix range headers.
afranken Sep 17, 2025
883856c
Remove duplicate jvmTarget config
afranken Sep 17, 2025
bb8ff65
Allow up to 3 minutes for ConcurrentIT to finish
afranken Sep 18, 2025
5254ba9
Update to Spring-Boot 4.0.0-M3
afranken Sep 18, 2025
bf90fcd
Use Jackson 3 annotations and mappers.
afranken Sep 18, 2025
08609bd
Fix Kotlin refactorings after Java refactorings on main
afranken Nov 14, 2025
2833562
Update to Spring Boot 4.0.0-RC2
afranken Nov 14, 2025
cd2e813
Update to TestContainers 2.x
afranken Nov 14, 2025
4d28aa5
Remove commons-codec dependency
afranken Nov 14, 2025
a104b15
Bump Spring Boot to 4.0.0
afranken Nov 21, 2025
6c8fdc7
Bump Maven to 4.0.0-rc-5
afranken Nov 21, 2025
ccd534b
chore: fix test after rebasing to main
afranken Dec 4, 2025
cf49915
fix: use correct spelling
afranken Dec 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
wrapperVersion=3.3.4
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/4.0.0-rc-5/apache-maven-4.0.0-rc-5-bin.zip
36 changes: 25 additions & 11 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Whenever a 3rd party library is updated, S3Mock will update it's MINOR version.
* [Changelog](#changelog)
* [PLANNED - 5.x - RELEASE TBD](#planned---5x---release-tbd)
* [Planned changes](#planned-changes)
* [5.0.0 - PLANNED](#500---planned)
* [CURRENT - 4.x - THIS VERSION IS UNDER ACTIVE DEVELOPMENT](#current---4x---this-version-is-under-active-development)
* [4.11.0](#4110)
* [4.10.0](#4100)
Expand Down Expand Up @@ -130,22 +131,35 @@ Running S3Mock in unit tests is still supported by using [TestContainers](https:
* Features and fixes
* TBD
* Refactorings
* Removal of legacy-style Spring properties in favor of current environment variables.
* AWS has deprecated SDK for Java v1, and will remove support EOY 2025.
* S3Mock will remove bundled support for Java SDK v1 in late 2025.
* JUnit 4.x deprecation
* S3Mock will remove bundled support for JUnit 4.x in late 2025.
* Looking to Remove unit test modules. This enables
* Refactoring S3Mock to a "standard" Spring Boot application.
* Removal of workarounds to use `S3MockApplication#start` from a static context
* Removal of properties workarounds
* Migration to `Kotlin` - the IntegrationTests and unit tests were migrated already.
* Version updates
* Bump Spring Boot version to 4.x
* Bump Spring Framework version to 7.x
* TBD

## 5.0.0 - PLANNED

* Features and fixes
* Get object with range now returns the same headers as non-range calls.
* Refactorings
* Use Jackson 3 annotations and mappers.
* AWS has deprecated SDK for Java v1 and will remove support EOY 2025.
* Remove Java SDK v1.
* JUnit 4.x deprecation
* Remove JUnit 4.x support.
* Remove legacy properties for S3Mock configuration.
* Move all controller-related code from "com.adobe.testing.s3mock" to "com.adobe.testing.s3mock.controller" package.
* Remove Apache libraries like "commons-compress", "commons-codec" or "commons-lang3" from dependencies. Kotlin and Java standard library provide similar functionality.
* Version updates
* Bump Spring Boot version to 4.0.0
* Bump Spring Framework version to 7.0.1
* Bump java version from 17 to 25
* S3Mock will use the baseline Spring chooses to support.
* The Docker container will run Java 25 LTS.
* Compile with Java 25, target Java 17
* Docker container runs Java 25
* Bump TestContainers to 2.0.2
* Bump Alpine Linux to 3.23.0 (release: December 2025)
* This is the latest version of Alpine Linux that supports Java 25.
* Bump Maven to 4.0.0

# CURRENT - 4.x - THIS VERSION IS UNDER ACTIVE DEVELOPMENT
Version 4.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Java integration.
Expand Down
30 changes: 5 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
* [Expanded example](#expanded-example)
* [Start using a self-signed SSL certificate](#start-using-a-self-signed-ssl-certificate)
* [S3Mock Java](#s3mock-java)
* [Start using the JUnit4 Rule](#start-using-the-junit4-rule)
* [Start using the JUnit5 Extension](#start-using-the-junit5-extension)
* [Start using the TestNG Listener](#start-using-the-testng-listener)
* [Start programmatically](#start-programmatically)
Expand Down Expand Up @@ -73,8 +72,7 @@
the [Amazon S3 API](https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html).
It has been created to support local integration testing by reducing infrastructure dependencies.

The `S3Mock` server can be started as a standalone *Docker* container, using *Testcontainers*, *JUnit4*, *JUnit5* and
*TestNG* support, or programmatically.
The `S3Mock` server can be started as a standalone *Docker* container, using *Testcontainers*, *JUnit5* and *TestNG* support, or programmatically.

## Changelog

Expand Down Expand Up @@ -303,7 +301,6 @@ curl --insecure --request GET https://localhost:9191/my-test-bucket/my-file -O
The mock can be configured with the following environment variables:

- `COM_ADOBE_TESTING_S3MOCK_STORE_VALID_KMS_KEYS`: list of KMS Key-Refs that are to be treated as *valid*.
- Legacy name: `validKmsKeys`
- Default: none
- KMS keys must be configured as valid ARNs in the format of "`arn:aws:kms:region:acct-id:key/key-id`", for example "
`arn:aws:kms:us-east-1:1234567890:key/valid-test-key-id`"
Expand All @@ -313,21 +310,20 @@ The mock can be configured with the following environment variables:
- *S3Mock does not implement KMS encryption*, if a key ID is passed in a request, S3Mock will just validate if a given
Key was configured during startup and reject the request if the given Key was not configured.
- `COM_ADOBE_TESTING_S3MOCK_STORE_INITIAL_BUCKETS`: list of names for buckets that will be available initially.
- Legacy name: `initialBuckets`
- Default: none
- The list must be comma separated names like `bucketa, bucketb`
- `COM_ADOBE_TESTING_S3MOCK_STORE_REGION`: the region the S3Mock is supposed to mock.
- Legacy name: `COM_ADOBE_TESTING_S3MOCK_REGION`
- Example: `eu-west-1`
- Default: `us-east-1`
- Value must be a valid AWS region identifier like `eu-west-1`
- `COM_ADOBE_TESTING_S3MOCK_STORE_ROOT`: the base directory to place the temporary files exposed by the mock. If S3Mock is started in Docker, a volume
must be mounted as the `root` directory, see examples below.
- Legacy name: `root`
- Default: Java temp directory
- `COM_ADOBE_TESTING_S3MOCK_STORE_RETAIN_FILES_ON_EXIT`: set to `true` to let S3Mock keep all files that were created during its lifetime. Default is
`false`, all files are removed if S3Mock shuts down.
- Legacy name: `retainFilesOnExit`
- Default: false
- `COM_ADOBE_TESTING_S3MOCK_CONTROLLER_CONTEXT_PATH`: the base context path for all controllers.
Use if you start S3Mock within your application's unit tests and need to separate the context paths.
- Default: ""
- `debug`: set to `true` to
enable [Spring Boot's debug output](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.logging.console-output).
- `trace`: set to `true` to
Expand Down Expand Up @@ -579,22 +575,6 @@ integration..._

**See also [the Java section below](#Java)**

#### Start using the JUnit4 Rule

The example [`S3MockRuleTest`](testsupport/junit4/src/test/java/com/adobe/testing/s3mock/junit4/S3MockRuleTest.java)
demonstrates the usage of the `S3MockRule`, which can be configured through a _builder_.

To use the JUnit4 Rule, use the following Maven artifact in `test` scope:

```xml
<dependency>
<groupId>com.adobe.testing</groupId>
<artifactId>s3mock-junit4</artifactId>
<version>...</version>
<scope>test</scope>
</dependency>
```

#### Start using the JUnit5 Extension

The `S3MockExtension` can currently be used in two ways:
Expand Down
2 changes: 1 addition & 1 deletion docker/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<parent>
<groupId>com.adobe.testing</groupId>
<artifactId>s3mock-parent</artifactId>
<version>4.11.1-SNAPSHOT</version>
<version>5.0.0-SNAPSHOT</version>
</parent>

<artifactId>s3mock-docker</artifactId>
Expand Down
17 changes: 12 additions & 5 deletions integration-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<parent>
<groupId>com.adobe.testing</groupId>
<artifactId>s3mock-parent</artifactId>
<version>4.11.1-SNAPSHOT</version>
<version>5.0.0-SNAPSHOT</version>
</parent>

<artifactId>s3mock-integration-tests</artifactId>
Expand Down Expand Up @@ -62,6 +62,7 @@
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
<dependency>
Expand All @@ -73,7 +74,13 @@
<!-- Force Kotlin coroutines alignment to match aws-sdk-kotlin 1.5.x requirements -->
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core-jvm</artifactId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>${kotlin-coroutines.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-test</artifactId>
<version>${kotlin-coroutines.version}</version>
<scope>test</scope>
</dependency>
Expand Down Expand Up @@ -192,9 +199,9 @@
<time>30000</time>
</wait>
<env>
<COM_ADOBE_TESTING_S3MOCK_DOMAIN_VALID_KMS_KEYS>arn:aws:kms:us-east-1:1234567890:key/valid-test-key-id</COM_ADOBE_TESTING_S3MOCK_DOMAIN_VALID_KMS_KEYS>
<COM_ADOBE_TESTING_S3MOCK_DOMAIN_INITIAL_BUCKETS>bucket-a, bucket-b</COM_ADOBE_TESTING_S3MOCK_DOMAIN_INITIAL_BUCKETS>
<COM_ADOBE_TESTING_S3MOCK_REGION>eu-west-1</COM_ADOBE_TESTING_S3MOCK_REGION>
<COM_ADOBE_TESTING_S3MOCK_STORE_VALID_KMS_KEYS>arn:aws:kms:us-east-1:1234567890:key/valid-test-key-id</COM_ADOBE_TESTING_S3MOCK_STORE_VALID_KMS_KEYS>
<COM_ADOBE_TESTING_S3MOCK_STORE_INITIAL_BUCKETS>bucket-a, bucket-b</COM_ADOBE_TESTING_S3MOCK_STORE_INITIAL_BUCKETS>
<COM_ADOBE_TESTING_S3MOCK_STORE_REGION>eu-west-1</COM_ADOBE_TESTING_S3MOCK_STORE_REGION>
</env>
<memory>256000000</memory>
</run>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package com.adobe.testing.s3mock.its

import com.adobe.testing.s3mock.dto.Owner.DEFAULT_OWNER
import com.adobe.testing.s3mock.dto.Owner.Companion.DEFAULT_OWNER
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,78 +16,76 @@

package com.adobe.testing.s3mock.its

import aws.sdk.kotlin.services.s3.S3Client
import aws.sdk.kotlin.services.s3.model.DeleteObjectRequest
import aws.sdk.kotlin.services.s3.model.GetObjectRequest
import aws.sdk.kotlin.services.s3.model.PutObjectRequest
import aws.smithy.kotlin.runtime.content.ByteStream
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInfo
import software.amazon.awssdk.core.sync.RequestBody
import software.amazon.awssdk.services.s3.S3Client
import java.util.concurrent.Callable
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicInteger
import kotlin.time.Duration.Companion.minutes

internal class ConcurrencyIT : S3TestBase() {
private val s3Client: S3Client = createS3Client()
private val s3Client: S3Client = createS3ClientKotlin()

/**
* Test that there are no inconsistencies when multiple threads PUT, GET and DELETE objects in
* the same bucket.
*/
@Test
@S3VerifiedFailure(
year = 2022,
reason = "No need to test S3 concurrency.",
)
fun `concurrent bucket puts, gets and deletes are successful`(testInfo: TestInfo) {
val bucketName = givenBucket(testInfo)
val runners = (1..100).map { Runner(bucketName, "test/key$it") }
val pool = Executors.newFixedThreadPool(100)
val futures = pool.invokeAll(runners)
assertThat(futures).hasSize(100).allSatisfy { future ->
assertThat(future.get()).isTrue
}
assertThat(DONE.get()).isEqualTo(100)
}
fun `concurrent bucket puts, gets and deletes are successful`(testInfo: TestInfo) =
runTest(timeout = 3.minutes) {
val bucketName = givenBucket(testInfo)
val totalRequests = 1000
val maxConcurrency = 20
val done = AtomicInteger(0)

val limitedDispatcher = Dispatchers.IO.limitedParallelism(maxConcurrency)

coroutineScope {
(1..totalRequests)
.map { i ->
async(limitedDispatcher) {
val key = "test/key-$i"

val putResponse =
s3Client.putObject(
PutObjectRequest {
bucket = bucketName
this.key = key
body = ByteStream.fromBytes(byteArrayOf())
},
)
assertThat(putResponse.eTag).isNotNull
assertThat(putResponse.eTag!!.isNotBlank()).isTrue()

companion object {
private val LATCH = CountDownLatch(100)
private val DONE = AtomicInteger(0)
}
val getResponse =
s3Client.getObject(
GetObjectRequest {
bucket = bucketName
this.key = key
},
) {
it
}
assertThat(getResponse.eTag).isNotNull
assertThat(getResponse.eTag!!.isNotBlank()).isTrue()

inner class Runner(
val bucketName: String,
val key: String,
) : Callable<Boolean> {
override fun call(): Boolean {
LATCH.countDown()
s3Client
.putObject(
{
it.bucket(bucketName)
it.key(key)
},
RequestBody.empty(),
).let { response ->
assertThat(response.eTag()).isNotBlank
}
s3Client.deleteObject(
DeleteObjectRequest {
bucket = bucketName
this.key = key
},
)

s3Client
.getObject {
it.bucket(bucketName)
it.key(key)
}.use {
assertThat(it.response().eTag()).isNotBlank
}
done.incrementAndGet()
}
}.awaitAll()
}

s3Client
.deleteObject {
it.bucket(bucketName)
it.key(key)
}.let { response ->
assertThat(response.deleteMarker()).isTrue
}
DONE.incrementAndGet()
return true
assertThat(done.get()).isEqualTo(totalRequests)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package com.adobe.testing.s3mock.its

import com.adobe.testing.s3mock.S3Exception.PRECONDITION_FAILED
import com.adobe.testing.s3mock.S3Exception.Companion.PRECONDITION_FAILED
import com.adobe.testing.s3mock.util.DigestUtil
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package com.adobe.testing.s3mock.its

import com.adobe.testing.s3mock.util.DigestUtil
import org.apache.commons.codec.digest.DigestUtils
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInfo
Expand All @@ -29,6 +28,7 @@ import software.amazon.awssdk.transfer.s3.S3TransferManager
import software.amazon.awssdk.transfer.s3.model.DownloadRequest
import java.io.ByteArrayInputStream
import java.nio.charset.StandardCharsets
import java.security.MessageDigest

internal class CrtAsyncIT : S3TestBase() {
private val autoS3CrtAsyncClient: S3AsyncClient = createAutoS3CrtAsyncClient()
Expand Down Expand Up @@ -163,10 +163,11 @@ internal class CrtAsyncIT : S3TestBase() {
}, AsyncResponseTransformer.toBytes())
.join()

val md5 = MessageDigest.getInstance("MD5")
val uploadFileBytes = readStreamIntoByteArray(UPLOAD_FILE.inputStream())
(DigestUtils.md5(randomBytes) + DigestUtils.md5(uploadFileBytes)).also {
(md5.digest(randomBytes) + md5.digest(uploadFileBytes)).also {
// verify special etag
assertThat(completeMultipartUploadResponse.eTag()).isEqualTo("\"${DigestUtils.md5Hex(it)}-2\"")
assertThat(completeMultipartUploadResponse.eTag()).isEqualTo("\"${md5.digest(it).joinToString("") { "%02x".format(it) }}-2\"")
}

// verify content size
Expand Down
Loading
Loading