Skip to content

Commit 7fa1fce

Browse files
authored
Merge pull request #2453 from adobe/spring-boot-4-snapshot
S3Mock 5.0.0
2 parents 684a7a7 + cf49915 commit 7fa1fce

File tree

333 files changed

+13218
-13554
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

333 files changed

+13218
-13554
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
wrapperVersion=3.3.4
22
distributionType=only-script
3-
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
3+
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

CHANGELOG.md

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Whenever a 3rd party library is updated, S3Mock will update it's MINOR version.
77
* [Changelog](#changelog)
88
* [PLANNED - 5.x - RELEASE TBD](#planned---5x---release-tbd)
99
* [Planned changes](#planned-changes)
10+
* [5.0.0 - PLANNED](#500---planned)
1011
* [CURRENT - 4.x - THIS VERSION IS UNDER ACTIVE DEVELOPMENT](#current---4x---this-version-is-under-active-development)
1112
* [4.11.0](#4110)
1213
* [4.10.0](#4100)
@@ -130,22 +131,35 @@ Running S3Mock in unit tests is still supported by using [TestContainers](https:
130131
* Features and fixes
131132
* TBD
132133
* Refactorings
133-
* Removal of legacy-style Spring properties in favor of current environment variables.
134-
* AWS has deprecated SDK for Java v1, and will remove support EOY 2025.
135-
* S3Mock will remove bundled support for Java SDK v1 in late 2025.
136-
* JUnit 4.x deprecation
137-
* S3Mock will remove bundled support for JUnit 4.x in late 2025.
138134
* Looking to Remove unit test modules. This enables
139135
* Refactoring S3Mock to a "standard" Spring Boot application.
140136
* Removal of workarounds to use `S3MockApplication#start` from a static context
141-
* Removal of properties workarounds
142-
* Migration to `Kotlin` - the IntegrationTests and unit tests were migrated already.
143137
* Version updates
144-
* Bump Spring Boot version to 4.x
145-
* Bump Spring Framework version to 7.x
138+
* TBD
139+
140+
## 5.0.0 - PLANNED
141+
142+
* Features and fixes
143+
* Get object with range now returns the same headers as non-range calls.
144+
* Refactorings
145+
* Use Jackson 3 annotations and mappers.
146+
* AWS has deprecated SDK for Java v1 and will remove support EOY 2025.
147+
* Remove Java SDK v1.
148+
* JUnit 4.x deprecation
149+
* Remove JUnit 4.x support.
150+
* Remove legacy properties for S3Mock configuration.
151+
* Move all controller-related code from "com.adobe.testing.s3mock" to "com.adobe.testing.s3mock.controller" package.
152+
* Remove Apache libraries like "commons-compress", "commons-codec" or "commons-lang3" from dependencies. Kotlin and Java standard library provide similar functionality.
153+
* Version updates
154+
* Bump Spring Boot version to 4.0.0
155+
* Bump Spring Framework version to 7.0.1
146156
* Bump java version from 17 to 25
147-
* S3Mock will use the baseline Spring chooses to support.
148-
* The Docker container will run Java 25 LTS.
157+
* Compile with Java 25, target Java 17
158+
* Docker container runs Java 25
159+
* Bump TestContainers to 2.0.2
160+
* Bump Alpine Linux to 3.23.0 (release: December 2025)
161+
* This is the latest version of Alpine Linux that supports Java 25.
162+
* Bump Maven to 4.0.0
149163

150164
# CURRENT - 4.x - THIS VERSION IS UNDER ACTIVE DEVELOPMENT
151165
Version 4.x is JDK17 LTS bytecode compatible, with Docker and JUnit / direct Java integration.

README.md

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
* [Expanded example](#expanded-example)
3838
* [Start using a self-signed SSL certificate](#start-using-a-self-signed-ssl-certificate)
3939
* [S3Mock Java](#s3mock-java)
40-
* [Start using the JUnit4 Rule](#start-using-the-junit4-rule)
4140
* [Start using the JUnit5 Extension](#start-using-the-junit5-extension)
4241
* [Start using the TestNG Listener](#start-using-the-testng-listener)
4342
* [Start programmatically](#start-programmatically)
@@ -73,8 +72,7 @@
7372
the [Amazon S3 API](https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html).
7473
It has been created to support local integration testing by reducing infrastructure dependencies.
7574

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

7977
## Changelog
8078

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

305303
- `COM_ADOBE_TESTING_S3MOCK_STORE_VALID_KMS_KEYS`: list of KMS Key-Refs that are to be treated as *valid*.
306-
- Legacy name: `validKmsKeys`
307304
- Default: none
308305
- KMS keys must be configured as valid ARNs in the format of "`arn:aws:kms:region:acct-id:key/key-id`", for example "
309306
`arn:aws:kms:us-east-1:1234567890:key/valid-test-key-id`"
@@ -313,21 +310,20 @@ The mock can be configured with the following environment variables:
313310
- *S3Mock does not implement KMS encryption*, if a key ID is passed in a request, S3Mock will just validate if a given
314311
Key was configured during startup and reject the request if the given Key was not configured.
315312
- `COM_ADOBE_TESTING_S3MOCK_STORE_INITIAL_BUCKETS`: list of names for buckets that will be available initially.
316-
- Legacy name: `initialBuckets`
317313
- Default: none
318314
- The list must be comma separated names like `bucketa, bucketb`
319315
- `COM_ADOBE_TESTING_S3MOCK_STORE_REGION`: the region the S3Mock is supposed to mock.
320-
- Legacy name: `COM_ADOBE_TESTING_S3MOCK_REGION`
321-
- Example: `eu-west-1`
322316
- Default: `us-east-1`
317+
- Value must be a valid AWS region identifier like `eu-west-1`
323318
- `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
324319
must be mounted as the `root` directory, see examples below.
325-
- Legacy name: `root`
326320
- Default: Java temp directory
327321
- `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
328322
`false`, all files are removed if S3Mock shuts down.
329-
- Legacy name: `retainFilesOnExit`
330323
- Default: false
324+
- `COM_ADOBE_TESTING_S3MOCK_CONTROLLER_CONTEXT_PATH`: the base context path for all controllers.
325+
Use if you start S3Mock within your application's unit tests and need to separate the context paths.
326+
- Default: ""
331327
- `debug`: set to `true` to
332328
enable [Spring Boot's debug output](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.logging.console-output).
333329
- `trace`: set to `true` to
@@ -579,22 +575,6 @@ integration..._
579575

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

582-
#### Start using the JUnit4 Rule
583-
584-
The example [`S3MockRuleTest`](testsupport/junit4/src/test/java/com/adobe/testing/s3mock/junit4/S3MockRuleTest.java)
585-
demonstrates the usage of the `S3MockRule`, which can be configured through a _builder_.
586-
587-
To use the JUnit4 Rule, use the following Maven artifact in `test` scope:
588-
589-
```xml
590-
<dependency>
591-
<groupId>com.adobe.testing</groupId>
592-
<artifactId>s3mock-junit4</artifactId>
593-
<version>...</version>
594-
<scope>test</scope>
595-
</dependency>
596-
```
597-
598578
#### Start using the JUnit5 Extension
599579

600580
The `S3MockExtension` can currently be used in two ways:

docker/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<parent>
2323
<groupId>com.adobe.testing</groupId>
2424
<artifactId>s3mock-parent</artifactId>
25-
<version>4.11.1-SNAPSHOT</version>
25+
<version>5.0.0-SNAPSHOT</version>
2626
</parent>
2727

2828
<artifactId>s3mock-docker</artifactId>

integration-tests/pom.xml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<parent>
2323
<groupId>com.adobe.testing</groupId>
2424
<artifactId>s3mock-parent</artifactId>
25-
<version>4.11.1-SNAPSHOT</version>
25+
<version>5.0.0-SNAPSHOT</version>
2626
</parent>
2727

2828
<artifactId>s3mock-integration-tests</artifactId>
@@ -62,6 +62,7 @@
6262
<dependency>
6363
<groupId>org.jetbrains.kotlin</groupId>
6464
<artifactId>kotlin-stdlib</artifactId>
65+
<version>${kotlin.version}</version>
6566
<scope>test</scope>
6667
</dependency>
6768
<dependency>
@@ -73,7 +74,13 @@
7374
<!-- Force Kotlin coroutines alignment to match aws-sdk-kotlin 1.5.x requirements -->
7475
<dependency>
7576
<groupId>org.jetbrains.kotlinx</groupId>
76-
<artifactId>kotlinx-coroutines-core-jvm</artifactId>
77+
<artifactId>kotlinx-coroutines-core</artifactId>
78+
<version>${kotlin-coroutines.version}</version>
79+
<scope>test</scope>
80+
</dependency>
81+
<dependency>
82+
<groupId>org.jetbrains.kotlinx</groupId>
83+
<artifactId>kotlinx-coroutines-test</artifactId>
7784
<version>${kotlin-coroutines.version}</version>
7885
<scope>test</scope>
7986
</dependency>
@@ -192,9 +199,9 @@
192199
<time>30000</time>
193200
</wait>
194201
<env>
195-
<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>
196-
<COM_ADOBE_TESTING_S3MOCK_DOMAIN_INITIAL_BUCKETS>bucket-a, bucket-b</COM_ADOBE_TESTING_S3MOCK_DOMAIN_INITIAL_BUCKETS>
197-
<COM_ADOBE_TESTING_S3MOCK_REGION>eu-west-1</COM_ADOBE_TESTING_S3MOCK_REGION>
202+
<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>
203+
<COM_ADOBE_TESTING_S3MOCK_STORE_INITIAL_BUCKETS>bucket-a, bucket-b</COM_ADOBE_TESTING_S3MOCK_STORE_INITIAL_BUCKETS>
204+
<COM_ADOBE_TESTING_S3MOCK_STORE_REGION>eu-west-1</COM_ADOBE_TESTING_S3MOCK_STORE_REGION>
198205
</env>
199206
<memory>256000000</memory>
200207
</run>

integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/AclIT.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package com.adobe.testing.s3mock.its
1818

19-
import com.adobe.testing.s3mock.dto.Owner.DEFAULT_OWNER
19+
import com.adobe.testing.s3mock.dto.Owner.Companion.DEFAULT_OWNER
2020
import org.assertj.core.api.Assertions.assertThat
2121
import org.junit.jupiter.api.Test
2222
import org.junit.jupiter.api.TestInfo

integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/ConcurrencyIT.kt

Lines changed: 59 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -16,78 +16,76 @@
1616

1717
package com.adobe.testing.s3mock.its
1818

19+
import aws.sdk.kotlin.services.s3.S3Client
20+
import aws.sdk.kotlin.services.s3.model.DeleteObjectRequest
21+
import aws.sdk.kotlin.services.s3.model.GetObjectRequest
22+
import aws.sdk.kotlin.services.s3.model.PutObjectRequest
23+
import aws.smithy.kotlin.runtime.content.ByteStream
24+
import kotlinx.coroutines.Dispatchers
25+
import kotlinx.coroutines.async
26+
import kotlinx.coroutines.awaitAll
27+
import kotlinx.coroutines.coroutineScope
28+
import kotlinx.coroutines.test.runTest
1929
import org.assertj.core.api.Assertions.assertThat
2030
import org.junit.jupiter.api.Test
2131
import org.junit.jupiter.api.TestInfo
22-
import software.amazon.awssdk.core.sync.RequestBody
23-
import software.amazon.awssdk.services.s3.S3Client
24-
import java.util.concurrent.Callable
25-
import java.util.concurrent.CountDownLatch
26-
import java.util.concurrent.Executors
2732
import java.util.concurrent.atomic.AtomicInteger
33+
import kotlin.time.Duration.Companion.minutes
2834

2935
internal class ConcurrencyIT : S3TestBase() {
30-
private val s3Client: S3Client = createS3Client()
36+
private val s3Client: S3Client = createS3ClientKotlin()
3137

32-
/**
33-
* Test that there are no inconsistencies when multiple threads PUT, GET and DELETE objects in
34-
* the same bucket.
35-
*/
3638
@Test
37-
@S3VerifiedFailure(
38-
year = 2022,
39-
reason = "No need to test S3 concurrency.",
40-
)
41-
fun `concurrent bucket puts, gets and deletes are successful`(testInfo: TestInfo) {
42-
val bucketName = givenBucket(testInfo)
43-
val runners = (1..100).map { Runner(bucketName, "test/key$it") }
44-
val pool = Executors.newFixedThreadPool(100)
45-
val futures = pool.invokeAll(runners)
46-
assertThat(futures).hasSize(100).allSatisfy { future ->
47-
assertThat(future.get()).isTrue
48-
}
49-
assertThat(DONE.get()).isEqualTo(100)
50-
}
39+
fun `concurrent bucket puts, gets and deletes are successful`(testInfo: TestInfo) =
40+
runTest(timeout = 3.minutes) {
41+
val bucketName = givenBucket(testInfo)
42+
val totalRequests = 1000
43+
val maxConcurrency = 20
44+
val done = AtomicInteger(0)
45+
46+
val limitedDispatcher = Dispatchers.IO.limitedParallelism(maxConcurrency)
47+
48+
coroutineScope {
49+
(1..totalRequests)
50+
.map { i ->
51+
async(limitedDispatcher) {
52+
val key = "test/key-$i"
53+
54+
val putResponse =
55+
s3Client.putObject(
56+
PutObjectRequest {
57+
bucket = bucketName
58+
this.key = key
59+
body = ByteStream.fromBytes(byteArrayOf())
60+
},
61+
)
62+
assertThat(putResponse.eTag).isNotNull
63+
assertThat(putResponse.eTag!!.isNotBlank()).isTrue()
5164

52-
companion object {
53-
private val LATCH = CountDownLatch(100)
54-
private val DONE = AtomicInteger(0)
55-
}
65+
val getResponse =
66+
s3Client.getObject(
67+
GetObjectRequest {
68+
bucket = bucketName
69+
this.key = key
70+
},
71+
) {
72+
it
73+
}
74+
assertThat(getResponse.eTag).isNotNull
75+
assertThat(getResponse.eTag!!.isNotBlank()).isTrue()
5676

57-
inner class Runner(
58-
val bucketName: String,
59-
val key: String,
60-
) : Callable<Boolean> {
61-
override fun call(): Boolean {
62-
LATCH.countDown()
63-
s3Client
64-
.putObject(
65-
{
66-
it.bucket(bucketName)
67-
it.key(key)
68-
},
69-
RequestBody.empty(),
70-
).let { response ->
71-
assertThat(response.eTag()).isNotBlank
72-
}
77+
s3Client.deleteObject(
78+
DeleteObjectRequest {
79+
bucket = bucketName
80+
this.key = key
81+
},
82+
)
7383

74-
s3Client
75-
.getObject {
76-
it.bucket(bucketName)
77-
it.key(key)
78-
}.use {
79-
assertThat(it.response().eTag()).isNotBlank
80-
}
84+
done.incrementAndGet()
85+
}
86+
}.awaitAll()
87+
}
8188

82-
s3Client
83-
.deleteObject {
84-
it.bucket(bucketName)
85-
it.key(key)
86-
}.let { response ->
87-
assertThat(response.deleteMarker()).isTrue
88-
}
89-
DONE.incrementAndGet()
90-
return true
89+
assertThat(done.get()).isEqualTo(totalRequests)
9190
}
92-
}
9391
}

integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/CopyObjectIT.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package com.adobe.testing.s3mock.its
1818

19-
import com.adobe.testing.s3mock.S3Exception.PRECONDITION_FAILED
19+
import com.adobe.testing.s3mock.S3Exception.Companion.PRECONDITION_FAILED
2020
import com.adobe.testing.s3mock.util.DigestUtil
2121
import org.assertj.core.api.Assertions.assertThat
2222
import org.assertj.core.api.Assertions.assertThatThrownBy

integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/CrtAsyncIT.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package com.adobe.testing.s3mock.its
1818

1919
import com.adobe.testing.s3mock.util.DigestUtil
20-
import org.apache.commons.codec.digest.DigestUtils
2120
import org.assertj.core.api.Assertions.assertThat
2221
import org.junit.jupiter.api.Test
2322
import org.junit.jupiter.api.TestInfo
@@ -29,6 +28,7 @@ import software.amazon.awssdk.transfer.s3.S3TransferManager
2928
import software.amazon.awssdk.transfer.s3.model.DownloadRequest
3029
import java.io.ByteArrayInputStream
3130
import java.nio.charset.StandardCharsets
31+
import java.security.MessageDigest
3232

3333
internal class CrtAsyncIT : S3TestBase() {
3434
private val autoS3CrtAsyncClient: S3AsyncClient = createAutoS3CrtAsyncClient()
@@ -163,10 +163,11 @@ internal class CrtAsyncIT : S3TestBase() {
163163
}, AsyncResponseTransformer.toBytes())
164164
.join()
165165

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

172173
// verify content size

0 commit comments

Comments
 (0)