Skip to content

Commit c372059

Browse files
Improve utils coverage
1 parent 531bc1e commit c372059

File tree

1 file changed

+360
-0
lines changed

1 file changed

+360
-0
lines changed

tests/python/test_utils.py

Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,3 +1161,363 @@ def fake_run(cmd, *args, **kwargs):
11611161
assert len(result) == 2
11621162
assert (INFRASTRUCTURE.SIMPLE_APIM, 1) in result
11631163
assert (INFRASTRUCTURE.SIMPLE_APIM, 2) in result
1164+
1165+
1166+
# ------------------------------
1167+
# NotebookHelper._get_current_index TESTS
1168+
# ------------------------------
1169+
1170+
def test_notebookhelper_get_current_index_with_index(monkeypatch):
1171+
"""Test _get_current_index when resource group has an index."""
1172+
nb_helper = utils.NotebookHelper(
1173+
'test-sample', 'apim-infra-simple-apim-5', 'eastus',
1174+
INFRASTRUCTURE.SIMPLE_APIM, [INFRASTRUCTURE.SIMPLE_APIM]
1175+
)
1176+
1177+
result = nb_helper._get_current_index()
1178+
assert result == 5
1179+
1180+
1181+
def test_notebookhelper_get_current_index_without_index(monkeypatch):
1182+
"""Test _get_current_index when resource group has no index."""
1183+
nb_helper = utils.NotebookHelper(
1184+
'test-sample', 'apim-infra-simple-apim', 'eastus',
1185+
INFRASTRUCTURE.SIMPLE_APIM, [INFRASTRUCTURE.SIMPLE_APIM]
1186+
)
1187+
1188+
result = nb_helper._get_current_index()
1189+
assert result is None
1190+
1191+
1192+
def test_notebookhelper_get_current_index_invalid_format(monkeypatch):
1193+
"""Test _get_current_index with invalid resource group format."""
1194+
nb_helper = utils.NotebookHelper(
1195+
'test-sample', 'custom-rg-name', 'eastus',
1196+
INFRASTRUCTURE.SIMPLE_APIM, [INFRASTRUCTURE.SIMPLE_APIM]
1197+
)
1198+
1199+
result = nb_helper._get_current_index()
1200+
assert result is None
1201+
1202+
1203+
def test_notebookhelper_get_current_index_non_numeric_suffix(monkeypatch):
1204+
"""Test _get_current_index with non-numeric suffix."""
1205+
nb_helper = utils.NotebookHelper(
1206+
'test-sample', 'apim-infra-simple-apim-abc', 'eastus',
1207+
INFRASTRUCTURE.SIMPLE_APIM, [INFRASTRUCTURE.SIMPLE_APIM]
1208+
)
1209+
1210+
result = nb_helper._get_current_index()
1211+
assert result is None
1212+
1213+
1214+
# ------------------------------
1215+
# NotebookHelper._clean_up_jwt TESTS
1216+
# ------------------------------
1217+
1218+
def test_notebookhelper_clean_up_jwt_success(monkeypatch):
1219+
"""Test _clean_up_jwt with successful cleanup."""
1220+
monkeypatch.setattr(az, 'cleanup_old_jwt_signing_keys', lambda *args: True)
1221+
monkeypatch.setattr('console.print_warning', lambda *args, **kwargs: None)
1222+
monkeypatch.setattr(utils, 'generate_signing_key', lambda: ('test-key', 'test-key-b64'))
1223+
monkeypatch.setattr(utils, 'print_val', lambda *args, **kwargs: None)
1224+
monkeypatch.setattr('time.time', lambda: 1234567890)
1225+
1226+
nb_helper = utils.NotebookHelper(
1227+
'test-sample', 'test-rg', 'eastus',
1228+
INFRASTRUCTURE.SIMPLE_APIM, [INFRASTRUCTURE.SIMPLE_APIM], use_jwt=True
1229+
)
1230+
1231+
# Should not raise or print warning
1232+
nb_helper._clean_up_jwt('test-apim')
1233+
1234+
1235+
def test_notebookhelper_clean_up_jwt_failure(monkeypatch, caplog):
1236+
"""Test _clean_up_jwt with failed cleanup."""
1237+
monkeypatch.setattr(az, 'cleanup_old_jwt_signing_keys', lambda *args: False)
1238+
monkeypatch.setattr(utils, 'generate_signing_key', lambda: ('test-key', 'test-key-b64'))
1239+
monkeypatch.setattr(utils, 'print_val', lambda *args, **kwargs: None)
1240+
monkeypatch.setattr('time.time', lambda: 1234567890)
1241+
1242+
nb_helper = utils.NotebookHelper(
1243+
'test-sample', 'test-rg', 'eastus',
1244+
INFRASTRUCTURE.SIMPLE_APIM, [INFRASTRUCTURE.SIMPLE_APIM], use_jwt=True
1245+
)
1246+
1247+
with caplog.at_level(logging.WARNING):
1248+
nb_helper._clean_up_jwt('test-apim')
1249+
1250+
# Should log warning about cleanup failure
1251+
assert any('JWT key cleanup failed' in record.message for record in caplog.records)
1252+
1253+
1254+
# ------------------------------
1255+
# get_endpoints TESTS
1256+
# ------------------------------
1257+
1258+
def test_get_endpoints_comprehensive(monkeypatch):
1259+
"""Test get_endpoints function."""
1260+
monkeypatch.setattr(az, 'get_frontdoor_url', lambda x, y: 'https://test-afd.azurefd.net')
1261+
monkeypatch.setattr(az, 'get_apim_url', lambda x: 'https://test-apim.azure-api.net')
1262+
monkeypatch.setattr(az, 'get_appgw_endpoint', lambda x: ('appgw.contoso.com', '1.2.3.4'))
1263+
monkeypatch.setattr('console.print_message', lambda x, **kw: None)
1264+
1265+
endpoints = utils.get_endpoints(INFRASTRUCTURE.AFD_APIM_PE, 'test-rg')
1266+
1267+
assert endpoints.afd_endpoint_url == 'https://test-afd.azurefd.net'
1268+
assert endpoints.apim_endpoint_url == 'https://test-apim.azure-api.net'
1269+
assert endpoints.appgw_hostname == 'appgw.contoso.com'
1270+
assert endpoints.appgw_public_ip == '1.2.3.4'
1271+
1272+
1273+
def test_get_endpoints_no_frontdoor(monkeypatch):
1274+
"""Test get_endpoints when Front Door is not available."""
1275+
monkeypatch.setattr(az, 'get_frontdoor_url', lambda x, y: None)
1276+
monkeypatch.setattr(az, 'get_apim_url', lambda x: 'https://test-apim.azure-api.net')
1277+
monkeypatch.setattr(az, 'get_appgw_endpoint', lambda x: (None, None))
1278+
monkeypatch.setattr('console.print_message', lambda x, **kw: None)
1279+
1280+
endpoints = utils.get_endpoints(INFRASTRUCTURE.SIMPLE_APIM, 'test-rg')
1281+
1282+
assert endpoints.afd_endpoint_url is None
1283+
assert endpoints.apim_endpoint_url == 'https://test-apim.azure-api.net'
1284+
1285+
1286+
# ------------------------------
1287+
# get_json TESTS
1288+
# ------------------------------
1289+
1290+
def test_get_json_valid_json_string():
1291+
"""Test get_json with valid JSON string."""
1292+
json_str = '{"key": "value", "number": 42}'
1293+
result = utils.get_json(json_str)
1294+
assert result == {'key': 'value', 'number': 42}
1295+
1296+
1297+
def test_get_json_python_dict_string():
1298+
"""Test get_json with Python dict string (single quotes)."""
1299+
dict_str = "{'key': 'value', 'number': 42}"
1300+
result = utils.get_json(dict_str)
1301+
assert result == {'key': 'value', 'number': 42}
1302+
1303+
1304+
def test_get_json_invalid_string(monkeypatch):
1305+
"""Test get_json with invalid string."""
1306+
monkeypatch.setattr('console.print_error', lambda *args, **kwargs: None)
1307+
1308+
invalid_str = "not valid json or python literal"
1309+
result = utils.get_json(invalid_str)
1310+
# Should return the original string when parsing fails
1311+
assert result == invalid_str
1312+
1313+
1314+
def test_get_json_non_string():
1315+
"""Test get_json with non-string input."""
1316+
result = utils.get_json({'already': 'a dict'})
1317+
assert result == {'already': 'a dict'}
1318+
1319+
result = utils.get_json([1, 2, 3])
1320+
assert result == [1, 2, 3]
1321+
1322+
1323+
# ------------------------------
1324+
# does_infrastructure_exist TESTS
1325+
# ------------------------------
1326+
1327+
def test_does_infrastructure_exist_not_exist(monkeypatch):
1328+
"""Test does_infrastructure_exist when infrastructure doesn't exist."""
1329+
monkeypatch.setattr(az, 'does_resource_group_exist', lambda x: False)
1330+
monkeypatch.setattr(az, 'get_infra_rg_name', lambda x, y: 'test-rg')
1331+
monkeypatch.setattr('console.print_plain', lambda *args, **kwargs: None)
1332+
1333+
result = utils.does_infrastructure_exist(INFRASTRUCTURE.SIMPLE_APIM, 1)
1334+
assert result is False
1335+
1336+
1337+
def test_does_infrastructure_exist_with_update_option_proceed(monkeypatch):
1338+
"""Test does_infrastructure_exist with update option - user proceeds."""
1339+
monkeypatch.setattr(az, 'does_resource_group_exist', lambda x: True)
1340+
monkeypatch.setattr(az, 'get_infra_rg_name', lambda x, y: 'test-rg')
1341+
monkeypatch.setattr('console.print_ok', lambda *args, **kwargs: None)
1342+
monkeypatch.setattr('console.print_plain', lambda *args, **kwargs: None)
1343+
monkeypatch.setattr('console.print_info', lambda *args, **kwargs: None)
1344+
monkeypatch.setattr('builtins.input', lambda prompt: '1')
1345+
1346+
result = utils.does_infrastructure_exist(INFRASTRUCTURE.SIMPLE_APIM, 1, allow_update_option=True)
1347+
assert result is False # Allow deployment to proceed
1348+
1349+
1350+
def test_does_infrastructure_exist_with_update_option_cancel(monkeypatch):
1351+
"""Test does_infrastructure_exist with update option - user cancels."""
1352+
monkeypatch.setattr(az, 'does_resource_group_exist', lambda x: True)
1353+
monkeypatch.setattr(az, 'get_infra_rg_name', lambda x, y: 'test-rg')
1354+
monkeypatch.setattr('console.print_ok', lambda *args, **kwargs: None)
1355+
monkeypatch.setattr('console.print_plain', lambda *args, **kwargs: None)
1356+
monkeypatch.setattr('console.print_info', lambda *args, **kwargs: None)
1357+
monkeypatch.setattr('builtins.input', lambda prompt: '2')
1358+
1359+
result = utils.does_infrastructure_exist(INFRASTRUCTURE.SIMPLE_APIM, 1, allow_update_option=True)
1360+
assert result is True # Block deployment
1361+
1362+
1363+
def test_does_infrastructure_exist_without_update_option(monkeypatch):
1364+
"""Test does_infrastructure_exist without update option."""
1365+
monkeypatch.setattr(az, 'does_resource_group_exist', lambda x: True)
1366+
monkeypatch.setattr(az, 'get_infra_rg_name', lambda x, y: 'test-rg')
1367+
monkeypatch.setattr('console.print_ok', lambda *args, **kwargs: None)
1368+
monkeypatch.setattr('console.print_plain', lambda *args, **kwargs: None)
1369+
1370+
result = utils.does_infrastructure_exist(INFRASTRUCTURE.SIMPLE_APIM, 1, allow_update_option=False)
1371+
assert result is True # Infrastructure exists, block deployment
1372+
1373+
1374+
# ------------------------------
1375+
# read_and_modify_policy_xml TESTS
1376+
# ------------------------------
1377+
1378+
def test_read_and_modify_policy_xml_with_replacements(monkeypatch):
1379+
"""Test read_and_modify_policy_xml with placeholders."""
1380+
xml_content = '<policy><key>{jwt_key}</key><value>{api_value}</value></policy>'
1381+
m = mock_open(read_data=xml_content)
1382+
1383+
real_open = builtins.open
1384+
1385+
def open_selector(file, *args, **kwargs):
1386+
mode = kwargs.get('mode', args[0] if args else 'r')
1387+
file_str = str(file)
1388+
if 'test-policy.xml' in file_str and 'b' not in mode:
1389+
return m(file, *args, **kwargs)
1390+
return real_open(file, *args, **kwargs)
1391+
1392+
monkeypatch.setattr(builtins, 'open', open_selector)
1393+
1394+
replacements = {
1395+
'jwt_key': 'JwtSigningKey123',
1396+
'api_value': 'test-api'
1397+
}
1398+
1399+
result = utils.read_and_modify_policy_xml('/path/to/test-policy.xml', replacements)
1400+
expected = '<policy><key>JwtSigningKey123</key><value>test-api</value></policy>'
1401+
assert result == expected
1402+
1403+
1404+
def test_read_and_modify_policy_xml_placeholder_not_found(monkeypatch, caplog):
1405+
"""Test read_and_modify_policy_xml when placeholder doesn't exist in XML."""
1406+
xml_content = '<policy><key>static</key></policy>'
1407+
m = mock_open(read_data=xml_content)
1408+
1409+
real_open = builtins.open
1410+
1411+
def open_selector(file, *args, **kwargs):
1412+
mode = kwargs.get('mode', args[0] if args else 'r')
1413+
file_str = str(file)
1414+
if 'test-policy.xml' in file_str and 'b' not in mode:
1415+
return m(file, *args, **kwargs)
1416+
return real_open(file, *args, **kwargs)
1417+
1418+
monkeypatch.setattr(builtins, 'open', open_selector)
1419+
1420+
replacements = {'missing_key': 'value'}
1421+
1422+
with caplog.at_level(logging.WARNING):
1423+
_ = utils.read_and_modify_policy_xml('/path/to/test-policy.xml', replacements)
1424+
1425+
# Should log warning about missing placeholder
1426+
assert any('missing_key' in record.message for record in caplog.records)
1427+
1428+
1429+
def test_read_and_modify_policy_xml_none_replacements(monkeypatch):
1430+
"""Test read_and_modify_policy_xml with None replacements."""
1431+
xml_content = '<policy><key>{jwt_key}</key></policy>'
1432+
m = mock_open(read_data=xml_content)
1433+
1434+
real_open = builtins.open
1435+
1436+
def open_selector(file, *args, **kwargs):
1437+
mode = kwargs.get('mode', args[0] if args else 'r')
1438+
file_str = str(file)
1439+
if 'test-policy.xml' in file_str and 'b' not in mode:
1440+
return m(file, *args, **kwargs)
1441+
return real_open(file, *args, **kwargs)
1442+
1443+
monkeypatch.setattr(builtins, 'open', open_selector)
1444+
1445+
result = utils.read_and_modify_policy_xml('/path/to/test-policy.xml', None)
1446+
# Should return unmodified XML
1447+
assert result == xml_content
1448+
1449+
1450+
# ------------------------------
1451+
# determine_shared_policy_path TESTS
1452+
# ------------------------------
1453+
1454+
def test_determine_shared_policy_path(monkeypatch):
1455+
"""Test determine_shared_policy_path function."""
1456+
monkeypatch.setattr(utils, 'find_project_root', lambda: 'c:\\mock\\project')
1457+
1458+
result = utils.determine_shared_policy_path('test-policy.xml')
1459+
expected = 'c:\\mock\\project\\shared\\apim-policies\\fragments\\test-policy.xml'
1460+
assert result == expected
1461+
1462+
1463+
# ------------------------------
1464+
# InfrastructureNotebookHelper TESTS
1465+
# ------------------------------
1466+
1467+
def test_infrastructure_notebook_helper_bypass_check(monkeypatch):
1468+
"""Test InfrastructureNotebookHelper with bypass_infrastructure_check=True."""
1469+
helper = utils.InfrastructureNotebookHelper('eastus', INFRASTRUCTURE.SIMPLE_APIM, 1, APIM_SKU.BASICV2)
1470+
1471+
# Mock subprocess execution to succeed
1472+
class MockProcess:
1473+
def __init__(self, *args, **kwargs):
1474+
self.returncode = 0
1475+
self.stdout = iter(['Mock deployment output\n'])
1476+
1477+
def wait(self):
1478+
pass
1479+
1480+
def __enter__(self):
1481+
return self
1482+
1483+
def __exit__(self, *args):
1484+
pass
1485+
1486+
monkeypatch.setattr('subprocess.Popen', MockProcess)
1487+
monkeypatch.setattr(utils, 'find_project_root', lambda: 'c:\\mock\\root')
1488+
monkeypatch.setattr('builtins.print', lambda *args, **kwargs: None)
1489+
1490+
# Test with bypass_infrastructure_check=True
1491+
result = helper.create_infrastructure(bypass_infrastructure_check=True)
1492+
assert result is True
1493+
1494+
1495+
def test_infrastructure_notebook_helper_allow_update_false(monkeypatch):
1496+
"""Test InfrastructureNotebookHelper with allow_update=False."""
1497+
helper = utils.InfrastructureNotebookHelper('eastus', INFRASTRUCTURE.SIMPLE_APIM, 1, APIM_SKU.BASICV2)
1498+
1499+
# Mock RG exists but allow_update=False
1500+
monkeypatch.setattr(az, 'does_resource_group_exist', lambda x: True)
1501+
1502+
# Mock subprocess execution to succeed
1503+
class MockProcess:
1504+
def __init__(self, *args, **kwargs):
1505+
self.returncode = 0
1506+
self.stdout = iter(['Mock deployment output\n'])
1507+
1508+
def wait(self):
1509+
pass
1510+
1511+
def __enter__(self):
1512+
return self
1513+
1514+
def __exit__(self, *args):
1515+
pass
1516+
1517+
monkeypatch.setattr('subprocess.Popen', MockProcess)
1518+
monkeypatch.setattr(utils, 'find_project_root', lambda: 'c:\\mock\\root')
1519+
monkeypatch.setattr('builtins.print', lambda *args, **kwargs: None)
1520+
1521+
# With allow_update=False, should still create when infrastructure doesn't exist
1522+
result = helper.create_infrastructure(allow_update=False, bypass_infrastructure_check=True)
1523+
assert result is True

0 commit comments

Comments
 (0)