|
2 | 2 | Unit tests for infrastructures.py. |
3 | 3 | """ |
4 | 4 |
|
| 5 | +import json |
| 6 | +import os |
5 | 7 | from unittest.mock import Mock, patch, MagicMock |
6 | 8 | import pytest |
7 | 9 |
|
@@ -2432,32 +2434,96 @@ def test_appgw_apim_pe_approve_private_links(mock_utils, mock_az): |
2432 | 2434 | assert isinstance(result, bool) |
2433 | 2435 |
|
2434 | 2436 |
|
2435 | | -def test_afd_apim_aca_disable_public_access(mock_utils, mock_az): |
2436 | | - """Test disabling public access for AfdApimAcaInfrastructure.""" |
2437 | | - infra = infrastructures.AfdApimAcaInfrastructure( |
2438 | | - rg_location='eastus', |
2439 | | - index=1 |
| 2437 | +@pytest.mark.unit |
| 2438 | +def test_disable_apim_public_access_success_writes_params_and_calls_az(mock_utils, mock_az, tmp_path, monkeypatch): |
| 2439 | + """Disable public access should update params.json and trigger a redeploy.""" |
| 2440 | + infra = infrastructures.AfdApimAcaInfrastructure(rg_location='eastus', index=1) |
| 2441 | + |
| 2442 | + infra._define_policy_fragments() |
| 2443 | + infra._define_apis() |
| 2444 | + infra._define_bicep_parameters() |
| 2445 | + assert infra.bicep_parameters['apimPublicAccess']['value'] is True |
| 2446 | + |
| 2447 | + # Redirect module-relative path resolution to a temp project layout. |
| 2448 | + monkeypatch.setattr( |
| 2449 | + infrastructures, |
| 2450 | + '__file__', |
| 2451 | + str(tmp_path / 'shared' / 'python' / 'infrastructures.py'), |
| 2452 | + raising=False |
2440 | 2453 | ) |
| 2454 | + infra_dir = tmp_path / 'infrastructure' / infra.infra.value |
| 2455 | + infra_dir.mkdir(parents=True, exist_ok=True) |
2441 | 2456 |
|
2442 | 2457 | mock_az.run.return_value = Mock(success=True) |
2443 | 2458 |
|
| 2459 | + original_cwd = os.getcwd() |
2444 | 2460 | result = infra._disable_apim_public_access() |
2445 | 2461 |
|
2446 | | - assert isinstance(result, bool) |
| 2462 | + assert result is True |
| 2463 | + assert os.getcwd() == original_cwd |
| 2464 | + assert infra.bicep_parameters['apimPublicAccess']['value'] is False |
2447 | 2465 |
|
| 2466 | + params_path = infra_dir / 'params.json' |
| 2467 | + assert params_path.exists() |
2448 | 2468 |
|
2449 | | -def test_appgw_apim_pe_disable_public_access(mock_utils, mock_az): |
2450 | | - """Test disabling public access for AppGwApimPeInfrastructure.""" |
2451 | | - infra = infrastructures.AppGwApimPeInfrastructure( |
2452 | | - rg_location='eastus', |
2453 | | - index=1 |
| 2469 | + params_json = json.loads(params_path.read_text(encoding='utf-8')) |
| 2470 | + assert params_json['parameters']['apimPublicAccess']['value'] is False |
| 2471 | + |
| 2472 | + mock_az.run.assert_called_once() |
| 2473 | + cmd = mock_az.run.call_args.args[0] |
| 2474 | + assert 'az deployment group create' in cmd |
| 2475 | + assert f'--name {infra.infra.value}-lockdown' in cmd |
| 2476 | + assert f'--resource-group {infra.rg_name}' in cmd |
| 2477 | + assert '--template-file' in cmd |
| 2478 | + assert '--parameters' in cmd |
| 2479 | + |
| 2480 | + |
| 2481 | +@pytest.mark.unit |
| 2482 | +def test_disable_apim_public_access_returns_false_when_param_missing(mock_utils, mock_az): |
| 2483 | + """If apimPublicAccess isn't present in parameters, the method should fail safely.""" |
| 2484 | + infra = infrastructures.Infrastructure( |
| 2485 | + infra=INFRASTRUCTURE.SIMPLE_APIM, |
| 2486 | + index=1, |
| 2487 | + rg_location='eastus' |
2454 | 2488 | ) |
2455 | 2489 |
|
2456 | | - mock_az.run.return_value = Mock(success=True) |
| 2490 | + infra._define_policy_fragments() |
| 2491 | + infra._define_apis() |
| 2492 | + infra._define_bicep_parameters() |
| 2493 | + assert 'apimPublicAccess' not in infra.bicep_parameters |
2457 | 2494 |
|
2458 | 2495 | result = infra._disable_apim_public_access() |
2459 | 2496 |
|
2460 | | - assert isinstance(result, bool) |
| 2497 | + assert result is False |
| 2498 | + mock_az.run.assert_not_called() |
| 2499 | + |
| 2500 | + |
| 2501 | +@pytest.mark.unit |
| 2502 | +def test_disable_apim_public_access_returns_false_when_deploy_fails(mock_utils, mock_az, tmp_path, monkeypatch): |
| 2503 | + """If the redeploy fails (az.run.success=False), return False but still write params.json.""" |
| 2504 | + infra = infrastructures.AppGwApimPeInfrastructure(rg_location='eastus', index=1) |
| 2505 | + |
| 2506 | + infra._define_policy_fragments() |
| 2507 | + infra._define_apis() |
| 2508 | + infra._define_bicep_parameters() |
| 2509 | + assert infra.bicep_parameters['apimPublicAccess']['value'] is True |
| 2510 | + |
| 2511 | + monkeypatch.setattr( |
| 2512 | + infrastructures, |
| 2513 | + '__file__', |
| 2514 | + str(tmp_path / 'shared' / 'python' / 'infrastructures.py'), |
| 2515 | + raising=False |
| 2516 | + ) |
| 2517 | + infra_dir = tmp_path / 'infrastructure' / infra.infra.value |
| 2518 | + infra_dir.mkdir(parents=True, exist_ok=True) |
| 2519 | + |
| 2520 | + mock_az.run.return_value = Mock(success=False) |
| 2521 | + |
| 2522 | + result = infra._disable_apim_public_access() |
| 2523 | + |
| 2524 | + assert result is False |
| 2525 | + assert (infra_dir / 'params.json').exists() |
| 2526 | + assert infra.bicep_parameters['apimPublicAccess']['value'] is False |
2461 | 2527 |
|
2462 | 2528 |
|
2463 | 2529 | def test_afd_apim_aca_verify_connectivity(mock_utils, mock_az): |
@@ -2712,32 +2778,156 @@ def test_appgw_apim_pe_approve_private_links_multiple(mock_utils, mock_az): |
2712 | 2778 | assert isinstance(result, bool) |
2713 | 2779 |
|
2714 | 2780 |
|
2715 | | -def test_afd_apim_aca_disable_public_access_success(mock_utils, mock_az): |
2716 | | - """Test disabling public access for AfdApimAcaInfrastructure.""" |
2717 | | - infra = infrastructures.AfdApimAcaInfrastructure( |
2718 | | - rg_location='eastus', |
2719 | | - index=1 |
2720 | | - ) |
| 2781 | +@pytest.mark.unit |
| 2782 | +def test_afd_apim_aca_deploy_infrastructure_success_calls_steps(mock_utils, mock_az): |
| 2783 | + """AFD deploy should call approve/connectivity/disable steps when base deploy succeeds.""" |
| 2784 | + mock_utils.Output.side_effect = Output |
2721 | 2785 |
|
2722 | | - mock_az.run.return_value = Mock(success=True) |
| 2786 | + infra = infrastructures.AfdApimAcaInfrastructure(rg_location='eastus', index=1) |
2723 | 2787 |
|
2724 | | - result = infra._disable_apim_public_access() |
| 2788 | + base_output = Mock() |
| 2789 | + base_output.success = True |
| 2790 | + base_output.json_data = {'any': 'value'} |
| 2791 | + base_output.get.side_effect = lambda key, *_args, **_kwargs: { |
| 2792 | + 'apimServiceId': '/subscriptions/test/resourceGroups/test/providers/Microsoft.ApiManagement/service/test', |
| 2793 | + 'apimResourceGatewayURL': 'https://test-apim.azure-api.net' |
| 2794 | + }.get(key) |
2725 | 2795 |
|
2726 | | - assert isinstance(result, bool) |
| 2796 | + infra._approve_private_link_connections = Mock(return_value=True) |
| 2797 | + infra._verify_apim_connectivity = Mock(return_value=True) |
| 2798 | + infra._disable_apim_public_access = Mock(return_value=True) |
2727 | 2799 |
|
| 2800 | + with patch.object(infrastructures.Infrastructure, 'deploy_infrastructure', return_value=base_output) as mock_base_deploy: |
| 2801 | + result = infra.deploy_infrastructure(is_update=False) |
2728 | 2802 |
|
2729 | | -def test_appgw_apim_pe_disable_public_access_success(mock_utils, mock_az): |
2730 | | - """Test disabling public access for AppGwApimPeInfrastructure.""" |
2731 | | - infra = infrastructures.AppGwApimPeInfrastructure( |
2732 | | - rg_location='eastus', |
2733 | | - index=1 |
2734 | | - ) |
| 2803 | + assert result is base_output |
| 2804 | + mock_base_deploy.assert_called_once() |
| 2805 | + infra._approve_private_link_connections.assert_called_once_with('/subscriptions/test/resourceGroups/test/providers/Microsoft.ApiManagement/service/test') |
| 2806 | + infra._verify_apim_connectivity.assert_called_once_with('https://test-apim.azure-api.net') |
| 2807 | + infra._disable_apim_public_access.assert_called_once() |
2735 | 2808 |
|
2736 | | - mock_az.run.return_value = Mock(success=True) |
2737 | 2809 |
|
2738 | | - result = infra._disable_apim_public_access() |
| 2810 | +@pytest.mark.unit |
| 2811 | +def test_afd_apim_aca_deploy_infrastructure_returns_failed_output_when_approve_fails(mock_utils, mock_az): |
| 2812 | + """AFD deploy should return a failed Output when private link approval fails.""" |
| 2813 | + mock_utils.Output.side_effect = Output |
2739 | 2814 |
|
2740 | | - assert isinstance(result, bool) |
| 2815 | + infra = infrastructures.AfdApimAcaInfrastructure(rg_location='eastus', index=1) |
| 2816 | + |
| 2817 | + base_output = Mock() |
| 2818 | + base_output.success = True |
| 2819 | + base_output.json_data = {'any': 'value'} |
| 2820 | + base_output.get.side_effect = lambda key, *_args, **_kwargs: { |
| 2821 | + 'apimServiceId': '/subscriptions/test/resourceGroups/test/providers/Microsoft.ApiManagement/service/test', |
| 2822 | + 'apimResourceGatewayURL': 'https://test-apim.azure-api.net' |
| 2823 | + }.get(key) |
| 2824 | + |
| 2825 | + infra._approve_private_link_connections = Mock(return_value=False) |
| 2826 | + |
| 2827 | + with patch.object(infrastructures.Infrastructure, 'deploy_infrastructure', return_value=base_output): |
| 2828 | + result = infra.deploy_infrastructure(is_update=False) |
| 2829 | + |
| 2830 | + assert result.success is False |
| 2831 | + assert result.text == 'Private link approval failed' |
| 2832 | + |
| 2833 | + |
| 2834 | +@pytest.mark.unit |
| 2835 | +def test_appgw_apim_pe_deploy_infrastructure_success_calls_steps_and_sets_appgw_fields(mock_utils, mock_az): |
| 2836 | + """APPGW PE deploy should create prereqs, deploy, approve, verify connectivity, disable public access.""" |
| 2837 | + mock_utils.Output.side_effect = Output |
| 2838 | + |
| 2839 | + infra = infrastructures.AppGwApimPeInfrastructure(rg_location='eastus', index=1) |
| 2840 | + |
| 2841 | + base_output = Mock() |
| 2842 | + base_output.success = True |
| 2843 | + base_output.json_data = {'any': 'value'} |
| 2844 | + base_output.get.side_effect = lambda key, *_args, **_kwargs: { |
| 2845 | + 'apimServiceId': '/subscriptions/test/resourceGroups/test/providers/Microsoft.ApiManagement/service/test', |
| 2846 | + 'apimResourceGatewayURL': 'https://test-apim.azure-api.net', |
| 2847 | + 'appGatewayDomainName': 'api.example.com', |
| 2848 | + 'appgwPublicIpAddress': '1.2.3.4' |
| 2849 | + }.get(key) |
| 2850 | + |
| 2851 | + infra._create_keyvault = Mock(return_value=True) |
| 2852 | + infra._create_keyvault_certificate = Mock(return_value=True) |
| 2853 | + infra._approve_private_link_connections = Mock(return_value=True) |
| 2854 | + infra._verify_apim_connectivity = Mock(return_value=True) |
| 2855 | + infra._disable_apim_public_access = Mock(return_value=True) |
| 2856 | + |
| 2857 | + with patch.object(infrastructures.Infrastructure, 'deploy_infrastructure', return_value=base_output) as mock_base_deploy: |
| 2858 | + result = infra.deploy_infrastructure(is_update=False) |
| 2859 | + |
| 2860 | + assert result is base_output |
| 2861 | + infra._create_keyvault.assert_called_once() |
| 2862 | + infra._create_keyvault_certificate.assert_called_once() |
| 2863 | + mock_base_deploy.assert_called_once() |
| 2864 | + infra._approve_private_link_connections.assert_called_once_with('/subscriptions/test/resourceGroups/test/providers/Microsoft.ApiManagement/service/test') |
| 2865 | + infra._verify_apim_connectivity.assert_called_once_with('https://test-apim.azure-api.net') |
| 2866 | + infra._disable_apim_public_access.assert_called_once() |
| 2867 | + assert infra.appgw_domain_name == 'api.example.com' |
| 2868 | + assert infra.appgw_public_ip == '1.2.3.4' |
| 2869 | + |
| 2870 | + |
| 2871 | +@pytest.mark.unit |
| 2872 | +def test_appgw_apim_pe_deploy_infrastructure_returns_failed_output_when_keyvault_fails(mock_utils, mock_az): |
| 2873 | + """APPGW PE deploy should return a failed Output if Key Vault creation fails.""" |
| 2874 | + mock_utils.Output.side_effect = Output |
| 2875 | + |
| 2876 | + infra = infrastructures.AppGwApimPeInfrastructure(rg_location='eastus', index=1) |
| 2877 | + infra._create_keyvault = Mock(return_value=False) |
| 2878 | + |
| 2879 | + result = infra.deploy_infrastructure(is_update=False) |
| 2880 | + |
| 2881 | + assert result.success is False |
| 2882 | + assert result.text == 'Failed to create Key Vault' |
| 2883 | + |
| 2884 | + |
| 2885 | +@pytest.mark.unit |
| 2886 | +def test_appgw_apim_pe_verify_infrastructure_specific_success(mock_utils, mock_az): |
| 2887 | + """Verify should pass when App Gateway exists; container apps and PE checks are optional.""" |
| 2888 | + infra = infrastructures.AppGwApimPeInfrastructure(rg_location='eastus', index=1) |
| 2889 | + |
| 2890 | + appgw_output = Mock(success=True, json_data={'name': 'test-appgw'}) |
| 2891 | + aca_output = Mock(success=True, text='1') |
| 2892 | + apim_output = Mock(success=True, text='/subscriptions/test/.../apim') |
| 2893 | + pe_output = Mock(success=True, text='2') |
| 2894 | + |
| 2895 | + mock_az.run.side_effect = [appgw_output, aca_output, apim_output, pe_output] |
| 2896 | + |
| 2897 | + assert infra._verify_infrastructure_specific('rg-test') is True |
| 2898 | + |
| 2899 | + |
| 2900 | +@pytest.mark.unit |
| 2901 | +def test_appgw_apim_pe_verify_infrastructure_specific_returns_false_when_appgw_missing(mock_utils, mock_az): |
| 2902 | + """Verify should fail when App Gateway cannot be retrieved.""" |
| 2903 | + infra = infrastructures.AppGwApimPeInfrastructure(rg_location='eastus', index=1) |
| 2904 | + |
| 2905 | + mock_az.run.return_value = Mock(success=False, json_data=None) |
| 2906 | + |
| 2907 | + assert infra._verify_infrastructure_specific('rg-test') is False |
| 2908 | + |
| 2909 | + |
| 2910 | +@pytest.mark.unit |
| 2911 | +def test_appgw_apim_pe_verify_infrastructure_specific_ignores_private_endpoint_errors(mock_utils, mock_az): |
| 2912 | + """Private endpoint verification is best-effort and should not fail the overall verification.""" |
| 2913 | + infra = infrastructures.AppGwApimPeInfrastructure(rg_location='eastus', index=1) |
| 2914 | + |
| 2915 | + appgw_output = Mock(success=True, json_data={'name': 'test-appgw'}) |
| 2916 | + aca_output = Mock(success=True, text='0') |
| 2917 | + |
| 2918 | + def run_side_effect(*args, **kwargs): |
| 2919 | + cmd = args[0] if args else '' |
| 2920 | + if 'application-gateway list' in cmd: |
| 2921 | + return appgw_output |
| 2922 | + if 'containerapp list' in cmd: |
| 2923 | + return aca_output |
| 2924 | + if 'az apim list' in cmd: |
| 2925 | + raise RuntimeError('boom') |
| 2926 | + raise AssertionError(f'Unexpected az.run call: {cmd}') |
| 2927 | + |
| 2928 | + mock_az.run.side_effect = run_side_effect |
| 2929 | + |
| 2930 | + assert infra._verify_infrastructure_specific('rg-test') is True |
2741 | 2931 |
|
2742 | 2932 |
|
2743 | 2933 | def test_afd_apim_aca_verify_connectivity_with_retry(mock_utils, mock_az): |
@@ -2959,35 +3149,3 @@ def test_infrastructure_network_mode_with_custom_components(mock_utils): |
2959 | 3149 | infra._define_apis() |
2960 | 3150 | assert len(infra.pfs) == 7 |
2961 | 3151 | assert len(infra.apis) == 2 |
2962 | | - |
2963 | | - |
2964 | | -def test_infrastructure_account_info_retrieval(mock_utils, mock_az): |
2965 | | - """Test that account info is properly retrieved and stored.""" |
2966 | | - mock_az.get_account_info.return_value = ( |
2967 | | - |
2968 | | - 'user-id-12345', |
2969 | | - 'tenant-id-67890', |
2970 | | - 'subscription-id-abcde' |
2971 | | - ) |
2972 | | - |
2973 | | - infra = infrastructures.Infrastructure( |
2974 | | - infra=INFRASTRUCTURE.SIMPLE_APIM, |
2975 | | - index=1, |
2976 | | - rg_location='eastus' |
2977 | | - ) |
2978 | | - |
2979 | | - assert infra. current_user == '[email protected]' |
2980 | | - assert infra.current_user_id == 'user-id-12345' |
2981 | | - assert infra.tenant_id == 'tenant-id-67890' |
2982 | | - assert infra.subscription_id == 'subscription-id-abcde' |
2983 | | - |
2984 | | - |
2985 | | -def test_infrastructure_with_different_indices(mock_utils): |
2986 | | - """Test infrastructure with different index values.""" |
2987 | | - for index in [1, 2, 5, 10, 100]: |
2988 | | - infra = infrastructures.Infrastructure( |
2989 | | - infra=INFRASTRUCTURE.SIMPLE_APIM, |
2990 | | - index=index, |
2991 | | - rg_location='eastus' |
2992 | | - ) |
2993 | | - assert infra.index == index |
|
0 commit comments