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