|
1 | 1 | import os |
| 2 | +from unittest.mock import MagicMock, patch |
2 | 3 |
|
3 | 4 | from azure.core.credentials import AzureNamedKeyCredential |
4 | 5 | from azure.identity import DefaultAzureCredential |
@@ -39,10 +40,143 @@ def test_azureblobpath_properties(path_class, monkeypatch): |
39 | 40 | @pytest.mark.parametrize("client_class", [AzureBlobClient, LocalAzureBlobClient]) |
40 | 41 | def test_azureblobpath_nocreds(client_class, monkeypatch): |
41 | 42 | monkeypatch.delenv("AZURE_STORAGE_CONNECTION_STRING", raising=False) |
| 43 | + monkeypatch.delenv("AZURE_STORAGE_ACCOUNT_URL", raising=False) |
| 44 | + monkeypatch.setattr( |
| 45 | + "cloudpathlib.azure.azblobclient.DefaultAzureCredential", None |
| 46 | + ) |
42 | 47 | with pytest.raises(MissingCredentialsError): |
43 | 48 | client_class() |
44 | 49 |
|
45 | 50 |
|
| 51 | +def test_default_credential_used_with_account_url(monkeypatch): |
| 52 | + """DefaultAzureCredential is used when account_url is provided without credential.""" |
| 53 | + monkeypatch.delenv("AZURE_STORAGE_CONNECTION_STRING", raising=False) |
| 54 | + monkeypatch.delenv("AZURE_STORAGE_ACCOUNT_URL", raising=False) |
| 55 | + |
| 56 | + mock_dac = MagicMock() |
| 57 | + mock_dac_class = MagicMock(return_value=mock_dac) |
| 58 | + monkeypatch.setattr( |
| 59 | + "cloudpathlib.azure.azblobclient.DefaultAzureCredential", mock_dac_class |
| 60 | + ) |
| 61 | + |
| 62 | + with patch.object(BlobServiceClient, "__init__", return_value=None) as mock_blob, patch.object( |
| 63 | + DataLakeServiceClient, "__init__", return_value=None |
| 64 | + ) as mock_datalake: |
| 65 | + AzureBlobClient(account_url="https://myaccount.blob.core.windows.net") |
| 66 | + |
| 67 | + mock_dac_class.assert_called_once() |
| 68 | + mock_blob.assert_called_once_with( |
| 69 | + account_url="https://myaccount.blob.core.windows.net", credential=mock_dac |
| 70 | + ) |
| 71 | + mock_datalake.assert_called_once_with( |
| 72 | + account_url="https://myaccount.dfs.core.windows.net", credential=mock_dac |
| 73 | + ) |
| 74 | + |
| 75 | + |
| 76 | +def test_no_default_credential_when_explicit_credential(monkeypatch): |
| 77 | + """DefaultAzureCredential is NOT used when an explicit credential is provided.""" |
| 78 | + monkeypatch.delenv("AZURE_STORAGE_CONNECTION_STRING", raising=False) |
| 79 | + monkeypatch.delenv("AZURE_STORAGE_ACCOUNT_URL", raising=False) |
| 80 | + |
| 81 | + mock_dac_class = MagicMock() |
| 82 | + monkeypatch.setattr( |
| 83 | + "cloudpathlib.azure.azblobclient.DefaultAzureCredential", mock_dac_class |
| 84 | + ) |
| 85 | + |
| 86 | + explicit_cred = MagicMock() |
| 87 | + with patch.object(BlobServiceClient, "__init__", return_value=None), patch.object( |
| 88 | + DataLakeServiceClient, "__init__", return_value=None |
| 89 | + ): |
| 90 | + AzureBlobClient( |
| 91 | + account_url="https://myaccount.blob.core.windows.net", |
| 92 | + credential=explicit_cred, |
| 93 | + ) |
| 94 | + |
| 95 | + mock_dac_class.assert_not_called() |
| 96 | + |
| 97 | + |
| 98 | +def test_fallback_when_azure_identity_not_installed(monkeypatch): |
| 99 | + """When azure-identity is not installed, credential=None is passed through.""" |
| 100 | + monkeypatch.delenv("AZURE_STORAGE_CONNECTION_STRING", raising=False) |
| 101 | + monkeypatch.delenv("AZURE_STORAGE_ACCOUNT_URL", raising=False) |
| 102 | + monkeypatch.setattr( |
| 103 | + "cloudpathlib.azure.azblobclient.DefaultAzureCredential", None |
| 104 | + ) |
| 105 | + |
| 106 | + with patch.object(BlobServiceClient, "__init__", return_value=None) as mock_blob, patch.object( |
| 107 | + DataLakeServiceClient, "__init__", return_value=None |
| 108 | + ): |
| 109 | + AzureBlobClient(account_url="https://myaccount.blob.core.windows.net") |
| 110 | + |
| 111 | + mock_blob.assert_called_once_with( |
| 112 | + account_url="https://myaccount.blob.core.windows.net", credential=None |
| 113 | + ) |
| 114 | + |
| 115 | + |
| 116 | +def test_account_url_env_var_blob(monkeypatch): |
| 117 | + """AZURE_STORAGE_ACCOUNT_URL env var with .blob. URL creates both clients.""" |
| 118 | + monkeypatch.delenv("AZURE_STORAGE_CONNECTION_STRING", raising=False) |
| 119 | + monkeypatch.setenv( |
| 120 | + "AZURE_STORAGE_ACCOUNT_URL", "https://myaccount.blob.core.windows.net" |
| 121 | + ) |
| 122 | + |
| 123 | + mock_dac = MagicMock() |
| 124 | + mock_dac_class = MagicMock(return_value=mock_dac) |
| 125 | + monkeypatch.setattr( |
| 126 | + "cloudpathlib.azure.azblobclient.DefaultAzureCredential", mock_dac_class |
| 127 | + ) |
| 128 | + |
| 129 | + with patch.object(BlobServiceClient, "__init__", return_value=None) as mock_blob, patch.object( |
| 130 | + DataLakeServiceClient, "__init__", return_value=None |
| 131 | + ) as mock_datalake: |
| 132 | + AzureBlobClient() |
| 133 | + |
| 134 | + mock_dac_class.assert_called_once() |
| 135 | + mock_blob.assert_called_once_with( |
| 136 | + account_url="https://myaccount.blob.core.windows.net", credential=mock_dac |
| 137 | + ) |
| 138 | + mock_datalake.assert_called_once_with( |
| 139 | + account_url="https://myaccount.dfs.core.windows.net", credential=mock_dac |
| 140 | + ) |
| 141 | + |
| 142 | + |
| 143 | +def test_account_url_env_var_dfs(monkeypatch): |
| 144 | + """AZURE_STORAGE_ACCOUNT_URL env var with .dfs. URL creates both clients.""" |
| 145 | + monkeypatch.delenv("AZURE_STORAGE_CONNECTION_STRING", raising=False) |
| 146 | + monkeypatch.setenv( |
| 147 | + "AZURE_STORAGE_ACCOUNT_URL", "https://myaccount.dfs.core.windows.net" |
| 148 | + ) |
| 149 | + |
| 150 | + mock_dac = MagicMock() |
| 151 | + mock_dac_class = MagicMock(return_value=mock_dac) |
| 152 | + monkeypatch.setattr( |
| 153 | + "cloudpathlib.azure.azblobclient.DefaultAzureCredential", mock_dac_class |
| 154 | + ) |
| 155 | + |
| 156 | + with patch.object(BlobServiceClient, "__init__", return_value=None) as mock_blob, patch.object( |
| 157 | + DataLakeServiceClient, "__init__", return_value=None |
| 158 | + ) as mock_datalake: |
| 159 | + AzureBlobClient() |
| 160 | + |
| 161 | + mock_blob.assert_called_once_with( |
| 162 | + account_url="https://myaccount.blob.core.windows.net", credential=mock_dac |
| 163 | + ) |
| 164 | + mock_datalake.assert_called_once_with( |
| 165 | + account_url="https://myaccount.dfs.core.windows.net", credential=mock_dac |
| 166 | + ) |
| 167 | + |
| 168 | + |
| 169 | +def test_missing_creds_error_no_env_vars(monkeypatch): |
| 170 | + """MissingCredentialsError is still raised when nothing is configured.""" |
| 171 | + monkeypatch.delenv("AZURE_STORAGE_CONNECTION_STRING", raising=False) |
| 172 | + monkeypatch.delenv("AZURE_STORAGE_ACCOUNT_URL", raising=False) |
| 173 | + monkeypatch.setattr( |
| 174 | + "cloudpathlib.azure.azblobclient.DefaultAzureCredential", None |
| 175 | + ) |
| 176 | + with pytest.raises(MissingCredentialsError): |
| 177 | + AzureBlobClient() |
| 178 | + |
| 179 | + |
46 | 180 | def test_as_url(azure_rigs): |
47 | 181 | p: AzureBlobPath = azure_rigs.create_cloud_path("dir_0/file0_0.txt") |
48 | 182 |
|
|
0 commit comments