Skip to content

Commit 700abbd

Browse files
Increase coverage
1 parent ee1ce9b commit 700abbd

File tree

9 files changed

+693
-198
lines changed

9 files changed

+693
-198
lines changed

tests/python/.pylintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ disable =
2020
R0801, # Duplicate code
2121
R0902, # Too many instance attributes
2222
R0903, # Too few public methods
23+
R0904, # Too many public methods
2324
R0911, # Too many return statements
2425
R0912, # Too many branches
2526
R0913, # Too many arguments

tests/python/test_apimtypes.py

Lines changed: 224 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
Unit tests for apimtypes.py
33
"""
44

5+
import importlib
56
from pathlib import Path
67
import pytest
8+
import apimtypes
79

810
# APIM Samples imports
911
from apimtypes import API, APIMNetworkMode, APIM_SKU, APIOperation, BACKEND_XML_POLICY_PATH, DEFAULT_XML_POLICY_PATH, GET_APIOperation, \
10-
get_project_root, HELLO_WORLD_XML_POLICY_PATH, HTTP_VERB, INFRASTRUCTURE, NamedValue, Output, PolicyFragment, POST_APIOperation, \
11-
Product, REQUEST_HEADERS_XML_POLICY_PATH, Role, SUBSCRIPTION_KEY_PARAMETER_NAME, SLEEP_TIME_BETWEEN_REQUESTS_MS
12+
GET_APIOperation2, get_project_root, HELLO_WORLD_XML_POLICY_PATH, HTTP_VERB, INFRASTRUCTURE, NamedValue, Output, PolicyFragment, \
13+
POST_APIOperation, Product, REQUEST_HEADERS_XML_POLICY_PATH, Role, SUBSCRIPTION_KEY_PARAMETER_NAME, SLEEP_TIME_BETWEEN_REQUESTS_MS
1214
from test_helpers import assert_policy_fragment_structure
1315

1416

@@ -213,6 +215,27 @@ def test_apim_sku(self, enum_value, expected):
213215
"""Test APIM_SKU enum values."""
214216
assert enum_value == expected
215217

218+
@pytest.mark.parametrize('sku', [
219+
APIM_SKU.DEVELOPER,
220+
APIM_SKU.BASIC,
221+
APIM_SKU.STANDARD,
222+
APIM_SKU.PREMIUM
223+
])
224+
def test_apim_sku_is_v1(self, sku):
225+
"""Test APIM_SKU.is_v1() method for v1 SKUs."""
226+
assert sku.is_v1() is True
227+
assert sku.is_v2() is False
228+
229+
@pytest.mark.parametrize('sku', [
230+
APIM_SKU.BASICV2,
231+
APIM_SKU.STANDARDV2,
232+
APIM_SKU.PREMIUMV2
233+
])
234+
def test_apim_sku_is_v2(self, sku):
235+
"""Test APIM_SKU.is_v2() method for v2 SKUs."""
236+
assert sku.is_v2() is True
237+
assert sku.is_v1() is False
238+
216239
@pytest.mark.parametrize('enum_value,expected', [
217240
(HTTP_VERB.GET, 'GET'),
218241
(HTTP_VERB.POST, 'POST'),
@@ -284,6 +307,24 @@ def test_get_operation(self):
284307
assert op.policyXml == '<xml/>'
285308
assert op.to_dict()['method'] == HTTP_VERB.GET
286309

310+
def test_get_operation2(self):
311+
"""Test GET_APIOperation2 class with custom parameters."""
312+
op = GET_APIOperation2(
313+
name='get-users',
314+
displayName='Get Users',
315+
urlTemplate='/users',
316+
description='Get all users',
317+
policyXml='<custom/>'
318+
)
319+
320+
assert op.name == 'get-users'
321+
assert op.displayName == 'Get Users'
322+
assert op.urlTemplate == '/users'
323+
assert op.method == HTTP_VERB.GET
324+
assert op.description == 'Get all users'
325+
assert op.policyXml == '<custom/>'
326+
assert op.to_dict()['method'] == HTTP_VERB.GET
327+
287328
def test_post_operation(self):
288329
"""Test POST_APIOperation convenience class."""
289330
op = POST_APIOperation(description='desc', policyXml='<xml/>')
@@ -361,6 +402,18 @@ def test_approval_required_default(self, base_product_params):
361402
product = Product(**base_product_params)
362403
assert product.approvalRequired is False
363404

405+
def test_product_fallback_policy_when_file_not_found(self, monkeypatch, base_product_params):
406+
"""Test Product uses fallback policy when default policy file is not found."""
407+
def mock_read_policy_xml_raise(path):
408+
raise FileNotFoundError(f'Policy file not found: {path}')
409+
410+
monkeypatch.setattr(apimtypes, '_read_policy_xml', mock_read_policy_xml_raise)
411+
412+
product = Product(**base_product_params)
413+
assert product.policyXml is not None
414+
assert '<policies>' in product.policyXml
415+
assert '<inbound>' in product.policyXml
416+
364417

365418
class TestProductSerialization:
366419
"""Test suite for Product.to_dict() method."""
@@ -451,6 +504,164 @@ def test_json_parsing_invalid(self):
451504
output = Output(success=True, text='not json')
452505
assert output.json_data is None
453506

507+
def test_get_method_with_properties_structure(self):
508+
"""Test Output.get() with standard deployment output structure."""
509+
json_text = '''{"properties": {"outputs": {"endpoint": {"value": "https://test.com"}}}}'''
510+
output = Output(success=True, text=json_text)
511+
512+
result = output.get('endpoint', suppress_logging=True)
513+
assert result == 'https://test.com'
514+
515+
def test_get_method_with_simple_structure(self):
516+
"""Test Output.get() with simple output structure."""
517+
json_text = '''{"endpoint": {"value": "https://simple.com"}}'''
518+
output = Output(success=True, text=json_text)
519+
520+
result = output.get('endpoint', suppress_logging=True)
521+
assert result == 'https://simple.com'
522+
523+
def test_get_method_key_not_found(self):
524+
"""Test Output.get() when key is not found."""
525+
json_text = '''{"properties": {"outputs": {"other": {"value": "val"}}}}'''
526+
output = Output(success=True, text=json_text)
527+
528+
result = output.get('missing', suppress_logging=True)
529+
assert result is None
530+
531+
def test_get_method_key_not_found_with_label_raises(self):
532+
"""Test Output.get() raises when key not found and label provided."""
533+
json_text = '''{"properties": {"outputs": {"other": {"value": "val"}}}}'''
534+
output = Output(success=True, text=json_text)
535+
536+
with pytest.raises(Exception):
537+
output.get('missing', label='Test Label', suppress_logging=True)
538+
539+
def test_get_method_with_label_and_secure_masking(self):
540+
"""Test Output.get() with label and secure masking."""
541+
json_text = '''{"properties": {"outputs": {"secret": {"value": "supersecretvalue"}}}}'''
542+
output = Output(success=True, text=json_text)
543+
544+
result = output.get('secret', label='Secret', secure=True)
545+
assert result == 'supersecretvalue'
546+
547+
def test_get_method_json_data_not_dict(self):
548+
"""Test Output.get() when json_data is not a dict."""
549+
output = Output(success=True, text='["array", "data"]')
550+
551+
result = output.get('key', suppress_logging=True)
552+
assert result is None
553+
554+
def test_get_method_properties_not_dict(self):
555+
"""Test Output.get() when properties is not a dict."""
556+
json_text = '''{"properties": "not a dict"}'''
557+
output = Output(success=True, text=json_text)
558+
559+
result = output.get('key', suppress_logging=True)
560+
assert result is None
561+
562+
def test_get_method_outputs_not_dict(self):
563+
"""Test Output.get() when outputs is not a dict."""
564+
json_text = '''{"properties": {"outputs": "not a dict"}}'''
565+
output = Output(success=True, text=json_text)
566+
567+
result = output.get('key', suppress_logging=True)
568+
assert result is None
569+
570+
def test_get_method_output_entry_invalid(self):
571+
"""Test Output.get() when output entry is invalid."""
572+
json_text = '''{"properties": {"outputs": {"key": "no value field"}}}'''
573+
output = Output(success=True, text=json_text)
574+
575+
result = output.get('key', suppress_logging=True)
576+
assert result is None
577+
578+
def test_getjson_method_with_dict_value(self):
579+
"""Test Output.getJson() with dictionary value."""
580+
json_text = '''{"properties": {"outputs": {"config": {"value": {"key": "val"}}}}}'''
581+
output = Output(success=True, text=json_text)
582+
583+
result = output.getJson('config', suppress_logging=True)
584+
assert result == {'key': 'val'}
585+
586+
def test_getjson_method_with_string_json(self):
587+
"""Test Output.getJson() parsing string as JSON."""
588+
json_text = '''{"properties": {"outputs": {"data": {"value": "{\\"nested\\": \\"value\\"}"}}}}'''
589+
output = Output(success=True, text=json_text)
590+
591+
result = output.getJson('data', suppress_logging=True)
592+
assert result == {'nested': 'value'}
593+
594+
def test_getjson_method_with_python_literal(self):
595+
"""Test Output.getJson() parsing Python literal."""
596+
json_text = '''{"properties": {"outputs": {"data": {"value": "{'key': 'value'}"}}}}'''
597+
output = Output(success=True, text=json_text)
598+
599+
result = output.getJson('data', suppress_logging=True)
600+
assert result == {'key': 'value'}
601+
602+
def test_getjson_method_unparseable_string(self):
603+
"""Test Output.getJson() with unparseable string returns original value."""
604+
json_text = '''{"properties": {"outputs": {"data": {"value": "not valid json or literal"}}}}'''
605+
output = Output(success=True, text=json_text)
606+
607+
result = output.getJson('data')
608+
assert result == 'not valid json or literal'
609+
610+
def test_getjson_method_key_not_found(self):
611+
"""Test Output.getJson() when key not found."""
612+
json_text = '''{"properties": {"outputs": {"other": {"value": "val"}}}}'''
613+
output = Output(success=True, text=json_text)
614+
615+
result = output.getJson('missing', suppress_logging=True)
616+
assert result is None
617+
618+
def test_getjson_method_raises_with_label(self):
619+
"""Test Output.getJson() raises when key not found and label provided."""
620+
json_text = '''{"properties": {"outputs": {}}}'''
621+
output = Output(success=True, text=json_text)
622+
623+
with pytest.raises(Exception):
624+
output.getJson('missing', label='Test')
625+
626+
def test_getjson_method_json_data_not_dict(self):
627+
"""Test Output.getJson() when json_data is not a dict."""
628+
output = Output(success=True, text='[1, 2, 3]')
629+
630+
result = output.getJson('key', suppress_logging=True)
631+
assert result is None
632+
633+
def test_getjson_method_properties_not_dict(self):
634+
"""Test Output.getJson() when properties is not a dict."""
635+
json_text = '''{"properties": ["not", "a", "dict"]}'''
636+
output = Output(success=True, text=json_text)
637+
638+
result = output.getJson('key', suppress_logging=True)
639+
assert result is None
640+
641+
def test_getjson_method_outputs_not_dict(self):
642+
"""Test Output.getJson() when outputs is not a dict."""
643+
json_text = '''{"properties": {"outputs": ["not", "dict"]}}'''
644+
output = Output(success=True, text=json_text)
645+
646+
result = output.getJson('key', suppress_logging=True)
647+
assert result is None
648+
649+
def test_getjson_method_output_entry_invalid(self):
650+
"""Test Output.getJson() when output entry is missing value field."""
651+
json_text = '''{"properties": {"outputs": {"key": {"no_value": "here"}}}}'''
652+
output = Output(success=True, text=json_text)
653+
654+
result = output.getJson('key', suppress_logging=True)
655+
assert result is None
656+
657+
def test_output_with_simple_structure_getjson(self):
658+
"""Test Output.getJson() with simple structure (no properties wrapper)."""
659+
json_text = '''{"data": {"value": {"nested": "obj"}}}'''
660+
output = Output(success=True, text=json_text)
661+
662+
result = output.getJson('data', suppress_logging=True)
663+
assert result == {'nested': 'obj'}
664+
454665

455666
# ------------------------------
456667
# NAMED VALUE TESTS
@@ -536,3 +747,14 @@ def test_get_project_root(self):
536747
assert isinstance(root, Path)
537748
assert root.exists()
538749
assert root.is_dir()
750+
751+
def test_get_project_root_from_env_var(self, monkeypatch):
752+
"""Test get_project_root uses PROJECT_ROOT environment variable."""
753+
test_path = Path('c:/test/project')
754+
monkeypatch.setenv('PROJECT_ROOT', str(test_path))
755+
756+
# Need to reimport to pick up new env var
757+
importlib.reload(apimtypes)
758+
759+
root = apimtypes.get_project_root()
760+
assert root == test_path

0 commit comments

Comments
 (0)