3030import com .github .tomakehurst .wiremock .junit5 .WireMockRuntimeInfo ;
3131import com .github .tomakehurst .wiremock .junit5 .WireMockTest ;
3232import com .github .tomakehurst .wiremock .stubbing .Scenario ;
33+ import java .io .IOException ;
34+ import java .io .UncheckedIOException ;
3335import java .net .URI ;
36+ import java .nio .file .Files ;
37+ import java .nio .file .Path ;
3438import java .time .Duration ;
3539import java .util .ArrayList ;
3640import java .util .List ;
3741import java .util .UUID ;
3842import java .util .concurrent .CompletableFuture ;
3943import java .util .concurrent .CompletionException ;
4044import java .util .concurrent .TimeUnit ;
45+ import java .util .stream .Stream ;
4146import org .junit .jupiter .api .BeforeEach ;
42- import org .junit .jupiter .api .Test ;
4347import org .junit .jupiter .api .Timeout ;
48+ import org .junit .jupiter .params .ParameterizedTest ;
49+ import org .junit .jupiter .params .provider .MethodSource ;
4450import software .amazon .awssdk .auth .credentials .AwsBasicCredentials ;
4551import software .amazon .awssdk .auth .credentials .StaticCredentialsProvider ;
46- import software .amazon .awssdk .core .ResponseBytes ;
4752import software .amazon .awssdk .core .async .AsyncResponseTransformer ;
4853import software .amazon .awssdk .http .nio .netty .NettyNioAsyncHttpClient ;
4954import software .amazon .awssdk .regions .Region ;
5459@ WireMockTest
5560@ Timeout (value = 30 , unit = TimeUnit .SECONDS )
5661public class S3MultipartClientGetObjectWiremockTest {
57- public static final String BUCKET = "Example-Bucket" ;
58- public static final String KEY = "Key" ;
59- private static final int MAX_ATTEMPTS = 7 ;
62+ private static final String BUCKET = "Example-Bucket" ;
63+ private static final String KEY = "Key" ;
64+ private static int fileCounter = 0 ;
6065 private S3AsyncClient multipartClient ;
6166
6267 @ BeforeEach
@@ -72,13 +77,38 @@ public void setup(WireMockRuntimeInfo wm) {
7277 .build ();
7378 }
7479
75- @ Test
76- public void getObject_single500WithinMany200s_shouldNotRetryError () {
77- List <CompletableFuture <ResponseBytes <GetObjectResponse >>> futures = new ArrayList <>();
80+ private static Stream <TransformerFactory > responseTransformerFactories () {
81+ return Stream .of (
82+ AsyncResponseTransformer ::toBytes ,
83+ AsyncResponseTransformer ::toBlockingInputStream ,
84+ // TODO - hanging
85+ //AsyncResponseTransformer::toPublisher,
86+ () -> {
87+ try {
88+ Path tempDir = Files .createTempDirectory ("s3-test" );
89+ Path tempFile = tempDir .resolve ("testFile" + fileCounter + ".txt" );
90+ fileCounter ++;
91+ tempFile .toFile ().deleteOnExit ();
92+ return AsyncResponseTransformer .toFile (tempFile );
93+ } catch (IOException e ) {
94+ throw new UncheckedIOException (e );
95+ }
96+ }
97+ );
98+ }
99+
100+ interface TransformerFactory {
101+ AsyncResponseTransformer <GetObjectResponse , ?> create ();
102+ }
103+
104+ @ ParameterizedTest
105+ @ MethodSource ("responseTransformerFactories" )
106+ public void getObject_single500WithinMany200s_shouldNotRetryError (TransformerFactory transformerFactory ) {
107+ List <CompletableFuture <?>> futures = new ArrayList <>();
78108
79109 int numRuns = 1000 ;
80110 for (int i = 0 ; i < numRuns ; i ++) {
81- CompletableFuture <ResponseBytes < GetObjectResponse >> resp = mock200Response (multipartClient , i );
111+ CompletableFuture <?> resp = mock200Response (multipartClient , i , transformerFactory );
82112 futures .add (resp );
83113 }
84114
@@ -100,12 +130,12 @@ public void getObject_single500WithinMany200s_shouldNotRetryError() {
100130 .withHeader ("x-amz-request-id" , String .valueOf (UUID .randomUUID ()))
101131 .withBody ("Hello World" )));
102132
103- CompletableFuture <ResponseBytes < GetObjectResponse > > requestWithRetryableError =
104- multipartClient .getObject (r -> r .bucket (BUCKET ).key (errorKey ), AsyncResponseTransformer . toBytes ());
133+ CompletableFuture <? > requestWithRetryableError =
134+ multipartClient .getObject (r -> r .bucket (BUCKET ).key (errorKey ), transformerFactory . create ());
105135 futures .add (requestWithRetryableError );
106136
107137 for (int i = 0 ; i < numRuns ; i ++) {
108- CompletableFuture <ResponseBytes < GetObjectResponse >> resp = mock200Response (multipartClient , i + 1000 );
138+ CompletableFuture <?> resp = mock200Response (multipartClient , i + 1000 , transformerFactory );
109139 futures .add (resp );
110140 }
111141
@@ -119,19 +149,7 @@ public void getObject_single500WithinMany200s_shouldNotRetryError() {
119149 verify (1 , getRequestedFor (urlEqualTo (String .format ("/%s/%s?partNumber=1" , BUCKET , errorKey ))));
120150 }
121151
122- private String errorBody (String errorCode , String errorMessage ) {
123- return "<?xml version=\" 1.0\" encoding=\" UTF-8\" ?>\n "
124- + "<Error>\n "
125- + " <Code>" + errorCode + "</Code>\n "
126- + " <Message>" + errorMessage + "</Message>\n "
127- + "</Error>" ;
128- }
129-
130- private String internalErrorBody () {
131- return errorBody ("InternalError" , "We encountered an internal error. Please try again." );
132- }
133-
134- private CompletableFuture <ResponseBytes <GetObjectResponse >> mock200Response (S3AsyncClient s3Client , int runNumber ) {
152+ private CompletableFuture <?> mock200Response (S3AsyncClient s3Client , int runNumber , TransformerFactory transformerFactory ) {
135153 String runId = runNumber + " success" ;
136154
137155 stubFor (any (anyUrl ())
@@ -142,8 +160,20 @@ private CompletableFuture<ResponseBytes<GetObjectResponse>> mock200Response(S3As
142160 .withHeader ("x-amz-request-id" , String .valueOf (UUID .randomUUID ()))
143161 .withBody ("Hello World" )));
144162
145- return s3Client .getObject (r -> r .bucket (BUCKET ).key ("key" )
163+ return s3Client .getObject (r -> r .bucket (BUCKET ).key (KEY )
146164 .overrideConfiguration (c -> c .putHeader ("RunNum" , runId )),
147- AsyncResponseTransformer .toBytes ());
165+ transformerFactory .create ());
166+ }
167+
168+ private String errorBody (String errorCode , String errorMessage ) {
169+ return "<?xml version=\" 1.0\" encoding=\" UTF-8\" ?>\n "
170+ + "<Error>\n "
171+ + " <Code>" + errorCode + "</Code>\n "
172+ + " <Message>" + errorMessage + "</Message>\n "
173+ + "</Error>" ;
174+ }
175+
176+ private String internalErrorBody () {
177+ return errorBody ("InternalError" , "We encountered an internal error. Please try again." );
148178 }
149179}
0 commit comments