2323import java .util .ArrayList ;
2424import java .util .List ;
2525import java .util .Map ;
26+ import java .util .concurrent .CountDownLatch ;
27+
2628import org .assertj .core .data .MapEntry ;
2729import org .junit .jupiter .api .BeforeEach ;
2830import org .junit .jupiter .api .Test ;
3234import org .mockito .Mock ;
3335import org .mockito .Mockito ;
3436import org .mockito .MockitoAnnotations ;
37+
3538import software .amazon .awssdk .services .ssm .SsmClient ;
3639import software .amazon .awssdk .services .ssm .model .GetParameterRequest ;
3740import software .amazon .awssdk .services .ssm .model .GetParameterResponse ;
@@ -165,8 +168,8 @@ public void getMultipleWithNextToken() {
165168 List <Parameter > parameters1 = new ArrayList <>();
166169 parameters1 .add (Parameter .builder ().name ("/prod/app1/key1" ).value ("foo1" ).build ());
167170 parameters1 .add (Parameter .builder ().name ("/prod/app1/key2" ).value ("foo2" ).build ());
168- GetParametersByPathResponse response1 =
169- GetParametersByPathResponse . builder (). parameters ( parameters1 ) .nextToken ("123abc" ).build ();
171+ GetParametersByPathResponse response1 = GetParametersByPathResponse . builder (). parameters ( parameters1 )
172+ .nextToken ("123abc" ).build ();
170173
171174 List <Parameter > parameters2 = new ArrayList <>();
172175 parameters2 .add (Parameter .builder ().name ("/prod/app1/key3" ).value ("foo3" ).build ());
@@ -185,8 +188,7 @@ public void getMultipleWithNextToken() {
185188 GetParametersByPathRequest request1 = requestParams .get (0 );
186189 GetParametersByPathRequest request2 = requestParams .get (1 );
187190
188- assertThat (asList (request1 , request2 )).allSatisfy (req ->
189- {
191+ assertThat (asList (request1 , request2 )).allSatisfy (req -> {
190192 assertThat (req .path ()).isEqualTo ("/prod/app1" );
191193 assertThat (req .withDecryption ()).isFalse ();
192194 assertThat (req .recursive ()).isFalse ();
@@ -203,7 +205,101 @@ public void testSSMProvider_withoutParameter_shouldHaveDefaultTransformationMana
203205 SSMProvider ssmProvider = SSMProvider .builder ()
204206 .build ();
205207 // Assert
206- assertDoesNotThrow (()->ssmProvider .withTransformation (json ));
208+ assertDoesNotThrow (() -> ssmProvider .withTransformation (json ));
209+ }
210+
211+ @ Test
212+ public void withDecryption_concurrentCalls_shouldBeThreadSafe () throws InterruptedException {
213+ // GIVEN
214+ Parameter param1 = Parameter .builder ().value ("value1" ).build ();
215+ Parameter param2 = Parameter .builder ().value ("value2" ).build ();
216+ GetParameterResponse response1 = GetParameterResponse .builder ().parameter (param1 ).build ();
217+ GetParameterResponse response2 = GetParameterResponse .builder ().parameter (param2 ).build ();
218+ CountDownLatch latch = new CountDownLatch (2 );
219+ Mockito .when (client .getParameter (paramCaptor .capture ()))
220+ .thenReturn (response1 , response2 );
221+
222+ // WHEN
223+ Thread thread1 = new Thread (() -> {
224+ try {
225+ latch .countDown ();
226+ latch .await ();
227+ provider .withDecryption ().getValue ("key1" );
228+ } catch (InterruptedException e ) {
229+ Thread .currentThread ().interrupt ();
230+ }
231+ });
232+
233+ Thread thread2 = new Thread (() -> {
234+ try {
235+ latch .countDown ();
236+ latch .await ();
237+ provider .getValue ("key2" );
238+ } catch (InterruptedException e ) {
239+ Thread .currentThread ().interrupt ();
240+ }
241+ });
242+
243+ thread1 .start ();
244+ thread2 .start ();
245+ thread1 .join ();
246+ thread2 .join ();
247+
248+ // THEN
249+ List <GetParameterRequest > requests = paramCaptor .getAllValues ();
250+ assertThat (requests ).hasSize (2 );
251+ boolean hasDecryptedRequest = requests .stream ().anyMatch (GetParameterRequest ::withDecryption );
252+ boolean hasNonDecryptedRequest = requests .stream ().anyMatch (r -> !r .withDecryption ());
253+ assertThat (hasDecryptedRequest ).isTrue ();
254+ assertThat (hasNonDecryptedRequest ).isTrue ();
255+ }
256+
257+ @ Test
258+ public void recursive_concurrentCalls_shouldBeThreadSafe () throws InterruptedException {
259+ // GIVEN
260+ List <Parameter > params1 = new ArrayList <>();
261+ params1 .add (Parameter .builder ().name ("/path1/key1" ).value ("value1" ).build ());
262+ List <Parameter > params2 = new ArrayList <>();
263+ params2 .add (Parameter .builder ().name ("/path2/key2" ).value ("value2" ).build ());
264+ GetParametersByPathResponse response1 = GetParametersByPathResponse .builder ().parameters (params1 ).build ();
265+ GetParametersByPathResponse response2 = GetParametersByPathResponse .builder ().parameters (params2 ).build ();
266+ CountDownLatch latch = new CountDownLatch (2 );
267+ Mockito .when (client .getParametersByPath (paramByPathCaptor .capture ()))
268+ .thenReturn (response1 , response2 );
269+
270+ // WHEN
271+ Thread thread1 = new Thread (() -> {
272+ try {
273+ latch .countDown ();
274+ latch .await ();
275+ provider .recursive ().getMultiple ("/path1" );
276+ } catch (InterruptedException e ) {
277+ Thread .currentThread ().interrupt ();
278+ }
279+ });
280+
281+ Thread thread2 = new Thread (() -> {
282+ try {
283+ latch .countDown ();
284+ latch .await ();
285+ provider .getMultiple ("/path2" );
286+ } catch (InterruptedException e ) {
287+ Thread .currentThread ().interrupt ();
288+ }
289+ });
290+
291+ thread1 .start ();
292+ thread2 .start ();
293+ thread1 .join ();
294+ thread2 .join ();
295+
296+ // THEN
297+ List <GetParametersByPathRequest > requests = paramByPathCaptor .getAllValues ();
298+ assertThat (requests ).hasSize (2 );
299+ boolean hasRecursiveRequest = requests .stream ().anyMatch (GetParametersByPathRequest ::recursive );
300+ boolean hasNonRecursiveRequest = requests .stream ().anyMatch (r -> !r .recursive ());
301+ assertThat (hasRecursiveRequest ).isTrue ();
302+ assertThat (hasNonRecursiveRequest ).isTrue ();
207303 }
208304
209305 private void initMock (String expectedValue ) {
0 commit comments