1616package com .datastax .cdm .data ;
1717
1818import static org .junit .jupiter .api .Assertions .*;
19+ import static org .mockito .ArgumentMatchers .*;
1920import static org .mockito .Mockito .*;
2021
22+ import java .io .ByteArrayInputStream ;
2123import java .io .IOException ;
24+ import java .io .InputStream ;
2225import java .lang .reflect .Field ;
2326import java .lang .reflect .Method ;
27+ import java .net .URI ;
2428import java .net .http .HttpClient ;
29+ import java .net .http .HttpRequest ;
2530import java .net .http .HttpResponse ;
2631
2732import org .junit .jupiter .api .BeforeEach ;
2833import org .junit .jupiter .api .Test ;
2934import org .junit .jupiter .api .extension .ExtendWith ;
35+ import org .mockito .ArgumentCaptor ;
3036import org .mockito .Mock ;
3137import org .mockito .junit .jupiter .MockitoExtension ;
3238
@@ -46,6 +52,9 @@ class AstraDevOpsClientTest {
4652 @ Mock
4753 private HttpResponse <String > httpResponse ;
4854
55+ @ Mock
56+ private HttpResponse <InputStream > httpResponseStream ;
57+
4958 private AstraDevOpsClient client ;
5059
5160 @ BeforeEach
@@ -197,5 +206,301 @@ void testExtractDownloadUrlDefaultType() throws Exception {
197206 assertEquals ("https://example.com/bundle.zip" , url );
198207 }
199208
200- // More tests for extractDownloadUrl with different SCB types can be added here
201- }
209+ @ Test
210+ void testExtractDownloadUrlRegionType () throws Exception {
211+ // Setup
212+ String jsonResponse = "{ \" downloadURLs\" : ["
213+ + "{ \" region\" : \" us-east-1\" , \" downloadURL\" : \" https://us-east-1.example.com/bundle.zip\" },"
214+ + "{ \" region\" : \" us-west-2\" , \" downloadURL\" : \" https://us-west-2.example.com/bundle.zip\" },"
215+ + "{ \" region\" : \" eu-central-1\" , \" downloadURL\" : \" https://eu-central-1.example.com/bundle.zip\" }"
216+ + "]}" ;
217+
218+ when (propertyHelper .getAsString (KnownProperties .ORIGIN_ASTRA_SCB_REGION )).thenReturn ("us-west-2" );
219+
220+ // Use reflection to access the private method
221+ Method extractDownloadUrlMethod = AstraDevOpsClient .class .getDeclaredMethod ("extractDownloadUrl" , String .class ,
222+ String .class , PKFactory .Side .class );
223+ extractDownloadUrlMethod .setAccessible (true );
224+
225+ // Test
226+ String url = (String ) extractDownloadUrlMethod .invoke (client , jsonResponse , "region" , PKFactory .Side .ORIGIN );
227+
228+ // Verify
229+ assertEquals ("https://us-west-2.example.com/bundle.zip" , url );
230+ }
231+
232+ @ Test
233+ void testExtractDownloadUrlRegionTypeWithMissingRegion () throws Exception {
234+ // Setup
235+ String jsonResponse = "{ \" downloadURLs\" : ["
236+ + "{ \" region\" : \" us-east-1\" , \" downloadURL\" : \" https://us-east-1.example.com/bundle.zip\" },"
237+ + "{ \" region\" : \" us-west-2\" , \" downloadURL\" : \" https://us-west-2.example.com/bundle.zip\" }" + "]}" ;
238+
239+ when (propertyHelper .getAsString (KnownProperties .ORIGIN_ASTRA_SCB_REGION )).thenReturn (null );
240+
241+ // Use reflection to access the private method
242+ Method extractDownloadUrlMethod = AstraDevOpsClient .class .getDeclaredMethod ("extractDownloadUrl" , String .class ,
243+ String .class , PKFactory .Side .class );
244+ extractDownloadUrlMethod .setAccessible (true );
245+
246+ // Test
247+ String url = (String ) extractDownloadUrlMethod .invoke (client , jsonResponse , "region" , PKFactory .Side .ORIGIN );
248+
249+ // Verify
250+ assertNull (url );
251+ }
252+
253+ @ Test
254+ void testExtractDownloadUrlRegionTypeWithNoMatchingRegion () throws Exception {
255+ // Setup
256+ String jsonResponse = "{ \" downloadURLs\" : ["
257+ + "{ \" region\" : \" us-east-1\" , \" downloadURL\" : \" https://us-east-1.example.com/bundle.zip\" },"
258+ + "{ \" region\" : \" us-west-2\" , \" downloadURL\" : \" https://us-west-2.example.com/bundle.zip\" }" + "]}" ;
259+
260+ when (propertyHelper .getAsString (KnownProperties .ORIGIN_ASTRA_SCB_REGION )).thenReturn ("ap-south-1" );
261+
262+ // Use reflection to access the private method
263+ Method extractDownloadUrlMethod = AstraDevOpsClient .class .getDeclaredMethod ("extractDownloadUrl" , String .class ,
264+ String .class , PKFactory .Side .class );
265+ extractDownloadUrlMethod .setAccessible (true );
266+
267+ // Test
268+ String url = (String ) extractDownloadUrlMethod .invoke (client , jsonResponse , "region" , PKFactory .Side .ORIGIN );
269+
270+ // Verify
271+ assertNull (url );
272+ }
273+
274+ @ Test
275+ void testExtractDownloadUrlCustomDomainType () throws Exception {
276+ // Setup
277+ String jsonResponse = "{ \" customDomainBundles\" : ["
278+ + "{ \" domain\" : \" db1.example.com\" , \" downloadURL\" : \" https://db1.example.com/bundle.zip\" },"
279+ + "{ \" domain\" : \" db2.example.com\" , \" downloadURL\" : \" https://db2.example.com/bundle.zip\" }" + "]}" ;
280+
281+ when (propertyHelper .getAsString (KnownProperties .TARGET_ASTRA_SCB_CUSTOM_DOMAIN )).thenReturn ("db2.example.com" );
282+
283+ // Use reflection to access the private method
284+ Method extractDownloadUrlMethod = AstraDevOpsClient .class .getDeclaredMethod ("extractDownloadUrl" , String .class ,
285+ String .class , PKFactory .Side .class );
286+ extractDownloadUrlMethod .setAccessible (true );
287+
288+ // Test
289+ String url = (String ) extractDownloadUrlMethod .invoke (client , jsonResponse , "custom" , PKFactory .Side .TARGET );
290+
291+ // Verify
292+ assertEquals ("https://db2.example.com/bundle.zip" , url );
293+ }
294+
295+ @ Test
296+ void testExtractDownloadUrlCustomDomainTypeWithMissingDomain () throws Exception {
297+ // Setup
298+ String jsonResponse = "{ \" customDomainBundles\" : ["
299+ + "{ \" domain\" : \" db1.example.com\" , \" downloadURL\" : \" https://db1.example.com/bundle.zip\" },"
300+ + "{ \" domain\" : \" db2.example.com\" , \" downloadURL\" : \" https://db2.example.com/bundle.zip\" }" + "]}" ;
301+
302+ when (propertyHelper .getAsString (KnownProperties .TARGET_ASTRA_SCB_CUSTOM_DOMAIN )).thenReturn (null );
303+
304+ // Use reflection to access the private method
305+ Method extractDownloadUrlMethod = AstraDevOpsClient .class .getDeclaredMethod ("extractDownloadUrl" , String .class ,
306+ String .class , PKFactory .Side .class );
307+ extractDownloadUrlMethod .setAccessible (true );
308+
309+ // Test
310+ String url = (String ) extractDownloadUrlMethod .invoke (client , jsonResponse , "custom" , PKFactory .Side .TARGET );
311+
312+ // Verify
313+ assertNull (url );
314+ }
315+
316+ @ Test
317+ void testExtractDownloadUrlUnknownType () throws Exception {
318+ // Setup
319+ String jsonResponse = "{ \" downloadURL\" : \" https://example.com/bundle.zip\" }" ;
320+
321+ // Use reflection to access the private method
322+ Method extractDownloadUrlMethod = AstraDevOpsClient .class .getDeclaredMethod ("extractDownloadUrl" , String .class ,
323+ String .class , PKFactory .Side .class );
324+ extractDownloadUrlMethod .setAccessible (true );
325+
326+ // Test
327+ String url = (String ) extractDownloadUrlMethod .invoke (client , jsonResponse , "unknown" , PKFactory .Side .ORIGIN );
328+
329+ // Verify
330+ assertNull (url );
331+ }
332+
333+ @ Test
334+ void testFetchSecureBundleUrlInfo () throws Exception {
335+ // Mock the HTTP response
336+ when (httpResponse .statusCode ()).thenReturn (200 );
337+ when (httpResponse .body ()).thenReturn ("{ \" downloadURL\" : \" https://example.com/bundle.zip\" }" );
338+
339+ // Mock the HTTP client to return our mocked response
340+ when (httpClient .send (any (), eq (HttpResponse .BodyHandlers .ofString ()))).thenReturn (httpResponse );
341+
342+ // Use reflection to access the private method
343+ Method fetchSecureBundleUrlInfoMethod = AstraDevOpsClient .class .getDeclaredMethod (
344+ "fetchSecureBundleUrlInfo" , String .class , String .class , boolean .class );
345+ fetchSecureBundleUrlInfoMethod .setAccessible (true );
346+
347+ // Test
348+ String jsonResponse = (String ) fetchSecureBundleUrlInfoMethod .invoke (client , "test-token" , "test-db-id" , false );
349+
350+ // Verify
351+ assertEquals ("{ \" downloadURL\" : \" https://example.com/bundle.zip\" }" , jsonResponse );
352+
353+ // Verify the correct URL was used
354+ ArgumentCaptor <HttpRequest > requestCaptor = ArgumentCaptor .forClass (HttpRequest .class );
355+ verify (httpClient ).send (requestCaptor .capture (), eq (HttpResponse .BodyHandlers .ofString ()));
356+
357+ HttpRequest capturedRequest = requestCaptor .getValue ();
358+ assertEquals (URI .create ("https://api.astra.datastax.com/v2/databases/test-db-id/secureBundleURL" ),
359+ capturedRequest .uri ());
360+ assertTrue (capturedRequest .headers ().firstValue ("Authorization" ).isPresent ());
361+ assertEquals ("Bearer test-token" , capturedRequest .headers ().firstValue ("Authorization" ).get ());
362+ }
363+
364+ @ Test
365+ void testFetchSecureBundleUrlInfoWithRegionalFlag () throws Exception {
366+ // Mock the HTTP response
367+ when (httpResponse .statusCode ()).thenReturn (200 );
368+ when (httpResponse .body ()).thenReturn ("{ \" downloadURLs\" : [] }" );
369+
370+ // Mock the HTTP client to return our mocked response
371+ when (httpClient .send (any (), eq (HttpResponse .BodyHandlers .ofString ()))).thenReturn (httpResponse );
372+
373+ // Use reflection to access the private method
374+ Method fetchSecureBundleUrlInfoMethod = AstraDevOpsClient .class .getDeclaredMethod (
375+ "fetchSecureBundleUrlInfo" , String .class , String .class , boolean .class );
376+ fetchSecureBundleUrlInfoMethod .setAccessible (true );
377+
378+ // Test
379+ String jsonResponse = (String ) fetchSecureBundleUrlInfoMethod .invoke (client , "test-token" , "test-db-id" , true );
380+
381+ // Verify
382+ assertEquals ("{ \" downloadURLs\" : [] }" , jsonResponse );
383+
384+ // Verify the correct URL was used with all=true parameter
385+ ArgumentCaptor <HttpRequest > requestCaptor = ArgumentCaptor .forClass (HttpRequest .class );
386+ verify (httpClient ).send (requestCaptor .capture (), eq (HttpResponse .BodyHandlers .ofString ()));
387+
388+ HttpRequest capturedRequest = requestCaptor .getValue ();
389+ assertEquals (URI .create ("https://api.astra.datastax.com/v2/databases/test-db-id/secureBundleURL?all=true" ),
390+ capturedRequest .uri ());
391+ }
392+
393+ @ Test
394+ void testFetchSecureBundleUrlInfoError () throws Exception {
395+ // Mock the HTTP response for an error
396+ when (httpResponse .statusCode ()).thenReturn (401 );
397+ when (httpResponse .body ()).thenReturn ("{ \" error\" : \" Unauthorized\" }" );
398+
399+ // Mock the HTTP client to return our mocked response
400+ when (httpClient .send (any (), eq (HttpResponse .BodyHandlers .ofString ()))).thenReturn (httpResponse );
401+
402+ // Use reflection to access the private method
403+ Method fetchSecureBundleUrlInfoMethod = AstraDevOpsClient .class .getDeclaredMethod (
404+ "fetchSecureBundleUrlInfo" , String .class , String .class , boolean .class );
405+ fetchSecureBundleUrlInfoMethod .setAccessible (true );
406+
407+ // Test
408+ String jsonResponse = (String ) fetchSecureBundleUrlInfoMethod .invoke (client , "invalid-token" , "test-db-id" , false );
409+
410+ // Verify
411+ assertNull (jsonResponse );
412+ }
413+
414+ @ Test
415+ void testDownloadBundleFile () throws Exception {
416+ // Setup
417+ byte [] mockData = new byte [100 ]; // Mock some binary data
418+ ByteArrayInputStream inputStream = new ByteArrayInputStream (mockData );
419+
420+ // Mock the HTTP response
421+ when (httpResponseStream .statusCode ()).thenReturn (200 );
422+ when (httpResponseStream .body ()).thenReturn (inputStream );
423+
424+ // Mock the HTTP client to return our mocked response
425+ when (httpClient .send (any (), eq (HttpResponse .BodyHandlers .ofInputStream ()))).thenReturn (httpResponseStream );
426+
427+ // Use reflection to access the private method
428+ Method downloadBundleFileMethod = AstraDevOpsClient .class .getDeclaredMethod ("downloadBundleFile" , String .class ,
429+ PKFactory .Side .class );
430+ downloadBundleFileMethod .setAccessible (true );
431+
432+ // Test
433+ String filePath = (String ) downloadBundleFileMethod .invoke (client , "https://example.com/bundle.zip" ,
434+ PKFactory .Side .ORIGIN );
435+
436+ // Verify
437+ assertNotNull (filePath );
438+ assertTrue (filePath .contains ("origin-secure-bundle.zip" ));
439+
440+ // Verify the correct URL was used
441+ ArgumentCaptor <HttpRequest > requestCaptor = ArgumentCaptor .forClass (HttpRequest .class );
442+ verify (httpClient ).send (requestCaptor .capture (), eq (HttpResponse .BodyHandlers .ofInputStream ()));
443+
444+ HttpRequest capturedRequest = requestCaptor .getValue ();
445+ assertEquals (URI .create ("https://example.com/bundle.zip" ), capturedRequest .uri ());
446+ }
447+
448+ @ Test
449+ void testDownloadBundleFileHttpError () throws Exception {
450+ // Setup
451+
452+ // Mock the HTTP response with an error status
453+ when (httpResponseStream .statusCode ()).thenReturn (404 );
454+
455+ // Mock the HTTP client to return our mocked response
456+ when (httpClient .send (any (), eq (HttpResponse .BodyHandlers .ofInputStream ()))).thenReturn (httpResponseStream );
457+
458+ // Use reflection to access the private method
459+ Method downloadBundleFileMethod = AstraDevOpsClient .class .getDeclaredMethod (
460+ "downloadBundleFile" , String .class , PKFactory .Side .class );
461+ downloadBundleFileMethod .setAccessible (true );
462+
463+ try {
464+ downloadBundleFileMethod .invoke (client , "https://example.com/not-found.zip" , PKFactory .Side .ORIGIN );
465+ fail ("Expected an exception to be thrown" );
466+ } catch (java .lang .reflect .InvocationTargetException e ) {
467+ // Extract the actual exception that was wrapped
468+ assertTrue (e .getCause () instanceof IOException );
469+ assertEquals ("Failed to download secure bundle. Status code: 404" , e .getCause ().getMessage ());
470+ }
471+ }
472+
473+ @ Test
474+ void testDownloadSecureBundleSuccess () throws Exception {
475+ // Setup - mock all the components for a successful download
476+
477+ // Step 1: Mock the API response for fetching the SCB URL
478+ when (httpResponse .statusCode ()).thenReturn (200 );
479+ when (httpResponse .body ()).thenReturn ("{ \" downloadURL\" : \" https://example.com/bundle.zip\" }" );
480+
481+ // Step 2: Mock the binary download
482+ byte [] mockData = new byte [100 ]; // Mock some binary data
483+ ByteArrayInputStream inputStream = new ByteArrayInputStream (mockData );
484+
485+ // Mock the HTTP response
486+ when (httpResponseStream .statusCode ()).thenReturn (200 );
487+ when (httpResponseStream .body ()).thenReturn (inputStream );
488+
489+ // Configure the HTTP client to return responses based on different handler types
490+ // We need to use doReturn().when() syntax here to avoid NullPointerException in the matcher
491+ doReturn (httpResponse ).when (httpClient ).send (any (), eq (HttpResponse .BodyHandlers .ofString ()));
492+ doReturn (httpResponseStream ).when (httpClient ).send (any (), eq (HttpResponse .BodyHandlers .ofInputStream ()));
493+
494+ // Mock the property helper
495+ when (propertyHelper .getAsString (KnownProperties .ORIGIN_ASTRA_TOKEN )).thenReturn ("test-token" );
496+ when (propertyHelper .getAsString (KnownProperties .ORIGIN_ASTRA_DATABASE_ID )).thenReturn ("test-db-id" );
497+ when (propertyHelper .getAsString (KnownProperties .ORIGIN_ASTRA_SCB_TYPE )).thenReturn ("default" );
498+
499+ // Test
500+ String filePath = client .downloadSecureBundle (PKFactory .Side .ORIGIN );
501+
502+ // Verify
503+ assertNotNull (filePath );
504+ assertTrue (filePath .contains ("origin-secure-bundle.zip" ));
505+ }
506+ }
0 commit comments