|
1 | 1 | import os |
2 | 2 | import unittest |
| 3 | +from types import SimpleNamespace |
3 | 4 | from unittest.mock import patch |
4 | 5 |
|
5 | | -from msal.region import _detect_region, _validate_region |
| 6 | +from msal.region import ( |
| 7 | + _detect_region, _detect_region_of_azure_vm, _validate_region) |
| 8 | + |
| 9 | +from tests.http_client import MinimalResponse |
| 10 | + |
| 11 | + |
| 12 | +class _StubHttpClient(object): |
| 13 | + """Records the requested URL/headers and returns a preconfigured response. |
| 14 | +
|
| 15 | + If *response* is an exception instance, it is raised from ``get`` to |
| 16 | + simulate a network failure (e.g. not running in an Azure VM).""" |
| 17 | + |
| 18 | + def __init__(self, response): |
| 19 | + self._response = response |
| 20 | + self.url = None |
| 21 | + self.headers = None |
| 22 | + |
| 23 | + def get(self, url, params=None, headers=None, **kwargs): |
| 24 | + self.url = url |
| 25 | + self.headers = headers |
| 26 | + if isinstance(self._response, Exception): |
| 27 | + raise self._response |
| 28 | + return self._response |
| 29 | + |
6 | 30 |
|
7 | 31 |
|
8 | 32 | class TestValidateRegion(unittest.TestCase): |
@@ -55,5 +79,59 @@ def test_empty_env_returns_none(self): |
55 | 79 | self.assertIsNone(_detect_region()) |
56 | 80 |
|
57 | 81 |
|
| 82 | +class TestDetectRegionOfAzureVm(unittest.TestCase): |
| 83 | + |
| 84 | + def test_valid_location_is_returned(self): |
| 85 | + client = _StubHttpClient( |
| 86 | + MinimalResponse(status_code=200, text='{"location": "westus2"}')) |
| 87 | + self.assertEqual(_detect_region_of_azure_vm(client), "westus2") |
| 88 | + |
| 89 | + def test_request_uses_compute_json_endpoint(self): |
| 90 | + client = _StubHttpClient( |
| 91 | + MinimalResponse(status_code=200, text='{"location": "westus2"}')) |
| 92 | + _detect_region_of_azure_vm(client) |
| 93 | + self.assertEqual( |
| 94 | + client.url, |
| 95 | + "http://169.254.169.254/metadata/instance/compute" |
| 96 | + "?api-version=2021-02-01") |
| 97 | + self.assertNotIn("/location", client.url) |
| 98 | + self.assertNotIn("format=text", client.url) |
| 99 | + self.assertEqual(client.headers, {"Metadata": "true"}) |
| 100 | + |
| 101 | + def test_missing_location_returns_none(self): |
| 102 | + client = _StubHttpClient(MinimalResponse(status_code=200, text="{}")) |
| 103 | + self.assertIsNone(_detect_region_of_azure_vm(client)) |
| 104 | + |
| 105 | + def test_null_location_returns_none(self): |
| 106 | + client = _StubHttpClient( |
| 107 | + MinimalResponse(status_code=200, text='{"location": null}')) |
| 108 | + self.assertIsNone(_detect_region_of_azure_vm(client)) |
| 109 | + |
| 110 | + def test_malformed_json_returns_none(self): |
| 111 | + client = _StubHttpClient( |
| 112 | + MinimalResponse(status_code=200, text="not json")) |
| 113 | + self.assertIsNone(_detect_region_of_azure_vm(client)) |
| 114 | + |
| 115 | + def test_invalid_location_value_returns_none(self): |
| 116 | + client = _StubHttpClient( |
| 117 | + MinimalResponse(status_code=200, text='{"location": "evil.com/hijack"}')) |
| 118 | + self.assertIsNone(_detect_region_of_azure_vm(client)) |
| 119 | + |
| 120 | + def test_non_string_location_returns_none(self): |
| 121 | + client = _StubHttpClient( |
| 122 | + MinimalResponse(status_code=200, text='{"location": 123}')) |
| 123 | + self.assertIsNone(_detect_region_of_azure_vm(client)) |
| 124 | + |
| 125 | + def test_non_string_response_text_returns_none(self): |
| 126 | + # A custom http_client could yield a non-string resp.text; json.loads |
| 127 | + # would raise TypeError, which must be treated as a malformed response. |
| 128 | + client = _StubHttpClient(SimpleNamespace(status_code=200, text=None)) |
| 129 | + self.assertIsNone(_detect_region_of_azure_vm(client)) |
| 130 | + |
| 131 | + def test_network_failure_returns_none(self): |
| 132 | + client = _StubHttpClient(IOError("IMDS unreachable")) |
| 133 | + self.assertIsNone(_detect_region_of_azure_vm(client)) |
| 134 | + |
| 135 | + |
58 | 136 | if __name__ == "__main__": |
59 | 137 | unittest.main() |
0 commit comments