@@ -35,6 +35,7 @@ import java.time.Duration
3535import java.time.Instant
3636import java.time.temporal.ChronoUnit.SECONDS
3737import java.util.concurrent.Executors
38+ import java.util.concurrent.TimeUnit
3839
3940/* *
4041 * Test the application using the AmazonS3 SDK V2.
@@ -133,6 +134,127 @@ internal class CopyObjectIT : S3TestBase() {
133134 }
134135 }
135136
137+ @Test
138+ @S3VerifiedSuccess(year = 2025 )
139+ fun `copy object with if match true and if unmodified since false succeeds and object can be retrieved` (testInfo : TestInfo ) {
140+ val sourceKey = UPLOAD_FILE_NAME
141+ val (bucketName, putObjectResult) = givenBucketAndObject(testInfo, sourceKey)
142+ TimeUnit .SECONDS .sleep(5 )
143+ val now = Instant .now()
144+ val destinationBucketName = givenBucket()
145+ val destinationKey = " copyOf/$sourceKey "
146+
147+ val matchingEtag = putObjectResult.eTag()
148+
149+ s3Client.copyObject {
150+ it.sourceBucket(bucketName)
151+ it.sourceKey(sourceKey)
152+ it.destinationBucket(destinationBucketName)
153+ it.destinationKey(destinationKey)
154+ it.copySourceIfMatch(matchingEtag)
155+ it.copySourceIfUnmodifiedSince(now)
156+ }.copyObjectResult().eTag().also {
157+ assertThat(it).isEqualTo(putObjectResult.eTag())
158+ }
159+
160+ s3Client.getObject {
161+ it.bucket(destinationBucketName)
162+ it.key(destinationKey)
163+ }.use {
164+ val copiedDigest = DigestUtil .hexDigest(it)
165+ assertThat(" \" $copiedDigest \" " ).isEqualTo(matchingEtag)
166+ }
167+ }
168+
169+ @Test
170+ @S3VerifiedSuccess(year = 2025 )
171+ fun `copy object with if modified since succeeds and object can be retrieved` (testInfo : TestInfo ) {
172+ val sourceKey = UPLOAD_FILE_NAME
173+ val now = Instant .now()
174+ TimeUnit .SECONDS .sleep(5 )
175+ val (bucketName, putObjectResult) = givenBucketAndObject(testInfo, sourceKey)
176+ val destinationBucketName = givenBucket()
177+ val destinationKey = " copyOf/$sourceKey "
178+
179+ val matchingEtag = putObjectResult.eTag()
180+
181+ s3Client.copyObject {
182+ it.sourceBucket(bucketName)
183+ it.sourceKey(sourceKey)
184+ it.destinationBucket(destinationBucketName)
185+ it.destinationKey(destinationKey)
186+ it.copySourceIfModifiedSince(now)
187+ }.copyObjectResult().eTag().also {
188+ assertThat(it).isEqualTo(putObjectResult.eTag())
189+ }
190+
191+ s3Client.getObject {
192+ it.bucket(destinationBucketName)
193+ it.key(destinationKey)
194+ }.use {
195+ val copiedDigest = DigestUtil .hexDigest(it)
196+ assertThat(" \" $copiedDigest \" " ).isEqualTo(matchingEtag)
197+ }
198+ }
199+
200+ @Test
201+ @S3VerifiedSuccess(year = 2025 )
202+ fun `copy object with if modified since true and if none match false fails` (testInfo : TestInfo ) {
203+ val sourceKey = UPLOAD_FILE_NAME
204+ val now = Instant .now()
205+ TimeUnit .SECONDS .sleep(5 )
206+ val (bucketName, putObjectResult) = givenBucketAndObject(testInfo, sourceKey)
207+ val destinationBucketName = givenBucket()
208+ val destinationKey = " copyOf/$sourceKey "
209+
210+ val matchingEtag = putObjectResult.eTag()
211+
212+ assertThatThrownBy {
213+ s3Client.copyObject {
214+ it.sourceBucket(bucketName)
215+ it.sourceKey(sourceKey)
216+ it.destinationBucket(destinationBucketName)
217+ it.destinationKey(destinationKey)
218+ it.copySourceIfModifiedSince(now)
219+ it.copySourceIfNoneMatch(matchingEtag)
220+ }
221+ }
222+ .isInstanceOf(S3Exception ::class .java)
223+ .hasMessageContaining(" Service: S3, Status Code: 412" )
224+ .hasMessageContaining(PRECONDITION_FAILED .message)
225+ }
226+
227+ @Test
228+ @S3VerifiedSuccess(year = 2025 )
229+ fun `copy object with if unmodified since succeeds and object can be retrieved` (testInfo : TestInfo ) {
230+ val sourceKey = UPLOAD_FILE_NAME
231+ val (bucketName, putObjectResult) = givenBucketAndObject(testInfo, sourceKey)
232+ TimeUnit .SECONDS .sleep(5 )
233+ val now = Instant .now()
234+ val destinationBucketName = givenBucket()
235+ val destinationKey = " copyOf/$sourceKey "
236+
237+ val matchingEtag = putObjectResult.eTag()
238+
239+ s3Client.copyObject {
240+ it.sourceBucket(bucketName)
241+ it.sourceKey(sourceKey)
242+ it.destinationBucket(destinationBucketName)
243+ it.destinationKey(destinationKey)
244+ it.copySourceIfUnmodifiedSince(now)
245+ }.copyObjectResult().eTag().also {
246+ assertThat(it).isEqualTo(putObjectResult.eTag())
247+ }
248+
249+ s3Client.getObject {
250+ it.bucket(destinationBucketName)
251+ it.key(destinationKey)
252+ }.use {
253+ val copiedDigest = DigestUtil .hexDigest(it)
254+ assertThat(" \" $copiedDigest \" " ).isEqualTo(matchingEtag)
255+ }
256+ }
257+
136258 @Test
137259 @S3VerifiedSuccess(year = 2025 )
138260 fun `copy object with if nonematch succeeds and object can be retrieved` (testInfo : TestInfo ) {
0 commit comments