@@ -38,6 +38,14 @@ namespace AWSSDK_DotNet.IntegrationTests.Tests.S3
38
38
[ TestClass ]
39
39
public class CreatePresignedPostTests : TestBase < AmazonS3Client >
40
40
{
41
+ // Test result classes for better structure
42
+ private class UploadResult
43
+ {
44
+ public bool IsSuccessful { get ; set ; }
45
+ public HttpStatusCode StatusCode { get ; set ; }
46
+ public string ResponseText { get ; set ; }
47
+ }
48
+
41
49
private const string TestContent = "This is the content body!" ;
42
50
private const string TestKey = "presigned-post-key" ;
43
51
@@ -216,109 +224,189 @@ private void TestPresignedPostWithConditions(PresignedPostTestParameters testPar
216
224
}
217
225
}
218
226
219
- private void TestPresignedPostWithMixedContentType ( PresignedPostTestParameters testParams )
227
+ // Helper methods for creating and working with presigned POST URLs
228
+ private CreatePresignedPostResponse GeneratePresignedPostRequest (
229
+ AmazonS3Client client ,
230
+ string bucketName ,
231
+ string objectKey ,
232
+ DateTime expiration ,
233
+ Dictionary < string , string > fields = null ,
234
+ List < S3PostCondition > conditions = null )
220
235
{
221
- var client = new AmazonS3Client ( testParams . Region ) ;
222
- try
236
+ var request = new CreatePresignedPostRequest
223
237
{
224
- // Create regular bucket
225
- testParams . BucketName = S3TestUtils . CreateBucketWithWait ( client ) ;
226
-
227
- string objectKey = TestKey + DateTime . UtcNow . Ticks ;
228
-
229
- // Generate presigned POST response
230
- var request = new CreatePresignedPostRequest
231
- {
232
- BucketName = testParams . BucketName ,
233
- Key = objectKey ,
234
- Expires = testParams . Expiration
235
- } ;
238
+ BucketName = bucketName ,
239
+ Key = objectKey ,
240
+ Expires = expiration
241
+ } ;
236
242
237
- // Add custom fields
238
- foreach ( var field in testParams . Fields )
243
+ // Add custom fields if provided
244
+ if ( fields != null )
245
+ {
246
+ foreach ( var field in fields )
239
247
{
240
248
request . Fields . Add ( field . Key , field . Value ) ;
241
249
}
250
+ }
242
251
243
- // Add conditions
244
- foreach ( var condition in testParams . Conditions )
252
+ // Add conditions if provided
253
+ if ( conditions != null )
254
+ {
255
+ foreach ( var condition in conditions )
245
256
{
246
257
request . Conditions . Add ( condition ) ;
247
258
}
259
+ }
260
+
261
+ return client . CreatePresignedPost ( request ) ;
262
+ }
263
+
264
+ // Validates that Content-Type field exists with expected value
265
+ private void ValidateContentTypeFieldPresent ( CreatePresignedPostResponse response , string expectedContentType )
266
+ {
267
+ Assert . IsTrue ( response . Fields . ContainsKey ( "Content-Type" ) ,
268
+ "Content-Type should be included in response fields even with a starts-with condition" ) ;
269
+ Assert . AreEqual ( expectedContentType , response . Fields [ "Content-Type" ] ) ;
270
+ }
271
+
272
+ // Performs an upload with valid Content-Type
273
+ private UploadResult PerformUpload (
274
+ string url ,
275
+ Dictionary < string , string > fields ,
276
+ string content ,
277
+ string objectKey ,
278
+ string contentType )
279
+ {
280
+ var formData = new MultipartFormDataContent ( ) ;
281
+
282
+ // Add all form fields
283
+ foreach ( var field in fields )
284
+ {
285
+ formData . Add ( new StringContent ( field . Value ) , field . Key ) ;
286
+ }
248
287
249
- var response = client . CreatePresignedPost ( request ) ;
288
+ // Add file content with proper Content-Type
289
+ var fileContent = new ByteArrayContent ( Encoding . UTF8 . GetBytes ( content ) ) ;
290
+ fileContent . Headers . ContentType = new System . Net . Http . Headers . MediaTypeHeaderValue ( contentType ) ;
291
+ formData . Add ( fileContent , "file" , objectKey ) ;
250
292
251
- // VALIDATION 1: Content-Type should be included in response fields
252
- // matching the JavaScript SDK behavior
253
- Assert . IsTrue ( response . Fields . ContainsKey ( "Content-Type" ) ,
254
- "Content-Type should be included in response fields even with a starts-with condition" ) ;
255
- Assert . AreEqual ( "text/plain" , response . Fields [ "Content-Type" ] ) ;
293
+ using ( var httpClient = new System . Net . Http . HttpClient ( ) )
294
+ {
295
+ // Send the POST request
296
+ var httpResponse = httpClient . PostAsync ( url , formData ) . Result ;
256
297
257
- // Use the presigned post form to upload a file
258
- var formData = new MultipartFormDataContent ( ) ;
298
+ return new UploadResult
299
+ {
300
+ IsSuccessful = httpResponse . IsSuccessStatusCode ,
301
+ StatusCode = httpResponse . StatusCode ,
302
+ ResponseText = httpResponse . Content . ReadAsStringAsync ( ) . Result
303
+ } ;
304
+ }
305
+ }
306
+
307
+ // Validates that the object was uploaded correctly
308
+ private void ValidateObjectContent ( AmazonS3Client client , string bucketName , string objectKey , string expectedContent )
309
+ {
310
+ var getObjectResponse = client . GetObject ( bucketName , objectKey ) ;
311
+ using ( var reader = new StreamReader ( getObjectResponse . ResponseStream ) )
312
+ {
313
+ var content = reader . ReadToEnd ( ) ;
314
+ Assert . AreEqual ( expectedContent , content , "Object content does not match expected content" ) ;
315
+ }
316
+ }
259
317
260
- // Add all form fields
261
- foreach ( var field in response . Fields )
318
+ // Performs an upload with invalid Content-Type to test condition enforcement
319
+ private UploadResult PerformInvalidContentTypeUpload (
320
+ string url ,
321
+ Dictionary < string , string > fields ,
322
+ string content ,
323
+ string objectKey )
324
+ {
325
+ var formData = new MultipartFormDataContent ( ) ;
326
+
327
+ // Add all fields from response
328
+ foreach ( var field in fields )
329
+ {
330
+ if ( field . Key == "Content-Type" )
331
+ {
332
+ // Override Content-Type with an invalid one
333
+ formData . Add ( new StringContent ( "image/jpeg" ) , field . Key ) ;
334
+ }
335
+ else
262
336
{
263
337
formData . Add ( new StringContent ( field . Value ) , field . Key ) ;
264
338
}
339
+ }
340
+
341
+ // Add file with invalid Content-Type header
342
+ var invalidFileContent = new ByteArrayContent ( Encoding . UTF8 . GetBytes ( content ) ) ;
343
+ invalidFileContent . Headers . ContentType = new System . Net . Http . Headers . MediaTypeHeaderValue ( "image/jpeg" ) ;
344
+ formData . Add ( invalidFileContent , "file" , objectKey ) ;
345
+
346
+ using ( var httpClient = new System . Net . Http . HttpClient ( ) )
347
+ {
348
+ var httpResponse = httpClient . PostAsync ( url , formData ) . Result ;
349
+
350
+ return new UploadResult
351
+ {
352
+ IsSuccessful = httpResponse . IsSuccessStatusCode ,
353
+ StatusCode = httpResponse . StatusCode ,
354
+ ResponseText = httpResponse . Content . ReadAsStringAsync ( ) . Result
355
+ } ;
356
+ }
357
+ }
265
358
266
- // Add file content with proper Content-Type
267
- var fileContent = new ByteArrayContent ( Encoding . UTF8 . GetBytes ( TestContent ) ) ;
268
- fileContent . Headers . ContentType = new System . Net . Http . Headers . MediaTypeHeaderValue ( "text/plain" ) ;
269
- formData . Add ( fileContent , "file" , objectKey ) ;
359
+ private void TestPresignedPostWithMixedContentType ( PresignedPostTestParameters testParams )
360
+ {
361
+ var client = new AmazonS3Client ( testParams . Region ) ;
362
+ try
363
+ {
364
+ // Create regular bucket
365
+ testParams . BucketName = S3TestUtils . CreateBucketWithWait ( client ) ;
366
+
367
+ // Create a unique object key
368
+ string objectKey = TestKey + DateTime . UtcNow . Ticks ;
270
369
271
- // Create and configure the HttpClient
272
- using ( var httpClient = new System . Net . Http . HttpClient ( ) )
273
- {
274
- // Send the POST request
275
- var httpResponse = httpClient . PostAsync ( response . Url , formData ) . Result ;
276
-
277
- // VALIDATION 2: Verify the upload was successful
278
- Assert . IsTrue ( httpResponse . IsSuccessStatusCode ,
279
- $ "Upload failed with status code { httpResponse . StatusCode } ") ;
280
-
281
- // VALIDATION 3: Verify the uploaded object exists and has the correct content
282
- var getObjectResponse = client . GetObject ( testParams . BucketName , objectKey ) ;
283
- using ( var reader = new StreamReader ( getObjectResponse . ResponseStream ) )
284
- {
285
- var content = reader . ReadToEnd ( ) ;
286
- Assert . AreEqual ( TestContent , content ) ;
287
- }
288
-
289
- // Delete the object for the next test
290
- client . DeleteObject ( testParams . BucketName , objectKey ) ;
291
-
292
- // VALIDATION 4: Test with an invalid Content-Type that doesn't match the starts-with condition
293
- // This tests that the condition is enforced despite having Content-Type in fields
294
- var invalidFormData = new MultipartFormDataContent ( ) ;
295
-
296
- // Add all fields from response
297
- foreach ( var field in response . Fields )
298
- {
299
- if ( field . Key == "Content-Type" )
300
- {
301
- // Override Content-Type with an invalid one
302
- invalidFormData . Add ( new StringContent ( "image/jpeg" ) , field . Key ) ;
303
- }
304
- else
305
- {
306
- invalidFormData . Add ( new StringContent ( field . Value ) , field . Key ) ;
307
- }
308
- }
309
-
310
- // Add file with invalid Content-Type header
311
- var invalidFileContent = new ByteArrayContent ( Encoding . UTF8 . GetBytes ( TestContent ) ) ;
312
- invalidFileContent . Headers . ContentType = new System . Net . Http . Headers . MediaTypeHeaderValue ( "image/jpeg" ) ;
313
- invalidFormData . Add ( invalidFileContent , "file" , objectKey ) ;
314
-
315
- // This request should fail with 403 Forbidden
316
- var invalidResponse = httpClient . PostAsync ( response . Url , invalidFormData ) . Result ;
317
-
318
- // VALIDATION 5: Verify the upload with invalid Content-Type was rejected
319
- Assert . AreEqual ( HttpStatusCode . Forbidden , invalidResponse . StatusCode ,
320
- "Upload with invalid Content-Type should be rejected" ) ;
321
- }
370
+ // Step 1: Generate presigned POST response
371
+ var response = GeneratePresignedPostRequest (
372
+ client ,
373
+ testParams . BucketName ,
374
+ objectKey ,
375
+ testParams . Expiration ,
376
+ testParams . Fields ,
377
+ testParams . Conditions ) ;
378
+
379
+ // Step 2: Verify Content-Type field is included in response
380
+ ValidateContentTypeFieldPresent ( response , "text/plain" ) ;
381
+
382
+ // Step 3: Perform upload with valid Content-Type
383
+ var uploadResult = PerformUpload (
384
+ response . Url ,
385
+ response . Fields ,
386
+ TestContent ,
387
+ objectKey ,
388
+ "text/plain" ) ;
389
+
390
+ // Step 4: Verify upload was successful
391
+ Assert . IsTrue ( uploadResult . IsSuccessful ,
392
+ $ "Upload failed with status code { uploadResult . StatusCode } ") ;
393
+
394
+ // Step 5: Verify uploaded content
395
+ ValidateObjectContent ( client , testParams . BucketName , objectKey , TestContent ) ;
396
+
397
+ // Step 6: Clean up for next test
398
+ client . DeleteObject ( testParams . BucketName , objectKey ) ;
399
+
400
+ // Step 7: Test with invalid Content-Type
401
+ var invalidResult = PerformInvalidContentTypeUpload (
402
+ response . Url ,
403
+ response . Fields ,
404
+ TestContent ,
405
+ objectKey ) ;
406
+
407
+ // Step 8: Verify upload with invalid Content-Type was rejected
408
+ Assert . AreEqual ( HttpStatusCode . Forbidden , invalidResult . StatusCode ,
409
+ "Upload with invalid Content-Type should be rejected" ) ;
322
410
}
323
411
finally
324
412
{
0 commit comments