You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by er...@apache.org on 2023/05/10 20:50:53 UTC

[trafficcontrol] branch master updated: Api contract regions (#7458)

This is an automated email from the ASF dual-hosted git repository.

ericholguin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 40eee52ded Api contract regions (#7458)
40eee52ded is described below

commit 40eee52dedd1f80c8908b88029a853637b721fc7
Author: Kashatlast2 <10...@users.noreply.github.com>
AuthorDate: Wed May 10 16:50:41 2023 -0400

    Api contract regions (#7458)
    
    * Added divisions test case
    
    * resolve linting issues
    
    * trailing new lines removed
    
    * Added Regions test
    
    * fixed linting errors
    
    * updated code for regions and deleted divisions test
    
    * updated regions test case
    
    * updated regions test case and conftest
    
    * updated conftest.py
    
    * added new line
    
    * Update request_template.json removed phys_locations
    
    * Update response_template.json removed phys_locations
    
    * New line removed - Update conftest.py
    
    * Replaced Region ID with Region Name - Update test_regions.py
    
    * Resolved tabbing issues - Update conftest.py
    
    * Fixed Formatting - Update request_template.json
    
    ---------
    
    Co-authored-by: yblanc545 <Yv...@comcast.com>
---
 traffic_ops/testing/api_contract/v4/conftest.py    |  61 +++++++++++-
 .../testing/api_contract/v4/request_template.json  |  11 ++-
 .../testing/api_contract/v4/response_template.json |  20 ++++
 .../testing/api_contract/v4/test_regions.py        | 108 +++++++++++++++++++++
 4 files changed, 196 insertions(+), 4 deletions(-)

diff --git a/traffic_ops/testing/api_contract/v4/conftest.py b/traffic_ops/testing/api_contract/v4/conftest.py
index ad721b3f5f..36af044e7a 100644
--- a/traffic_ops/testing/api_contract/v4/conftest.py
+++ b/traffic_ops/testing/api_contract/v4/conftest.py
@@ -672,7 +672,6 @@ def tenant_post_data(to_session: TOSession, request_template_data: list[JSONData
 		logger.error("No Parameter response data from parameters POST request.")
 		sys.exit(1)
 
-
 @pytest.fixture()
 def server_capabilities_post_data(to_session: TOSession, request_template_data: list[JSONData]
 		  ) -> dict[str, object]:
@@ -716,7 +715,6 @@ def server_capabilities_post_data(to_session: TOSession, request_template_data:
 		logger.error("No server_capabilities response data from server_capabilities POST request.")
 		sys.exit(1)
 
-
 @pytest.fixture()
 def division_post_data(to_session: TOSession, request_template_data: list[JSONData]
 		  ) -> dict[str, object]:
@@ -760,3 +758,62 @@ def division_post_data(to_session: TOSession, request_template_data: list[JSONDa
 	except IndexError:
 		logger.error("No division response data from division POST request.")
 		sys.exit(1)
+
+@pytest.fixture()
+def region_post_data(to_session: TOSession, request_template_data: list[JSONData]
+			  ) -> dict[str, object]:
+	"""
+	PyTest Fixture to create POST data for region endpoint.
+
+	:param to_session: Fixture to get Traffic Ops session.
+	:param request_template_data: Fixture to get region data from a prerequisites file.
+  	:returns: Sample POST data and the actual API response.
+	"""
+
+	try:
+		region = request_template_data[0]
+	except IndexError as e:
+		raise TypeError(
+			"malformed prerequisite data; no Region present in 'regions' array property") from e
+
+	if not isinstance(region, dict):
+		raise TypeError(f"malformed prerequisite data; region must be objects, not '{type(region)}'")
+
+	# Return new post data and post response from regions POST request
+	randstr = str(randint(0, 1000))
+	try:
+		name = region["name"]
+		if not isinstance(name, str):
+			raise TypeError(f"name must be str, not '{type(name)}'")
+		region["name"] = name[:4] + randstr
+	except KeyError as e:
+		raise TypeError(f"missing Region property '{e.args[0]}'") from e
+	# Hitting types GET method to access typeID for region POST data
+	division_get_response: tuple[
+		dict[str, object] | list[dict[str, object] | list[object] | primitive] | primitive,
+		requests.Response
+	] = to_session.get_divisions()
+	try:
+		division_data = division_get_response[0]
+		if not isinstance(division_data, list):
+			raise TypeError("malformed API response; 'response' property not an array")
+		first_division = division_data[0]
+		if not isinstance(first_division, dict):
+			raise TypeError("malformed API response; first Division in response is not an object")
+		region["division"] = first_division["id"]
+		division_id = region["division"]
+		logger.info("extracted %s from %s", division_id, division_get_response)
+	except KeyError as e:
+		raise TypeError(f"missing Regions property '{e.args[0]}'") from e
+
+	logger.info("New region data to hit POST method %s", request_template_data)
+	# Hitting region POST method
+	response: tuple[JSONData, requests.Response] = to_session.create_region(data=region)
+	try:
+		resp_obj = response[0]
+		if not isinstance(resp_obj, dict):
+			raise TypeError("malformed API response; region is not an object")
+		return resp_obj
+	except IndexError:
+		logger.error("No Region response data from divisions POST request.")
+	sys.exit(1)
diff --git a/traffic_ops/testing/api_contract/v4/request_template.json b/traffic_ops/testing/api_contract/v4/request_template.json
index 89d575da4a..51dd169dd2 100644
--- a/traffic_ops/testing/api_contract/v4/request_template.json
+++ b/traffic_ops/testing/api_contract/v4/request_template.json
@@ -59,6 +59,13 @@
     "divisions":[
         {
             "name":"test"
-            }
-      ]
+        }
+    ],
+    "regions": [
+		    {
+			"divisionName": "Quebec",
+			"division": 1,
+			"name": "Montreal"
+    		}
+   	]
 }
diff --git a/traffic_ops/testing/api_contract/v4/response_template.json b/traffic_ops/testing/api_contract/v4/response_template.json
index 7e26105dde..3955152040 100644
--- a/traffic_ops/testing/api_contract/v4/response_template.json
+++ b/traffic_ops/testing/api_contract/v4/response_template.json
@@ -197,5 +197,25 @@
                 "type": "str"
             }
         }
+    },
+    "regions": {
+        "type": "object",
+        "properties": {
+            "divisionName": {
+                "type": "str"
+            },
+            "division": {
+                "type": "int"
+            },  
+            "id": {
+                "type": "int"
+            },
+            "lastUpdated": {
+                "type": "str"
+            },
+            "name": {
+                "type": "str"
+            }
+        }
     }
 }
diff --git a/traffic_ops/testing/api_contract/v4/test_regions.py b/traffic_ops/testing/api_contract/v4/test_regions.py
new file mode 100644
index 0000000000..58466984ad
--- /dev/null
+++ b/traffic_ops/testing/api_contract/v4/test_regions.py
@@ -0,0 +1,108 @@
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""API Contract Test Case for regions endpoint."""
+import logging
+import pytest
+import requests
+
+from trafficops.tosession import TOSession
+
+# Create and configure logger
+logger = logging.getLogger()
+
+primitive = bool | int | float | str | None
+
+@pytest.mark.parametrize('request_template_data', ["regions"], indirect=True)
+def test_region_contract(
+	to_session: TOSession,
+	request_template_data: list[dict[str, object] | list[object] | primitive],
+	response_template_data: dict[str, primitive | list[primitive | dict[str, object]
+						    | list[object]] | dict[object, object]],
+	region_post_data: dict[str, object]
+) -> None:
+	"""
+	Test step to validate keys, values and data types from regions endpoint
+	response.
+	:param to_session: Fixture to get Traffic Ops session.
+	:param request_template_data: Fixture to get request template data from a prerequisites file.
+	:param response_template_data: Fixture to get response template data from a prerequisites file.
+	:param region_post_data: Fixture to get sample region data and actual region response.
+	"""
+	# validate region keys from regions get response
+	logger.info("Accessing /regions endpoint through Traffic ops session.")
+
+	region = request_template_data[0]
+	if not isinstance(region, dict):
+		raise TypeError("malformed region in prerequisite data; not an object")
+
+	region_name = region.get("name")
+	if not isinstance(region_name, str):
+		raise TypeError("malformed region in prerequisite data; 'name' not a string")
+
+	region_get_response: tuple[
+		dict[str, object] | list[dict[str, object] | list[object] | primitive] | primitive,
+		requests.Response
+	] = to_session.get_regions(query_params={"name": region_name})
+	try:
+		region_data = region_get_response[0]
+		if not isinstance(region_data, list):
+			raise TypeError("malformed API response; 'response' property not an array")
+
+		first_region = region_data[0]
+		if not isinstance(first_region, dict):
+			raise TypeError("malformed API response; first region in response is not an object")
+		region_keys = set(first_region.keys())
+
+		logger.info("region Keys from regions endpoint response %s", region_keys)
+		region_response_template = response_template_data.get("regions")
+		if not isinstance(region_response_template, dict):
+			raise TypeError(
+				f"region response template data must be a dict, not '{type(region_response_template)}'")
+		response_template: dict[str, list[dict[str, object] | list[object] | primitive] |\
+			dict[object, object] |\
+			primitive
+		]
+		response_template = region_response_template.get("properties")
+		# validate region values from prereq data in regions get response.
+		prereq_values = [region_post_data["name"], region_post_data["division"],
+		region_post_data["divisionName"]]
+		get_values = [first_region["name"], first_region["division"], first_region["divisionName"]]
+		get_types = {}
+		for key, value in first_region.items():
+			get_types[key] = type(value).__name__
+		logger.info("types from region get response %s", get_types)
+		response_template_types= {}
+		for key, value in response_template.items():
+			actual_type = value.get("type")
+			if not isinstance(actual_type, str):
+				raise TypeError(
+					f"Type data must be a string, not '{type(actual_type)}'")
+			response_template_types[key] = actual_type
+		logger.info("types from regions response template %s", response_template_types)
+		# validate keys, data types and values from regions get json response.
+		assert region_keys == set(response_template.keys())
+		assert dict(sorted(get_types.items())) == dict(sorted(response_template_types.items()))
+		assert get_values == prereq_values
+	except IndexError:
+		logger.error("Either prerequisite data or API response was malformed")
+		pytest.fail("API contract test failed for regions endpoint: API response was malformed")
+	finally:
+		# Delete region after test execution to avoid redundancy.
+		try:
+			region_name = region_post_data["name"]
+			to_session.delete_region(query_params={"name": region_name})
+		except IndexError:
+			logger.error("region returned by Traffic Ops is missing a 'name' property")
+			pytest.fail("Response from delete request is empty, Failing test_region_contract")