Skip to content

Commit 2c1138c

Browse files
First batch of updated tests
1 parent b42af8d commit 2c1138c

File tree

5 files changed

+594
-0
lines changed

5 files changed

+594
-0
lines changed

tests/python/test_apimtypes.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,194 @@ def testget_project_root_functionality():
822822
assert isinstance(root, Path)
823823
assert root.exists()
824824

825+
def test_output_class_basic():
826+
"""Test Output class initialization and properties."""
827+
# Test successful output with JSON
828+
output = Output(True, '{"key": "value"}')
829+
assert output.success is True
830+
assert output.text == '{"key": "value"}'
831+
assert output.json_data == {"key": "value"}
832+
assert output.is_json is True
833+
834+
def test_output_class_non_json():
835+
"""Test Output class with non-JSON text."""
836+
output = Output(True, 'some plain text')
837+
assert output.success is True
838+
assert output.text == 'some plain text'
839+
assert output.json_data is None
840+
assert output.is_json is False
841+
842+
def test_output_get_with_properties_structure():
843+
"""Test Output.get() with deployment output structure."""
844+
json_text = json.dumps({
845+
'properties': {
846+
'outputs': {
847+
'apimName': {'value': 'my-apim'}
848+
}
849+
}
850+
})
851+
output = Output(True, json_text)
852+
result = output.get('apimName', suppress_logging=True)
853+
assert result == 'my-apim'
854+
855+
def test_output_get_missing_key():
856+
"""Test Output.get() with missing key."""
857+
json_text = json.dumps({
858+
'properties': {
859+
'outputs': {
860+
'apimName': {'value': 'my-apim'}
861+
}
862+
}
863+
})
864+
output = Output(True, json_text)
865+
result = output.get('nonExistent', suppress_logging=True)
866+
assert result is None
867+
868+
def test_output_get_non_dict_json():
869+
"""Test Output.get() when json_data is not a dict."""
870+
output = Output(True, '[1, 2, 3]')
871+
result = output.get('key', suppress_logging=True)
872+
assert result is None
873+
874+
def test_output_get_missing_properties():
875+
"""Test Output.get() when 'properties' key is missing."""
876+
json_text = json.dumps({
877+
'data': {
878+
'outputs': {
879+
'apimName': {'value': 'my-apim'}
880+
}
881+
}
882+
})
883+
output = Output(True, json_text)
884+
# Should look for key at root level
885+
result = output.get('apimName', suppress_logging=True)
886+
assert result is None
887+
888+
def test_output_getJson_with_nested_structure():
889+
"""Test Output.getJson() returns parsed JSON from nested value."""
890+
nested_json = '{"nested": "data"}'
891+
json_text = json.dumps({
892+
'properties': {
893+
'outputs': {
894+
'config': {'value': nested_json}
895+
}
896+
}
897+
})
898+
output = Output(True, json_text)
899+
result = output.getJson('config', suppress_logging=True)
900+
assert result == {"nested": "data"}
901+
902+
def test_output_getJson_with_dict_value():
903+
"""Test Output.getJson() with dict value."""
904+
json_text = json.dumps({
905+
'properties': {
906+
'outputs': {
907+
'config': {'value': {"nested": "dict"}}
908+
}
909+
}
910+
})
911+
output = Output(True, json_text)
912+
result = output.getJson('config', suppress_logging=True)
913+
assert result == {"nested": "dict"}
914+
915+
def test_output_getJson_with_missing_key():
916+
"""Test Output.getJson() with missing key."""
917+
json_text = json.dumps({
918+
'properties': {
919+
'outputs': {
920+
'apimName': {'value': 'my-apim'}
921+
}
922+
}
923+
})
924+
output = Output(True, json_text)
925+
result = output.getJson('nonExistent', suppress_logging=True)
926+
assert result is None
927+
928+
def test_output_get_with_direct_key():
929+
"""Test Output.get() when output key is at root level."""
930+
json_text = json.dumps({
931+
'apimName': {'value': 'my-apim'},
932+
'location': {'value': 'eastus'}
933+
})
934+
output = Output(True, json_text)
935+
result = output.get('apimName', suppress_logging=True)
936+
assert result == 'my-apim'
937+
938+
def test_output_get_with_label_and_secure():
939+
"""Test Output.get() with label and secure masking."""
940+
json_text = json.dumps({
941+
'properties': {
942+
'outputs': {
943+
'secretKey': {'value': 'very-secret-key-12345'}
944+
}
945+
}
946+
})
947+
output = Output(True, json_text)
948+
# Should not raise even with label; we suppress logging in test
949+
result = output.get('secretKey', label='Secret', secure=True, suppress_logging=True)
950+
assert result == 'very-secret-key-12345'
951+
952+
def test_output_getJson_with_list_value():
953+
"""Test Output.getJson() with array value."""
954+
json_text = json.dumps({
955+
'properties': {
956+
'outputs': {
957+
'items': {'value': [1, 2, 3, 4, 5]}
958+
}
959+
}
960+
})
961+
output = Output(True, json_text)
962+
result = output.getJson('items', suppress_logging=True)
963+
assert result == [1, 2, 3, 4, 5]
964+
965+
def test_output_getJson_with_string_json_value():
966+
"""Test Output.getJson() when value is JSON-formatted string."""
967+
nested_json = '{"nested": "object"}'
968+
json_text = json.dumps({
969+
'properties': {
970+
'outputs': {
971+
'config': {'value': nested_json}
972+
}
973+
}
974+
})
975+
output = Output(True, json_text)
976+
result = output.getJson('config', suppress_logging=True)
977+
assert result == {"nested": "object"}
978+
979+
def test_output_get_empty_string_value():
980+
"""Test Output.get() with empty string value."""
981+
json_text = json.dumps({
982+
'properties': {
983+
'outputs': {
984+
'empty': {'value': ''}
985+
}
986+
}
987+
})
988+
output = Output(True, json_text)
989+
result = output.get('empty', suppress_logging=True)
990+
assert result == ''
991+
992+
def test_output_getJson_empty_object():
993+
"""Test Output.getJson() with empty JSON object."""
994+
json_text = json.dumps({
995+
'properties': {
996+
'outputs': {
997+
'emptyObj': {'value': {}}
998+
}
999+
}
1000+
})
1001+
output = Output(True, json_text)
1002+
result = output.getJson('emptyObj', suppress_logging=True)
1003+
assert result == {}
1004+
1005+
def test_output_parse_error_handling():
1006+
"""Test Output class handles JSON parse errors gracefully."""
1007+
# JSON that doesn't parse but has structure
1008+
output = Output(True, '{invalid json here}')
1009+
# Should still initialize without crashing
1010+
assert output.text == '{invalid json here}'
1011+
assert output.success is True
1012+
8251013

8261014
def test_api_edge_cases():
8271015
"""Test API class with edge cases and full coverage."""

tests/python/test_azure_resources.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,122 @@ def test_get_account_info_no_json():
152152

153153
assert 'Failed to retrieve account information' in str(exc_info.value)
154154

155+
# ------------------------------
156+
# JWT SIGNING KEY CLEANUP TESTS
157+
# ------------------------------
158+
159+
def test_cleanup_old_jwt_signing_keys_success(monkeypatch):
160+
"""Test successful cleanup of old JWT signing keys."""
161+
162+
run_calls: list[str] = []
163+
164+
def fake_run(cmd: str, *args, **kwargs):
165+
run_calls.append(cmd)
166+
167+
if 'nv list' in cmd:
168+
return Output(True, 'JwtSigningKey-sample-123\nJwtSigningKey-sample-456\n')
169+
170+
if 'nv delete' in cmd:
171+
# Only the non-current key should be deleted
172+
return Output(True, '')
173+
174+
return Output(False, 'unexpected command')
175+
176+
monkeypatch.setattr(az, 'run', fake_run)
177+
monkeypatch.setattr(az, 'print_message', lambda *a, **k: None)
178+
monkeypatch.setattr(az, 'print_info', lambda *a, **k: None)
179+
monkeypatch.setattr(az, 'print_ok', lambda *a, **k: None)
180+
monkeypatch.setattr(az, 'print_error', lambda *a, **k: None)
181+
182+
result = az.cleanup_old_jwt_signing_keys('apim', 'rg', 'JwtSigningKey-sample-456')
183+
184+
assert result is True
185+
assert any('nv list' in c for c in run_calls)
186+
delete_calls = [c for c in run_calls if 'nv delete' in c]
187+
assert len(delete_calls) == 1
188+
assert 'JwtSigningKey-sample-123' in delete_calls[0]
189+
190+
191+
def test_cleanup_old_jwt_signing_keys_invalid_pattern(monkeypatch):
192+
"""Test cleanup when current key name does not match expected pattern."""
193+
194+
monkeypatch.setattr(az, 'run', lambda *a, **k: pytest.fail('run should not be called'))
195+
monkeypatch.setattr(az, 'print_message', lambda *a, **k: None)
196+
monkeypatch.setattr(az, 'print_info', lambda *a, **k: None)
197+
monkeypatch.setattr(az, 'print_ok', lambda *a, **k: None)
198+
199+
result = az.cleanup_old_jwt_signing_keys('apim', 'rg', 'invalid-key-name')
200+
201+
assert result is False
202+
203+
204+
# ------------------------------
205+
# APIM BLOB PERMISSIONS TESTS
206+
# ------------------------------
207+
208+
def test_check_apim_blob_permissions_success(monkeypatch):
209+
"""Test blob permission check succeeds when role assignment and access test succeed."""
210+
211+
monkeypatch.setattr(az, 'get_azure_role_guid', lambda *_: 'role-guid')
212+
monkeypatch.setattr(az, 'print_info', lambda *a, **k: None)
213+
monkeypatch.setattr(az, 'print_ok', lambda *a, **k: None)
214+
monkeypatch.setattr(az, 'print_warning', lambda *a, **k: None)
215+
monkeypatch.setattr(az, 'print_error', lambda *a, **k: None)
216+
217+
run_calls: list[str] = []
218+
219+
def fake_run(cmd: str, *args, **kwargs):
220+
run_calls.append(cmd)
221+
222+
if 'apim show' in cmd:
223+
return Output(True, 'principal-id\n')
224+
225+
if 'storage account show' in cmd:
226+
return Output(True, 'notice\n/subscriptions/123/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/storage\n')
227+
228+
if 'role assignment list' in cmd:
229+
return Output(True, 'assignment-id\n')
230+
231+
if 'storage blob list' in cmd:
232+
return Output(True, 'blob-name\n')
233+
234+
return Output(False, 'unexpected command')
235+
236+
monkeypatch.setattr(az, 'run', fake_run)
237+
monkeypatch.setattr(az.time, 'sleep', lambda *a, **k: None)
238+
239+
result = az.check_apim_blob_permissions('apim', 'storage', 'rg', max_wait_minutes = 1)
240+
241+
assert result is True
242+
assert any('role assignment list' in c for c in run_calls)
243+
assert any('storage blob list' in c for c in run_calls)
244+
245+
246+
def test_check_apim_blob_permissions_missing_resource_id(monkeypatch):
247+
"""Test blob permission check fails when storage account ID cannot be parsed."""
248+
249+
monkeypatch.setattr(az, 'get_azure_role_guid', lambda *_: 'role-guid')
250+
monkeypatch.setattr(az, 'print_info', lambda *a, **k: None)
251+
monkeypatch.setattr(az, 'print_ok', lambda *a, **k: None)
252+
monkeypatch.setattr(az, 'print_warning', lambda *a, **k: None)
253+
monkeypatch.setattr(az, 'print_error', lambda *a, **k: None)
254+
255+
def fake_run(cmd: str, *args, **kwargs):
256+
if 'apim show' in cmd:
257+
return Output(True, 'principal-id\n')
258+
259+
if 'storage account show' in cmd:
260+
return Output(True, 'no matching id here')
261+
262+
return Output(False, 'unexpected command')
263+
264+
monkeypatch.setattr(az, 'run', fake_run)
265+
266+
result = az.check_apim_blob_permissions('apim', 'storage', 'rg')
267+
268+
assert result is False
269+
270+
155271
# ------------------------------
156272
# DEPLOYMENT NAME TESTS
157273
# ------------------------------

0 commit comments

Comments
 (0)