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/04/26 19:34:46 UTC

[trafficcontrol] branch master updated: Api contract server capabilities (#7448)

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 43e0af1f9e Api contract server capabilities (#7448)
43e0af1f9e is described below

commit 43e0af1f9e50f8e63639c4e83f5a8bb1aabcc7b0
Author: Gokula Krishnan <67...@users.noreply.github.com>
AuthorDate: Thu Apr 27 01:04:39 2023 +0530

    Api contract server capabilities (#7448)
    
    * Added Api contract TC for server_capabilities endpoint
    
    * Added setup.py file
    
    * Added setup.py file
    
    * Modified setup.py
    
    * Modified tosession.py
    
    * Modified tosession.py
    
    * Resolved conflicts
    
    ---------
    
    Co-authored-by: Gokula krishnan <go...@comcast.com>
---
 .../clients/python/trafficops/tosession.py         |  34 +++++++
 traffic_ops/testing/api_contract/README.md         |  28 +++++-
 traffic_ops/testing/api_contract/v4/conftest.py    |  48 ++++++++-
 .../testing/api_contract/v4/request_template.json  |   5 +
 .../testing/api_contract/v4/requirements.txt       |   1 -
 .../testing/api_contract/v4/response_template.json |  11 +++
 .../api_contract/v4/test_server_capabilities.py    | 108 +++++++++++++++++++++
 7 files changed, 231 insertions(+), 4 deletions(-)

diff --git a/traffic_control/clients/python/trafficops/tosession.py b/traffic_control/clients/python/trafficops/tosession.py
index de74cded56..a38068ea17 100644
--- a/traffic_control/clients/python/trafficops/tosession.py
+++ b/traffic_control/clients/python/trafficops/tosession.py
@@ -1348,6 +1348,40 @@ class TOSession(RestApiSession):
 		:rtype: Tuple[Dict[str, Any], requests.Response]
 		:raises: Union[LoginError, OperationError]
 		"""
+	
+	#
+	# Server_capabilities
+	#
+	@api_request('get', 'server_capabilities', ('3.0', '4.0', '4.1', '5.0'))
+	def get_server_capabilities(self, query_params=None):
+		"""
+		Get all Server_capabilities.
+		:ref:`to-api-server_capabilities`
+		:rtype: Tuple[Union[Dict[str, Any], List[Dict[str, Any]]], requests.Response]
+		:raises: Union[LoginError, OperationError]
+		"""
+
+
+	@api_request('post', 'server_capabilities', ('3.0', '4.0', '4.1', '5.0'))
+	def create_server_capabilities(self, data=None):
+		"""
+		Create Server_capabilities
+		:ref:`to-api-server_capabilities`
+		:param data: The server_capabilities(s) data to use for server_capabilities creation.
+		:type data: Union[Dict[str, Any], List[Dict[str, Any]]]
+		:rtype: Tuple[Dict[str, Any], requests.Response]
+		:raises: Union[LoginError, OperationError]
+		"""
+
+
+	@api_request('delete', 'server_capabilities', ('3.0', '4.0', '4.1', '5.0'))
+	def delete_server_capabilities(self, query_params=None):
+		"""
+		Delete server_capabilities
+		:ref:`to-api-server_capabilities`
+		:rtype: Tuple[Dict[str, Any], requests.Response]
+		:raises: Union[LoginError, OperationError]
+		"""
 
 	#
 	# Physical Location
diff --git a/traffic_ops/testing/api_contract/README.md b/traffic_ops/testing/api_contract/README.md
index ce08da215c..40c42ca032 100644
--- a/traffic_ops/testing/api_contract/README.md
+++ b/traffic_ops/testing/api_contract/README.md
@@ -27,7 +27,33 @@ In order to run the tests you will need a running instance of Traffic Ops and Tr
 
 1. Follow the instructions for building and running Traffic Ops from docs.
 
-2. Install the requirements for testing API contract tests.
+2. Make sure you don't have the published Apache TrafficControl
+
+   ```console
+   pip uninstall Apache-TrafficControl
+   ```
+
+3. Install packages via setup.py under traffic_control/clients/python/setup.py
+   
+   ```console
+   sudo python3 setup.py install
+   ```
+
+4. Install local Apache Traffic Control under trafficcontrol root directory
+
+   ```console
+   pip install traffic_control/clients/python
+   ```
+
+5. Make sure a build is generated under traffic_control/clients/python
+
+6. set the PYTHONPATH environment variable to that directory (replacing the absolute path with your own):
+
+   ```console
+   export PYTHONPATH=/absolute/path/to/your/repo/trafficcontrol/traffic_control/clients/python/build/lib
+   ```
+
+7. Install the requirements under traffic_ops/testing/api_contract/v4/requirements.txt.
 
     ```console
     pip install -r /path/to/requirements.txt
diff --git a/traffic_ops/testing/api_contract/v4/conftest.py b/traffic_ops/testing/api_contract/v4/conftest.py
index 2f9f6365bc..0d58cc0a27 100644
--- a/traffic_ops/testing/api_contract/v4/conftest.py
+++ b/traffic_ops/testing/api_contract/v4/conftest.py
@@ -480,7 +480,7 @@ def parameter_post_data(to_session: TOSession, request_template_data: list[JSOND
 	PyTest Fixture to create POST data for parameters endpoint.
 
 	:param to_session: Fixture to get Traffic Ops session.
-	:param request_template_data: Fixture to get CDN request template data from a prerequisites file.
+	:param request_template_data: Fixture to get parameter data from a prerequisites file.
 	:returns: Sample POST data and the actual API response.
 	"""
 
@@ -635,7 +635,6 @@ def tenant_post_data(to_session: TOSession, request_template_data: list[JSONData
 		  ) -> dict[str, object]:
 	"""
 	PyTest Fixture to create POST data for tenants endpoint.
-
 	:param to_session: Fixture to get Traffic Ops session.
 	:param request_template_data: Fixture to get tenant request template from a prerequisites file.
 	:returns: Sample POST data and the actual API response.
@@ -672,3 +671,48 @@ def tenant_post_data(to_session: TOSession, request_template_data: list[JSONData
 	except IndexError:
 		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]:
+	"""
+	PyTest Fixture to create POST data for server_capabilities endpoint.
+
+	:param to_session: Fixture to get Traffic Ops session.
+	:param request_template_data: Fixture to get server_capabilities data from a prerequisites file.
+	:returns: Sample POST data and the actual API response.
+	"""
+
+	try:
+		server_capabilities = request_template_data[0]
+	except IndexError as e:
+		raise TypeError(
+			"malformed prerequisite data; no data present in 'server_capabilities' array property") from e
+
+	if not isinstance(server_capabilities, dict):
+		raise TypeError(
+			f"malformed prerequisite data; data must be objects, not '{type(server_capabilities)}'")
+
+	# Return new post data and post response from server_capabilities POST request
+	randstr = str(randint(0, 1000))
+	try:
+		name = server_capabilities["name"]
+		if not isinstance(name, str):
+			raise TypeError(f"name must be str, not '{type(name)}'")
+		server_capabilities["name"] = name[:3] + randstr
+	except KeyError as e:
+		raise TypeError(f"missing server_capabilities property '{e.args[0]}'") from e
+
+	logger.info("New server_capabilities data to hit POST method %s", request_template_data)
+	# Hitting server_capabilities POST method
+	response: tuple[
+		JSONData, requests.Response] = to_session.create_server_capabilities(data=server_capabilities)
+	try:
+		resp_obj = response[0]
+		if not isinstance(resp_obj, dict):
+			raise TypeError("malformed API response; server_capabilities is not an object")
+		return resp_obj
+	except IndexError:
+		logger.error("No server_capabilities response data from server_capabilities 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 a7e2638b96..0404cd3704 100644
--- a/traffic_ops/testing/api_contract/v4/request_template.json
+++ b/traffic_ops/testing/api_contract/v4/request_template.json
@@ -50,5 +50,10 @@
 			"name": "test",
 			"parentId": 1
 		}
+	],
+	"server_capabilities": [
+		{
+			"name": "RAM"
+		}
 	]
 }
diff --git a/traffic_ops/testing/api_contract/v4/requirements.txt b/traffic_ops/testing/api_contract/v4/requirements.txt
index 81b9257263..d08099f871 100644
--- a/traffic_ops/testing/api_contract/v4/requirements.txt
+++ b/traffic_ops/testing/api_contract/v4/requirements.txt
@@ -12,5 +12,4 @@
 # limitations under the License.
 #
 
-Apache-TrafficControl==3.1.0
 pytest==7.2.1
diff --git a/traffic_ops/testing/api_contract/v4/response_template.json b/traffic_ops/testing/api_contract/v4/response_template.json
index 46c4ecebf0..1b63bce1d4 100644
--- a/traffic_ops/testing/api_contract/v4/response_template.json
+++ b/traffic_ops/testing/api_contract/v4/response_template.json
@@ -172,5 +172,16 @@
                 "type": "str"
             }
         }
+    },
+    "server_capabilities": {
+        "type": "object",
+        "properties": {
+            "lastUpdated": {
+                "type": "str"
+            },
+            "name": {
+                "type": "str"
+            }
+        }
     }
 }
diff --git a/traffic_ops/testing/api_contract/v4/test_server_capabilities.py b/traffic_ops/testing/api_contract/v4/test_server_capabilities.py
new file mode 100644
index 0000000000..fb5723d2a4
--- /dev/null
+++ b/traffic_ops/testing/api_contract/v4/test_server_capabilities.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 server_capabilities 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', ["server_capabilities"], indirect=True)
+def test_server_capabilities_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]],
+	server_capabilities_post_data: dict[str, object]
+) -> None:
+	"""
+	Test step to validate keys, values and data types from server_capabilities 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 server_capabilities_post_data: Fixture to get sample server_capabilities data 
+    and actual server_capabilities response.
+	"""
+	# validate server_capabilities keys from server_capabilities get response
+	logger.info("Accessing /server_capabilities endpoint through Traffic ops session.")
+
+	server_capabilities = request_template_data[0]
+	if not isinstance(server_capabilities, dict):
+		raise TypeError("malformed server_capabilities in prerequisite data; not an object")
+
+	server_capabilities_name = server_capabilities.get("name")
+	if not isinstance(server_capabilities_name, str):
+		raise TypeError("malformed server_capabilities in prerequisite data; 'name' not a string")
+
+	server_capabilities_get_response: tuple[
+		dict[str, object] | list[dict[str, object] | list[object] | primitive] | primitive,
+		requests.Response
+	] = to_session.get_server_capabilities(query_params={"name": server_capabilities_name})
+	try:
+		server_capabilities_data = server_capabilities_get_response[0]
+		if not isinstance(server_capabilities_data, list):
+			raise TypeError("malformed API response; 'response' property not an array")
+
+		first_server_capabilities = server_capabilities_data[0]
+		if not isinstance(first_server_capabilities, dict):
+			raise TypeError("malformed API response; first server_capabilities in response is not an object")
+		server_capabilities_keys = set(first_server_capabilities.keys())
+
+		logger.info("server_capabilities Keys from endpoint response %s", server_capabilities_keys)
+		server_capabilities_response_template = response_template_data.get("server_capabilities")
+		if not isinstance(server_capabilities_response_template, dict):
+			raise TypeError(
+				f"server_capabilities data must be a dict, not '{type(server_capabilities_response_template)}'")
+		response_template: dict[str, list[dict[str, object] | list[object] | primitive] |\
+			dict[object, object] |\
+			primitive
+		]
+		response_template = server_capabilities_response_template.get("properties")
+		# validate server_capabilities values from prereq data in api get response.
+		prereq_values = [server_capabilities_post_data["name"]]
+		get_values = [first_server_capabilities["name"]]
+		get_types = {}
+		for key, value in first_server_capabilities.items():
+			get_types[key] = type(value).__name__
+		logger.info("types from server_capabilities 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 server_capabilities response template %s", response_template_types)
+		# validate keys, data types and values from server_capabilities get json response.
+		assert server_capabilities_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 server_capabilities: API response was malformed")
+	finally:
+		# Delete server_capabilities after test execution to avoid redundancy.
+		try:
+			server_capability_name = server_capabilities_post_data["name"]
+			to_session.delete_server_capabilities(query_params={"name": server_capability_name})
+		except IndexError:
+			logger.error("server_capabilities returned by Traffic Ops is missing an 'id' property")
+			pytest.fail("Response from delete request is empty, Failing test_server_capabilities_contract")