44# --------------------------------------------------------------------------------------------
55
66import os
7+ import sys
78import unittest
89from unittest import mock
910
@@ -518,4 +519,167 @@ def test_cosmosdb_xrr_single_region_account(self, resource_group):
518519 assert restored_account ['restoreParameters' ]['restoreSource' ] == restorable_database_account ['id' ]
519520 assert restored_account ['restoreParameters' ]['restoreTimestampInUtc' ] == restore_ts_string
520521 assert restored_account ['restoreParameters' ]['sourceBackupLocation' ] == source_loc_for_xrr
521- assert restored_account ['writeLocations' ][0 ]['locationName' ] == 'North Central US'
522+ assert restored_account ['writeLocations' ][0 ]['locationName' ] == 'North Central US'
523+
524+
525+ class CosmosDBRestoreUnitTests (unittest .TestCase ):
526+ def setUp (self ):
527+ # Mock dependencies that might be missing or problematic to import
528+ if 'azure.mgmt.cosmosdb.models' not in sys .modules :
529+ sys .modules ['azure.mgmt.cosmosdb.models' ] = mock .MagicMock ()
530+ if 'azure.cli.core.util' not in sys .modules :
531+ sys .modules ['azure.cli.core.util' ] = mock .MagicMock ()
532+ if 'knack.log' not in sys .modules :
533+ sys .modules ['knack.log' ] = mock .MagicMock ()
534+ # Mocking knack.util.CLIError is crucial if it's used in custom.py
535+ if 'knack.util' not in sys .modules :
536+ mock_knack_util = mock .MagicMock ()
537+ mock_knack_util .CLIError = Exception
538+ sys .modules ['knack.util' ] = mock_knack_util
539+
540+ # Ensure Azure Core Exceptions are available
541+ try :
542+ import azure .core .exceptions
543+ except ImportError :
544+ mock_core_exceptions = mock .MagicMock ()
545+ # Define minimal exception class
546+ class HttpResponseError (Exception ):
547+ def __init__ (self , message = None , response = None , ** kwargs ):
548+ self .message = message
549+ self .response = response
550+ self .status_code = kwargs .get ('status_code' , None )
551+ def __str__ (self ):
552+ return self .message or ""
553+ mock_core_exceptions .HttpResponseError = HttpResponseError
554+ mock_core_exceptions .ResourceNotFoundError = Exception
555+ sys .modules ['azure.core.exceptions' ] = mock_core_exceptions
556+
557+ def test_restore_handles_forbidden_error (self ):
558+ from azure .core .exceptions import HttpResponseError
559+ # Lazy import to ensure mocks are applied first
560+ from azure .cli .command_modules .cosmosdb .custom import _create_database_account
561+
562+ # Setup mocks
563+ client = mock .MagicMock ()
564+
565+ # Simulate the LRO poller raising the specific error
566+ poller = mock .MagicMock ()
567+ error_json = '{"code":"Forbidden","message":"Database Account riks-models-003-acc-westeurope does not exist"}'
568+ exception = HttpResponseError (message = error_json )
569+ exception .status_code = 403
570+
571+ # side_effect raises the exception when called
572+ poller .result .side_effect = exception
573+ client .begin_create_or_update .return_value = poller
574+
575+ # Simulate client.get returning the account successfully
576+ mock_account = mock .MagicMock ()
577+ mock_account .provisioning_state = "Succeeded"
578+ client .get .return_value = mock_account
579+
580+ # Parameters
581+ resource_group_name = "rg"
582+ account_name = "myaccount"
583+
584+ # Call the private function directly to verify logic
585+ result = _create_database_account (
586+ client = client ,
587+ resource_group_name = resource_group_name ,
588+ account_name = account_name ,
589+ locations = [],
590+ is_restore_request = True ,
591+ arm_location = "westeurope" ,
592+ restore_source = "/subscriptions/sub/providers/Microsoft.DocumentDB/locations/westeurope/restorableDatabaseAccounts/source-id" ,
593+ restore_timestamp = "2026-01-01T00:00:00+00:00"
594+ )
595+
596+ # Assertions
597+ # 1. begin_create_or_update called
598+ client .begin_create_or_update .assert_called ()
599+ # 2. poller.result() called (and raised exception)
600+ poller .result .assert_called ()
601+ # 3. client.get called (recovery mechanism)
602+ client .get .assert_called_with (resource_group_name , account_name )
603+ # 4. Result is the account returned by get
604+ self .assertEqual (result , mock_account )
605+
606+ def test_restore_raises_other_errors (self ):
607+ from azure .core .exceptions import HttpResponseError
608+ from azure .cli .command_modules .cosmosdb .custom import _create_database_account
609+
610+ # Setup mocks
611+ client = mock .MagicMock ()
612+ poller = mock .MagicMock ()
613+
614+ # Different error
615+ exception = HttpResponseError (message = "Some other error" )
616+ exception .status_code = 500
617+ poller .result .side_effect = exception
618+ client .begin_create_or_update .return_value = poller
619+
620+ with self .assertRaises (HttpResponseError ):
621+ _create_database_account (
622+ client = client ,
623+ resource_group_name = "rg" ,
624+ account_name = "myaccount" ,
625+ is_restore_request = True ,
626+ arm_location = "westeurope" ,
627+ restore_source = "src" ,
628+ restore_timestamp = "ts"
629+ )
630+
631+ def test_normal_create_does_not_suppress_error (self ):
632+ from azure .core .exceptions import HttpResponseError
633+ from azure .cli .command_modules .cosmosdb .custom import _create_database_account
634+
635+ # Setup mocks
636+ client = mock .MagicMock ()
637+ poller = mock .MagicMock ()
638+
639+ # Same error but NOT a restore request
640+ error_json = '{"code":"Forbidden","message":"Database Account riks-models-003-acc-westeurope does not exist"}'
641+ exception = HttpResponseError (message = error_json )
642+ exception .status_code = 403
643+ poller .result .side_effect = exception
644+ client .begin_create_or_update .return_value = poller
645+
646+ with self .assertRaises (HttpResponseError ):
647+ _create_database_account (
648+ client = client ,
649+ resource_group_name = "rg" ,
650+ account_name = "myaccount" ,
651+ is_restore_request = False , # Normal create
652+ arm_location = "westeurope"
653+ )
654+
655+ def test_normal_create_success (self ):
656+ from azure .cli .command_modules .cosmosdb .custom import _create_database_account
657+
658+ # Setup mocks
659+ client = mock .MagicMock ()
660+ poller = mock .MagicMock ()
661+
662+ # Simulate successful creation
663+ mock_created_account = mock .MagicMock ()
664+ mock_created_account .provisioning_state = "Succeeded"
665+ poller .result .return_value = mock_created_account
666+ client .begin_create_or_update .return_value = poller
667+
668+ # Call the private function
669+ result = _create_database_account (
670+ client = client ,
671+ resource_group_name = "rg" ,
672+ account_name = "myaccount" ,
673+ is_restore_request = False ,
674+ arm_location = "westeurope"
675+ )
676+
677+ # Assertions
678+ # 1. begin_create_or_update called
679+ client .begin_create_or_update .assert_called ()
680+ # 2. poller.result() called
681+ poller .result .assert_called ()
682+ # 3. client.get should NOT be called since result() succeeded
683+ client .get .assert_not_called ()
684+ # 4. Result matches
685+ self .assertEqual (result , mock_created_account )
0 commit comments