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

[trafficcontrol] branch master updated: Added Api contract tests for statuses endpoint (#7535)

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

ocket8888 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 4eb7a2a927 Added Api contract tests for statuses endpoint (#7535)
4eb7a2a927 is described below

commit 4eb7a2a9276b9ee56289a92cd8219742b18176e1
Author: Kashatlast2 <10...@users.noreply.github.com>
AuthorDate: Thu May 25 20:10:52 2023 -0400

    Added Api contract tests for statuses endpoint (#7535)
    
    Created statuses test
    
    Co-authored-by: yblanc545 <Yv...@comcast.com>
---
 .../clients/python/trafficops/tosession.py         | 22 ++++++
 traffic_ops/testing/api_contract/v4/conftest.py    | 28 ++++++++
 .../api_contract/v4/data/response_template.json    | 25 ++++++-
 .../testing/api_contract/v4/test_statuses.py       | 84 ++++++++++++++++++++++
 4 files changed, 158 insertions(+), 1 deletion(-)

diff --git a/traffic_control/clients/python/trafficops/tosession.py b/traffic_control/clients/python/trafficops/tosession.py
index 19ed6d8558..215d57a305 100644
--- a/traffic_control/clients/python/trafficops/tosession.py
+++ b/traffic_control/clients/python/trafficops/tosession.py
@@ -1846,6 +1846,28 @@ class TOSession(RestApiSession):
 		:raises: Union[LoginError, OperationError]
 		"""
 
+	@api_request('post', 'statuses', ('3.0', '3.1', '4.0', '4.1', '5.0'))
+	def create_statuses(self, query_params=None):
+		"""
+		Create server status code.
+		:ref:`to-api-statuses`
+		:param data: A new status code created.
+		:type data: Dict[str, Any]
+		:rtype: Tuple[Union[Dict[str, Any], List[Dict[str, Any]]], requests.Response]
+		:raises: Union[LoginError, OperationError]
+		"""
+
+	@api_request('delete', 'statuses/{status_id:d}', ('3.0', '3.1', '4.0', '4.1', '5.0'))
+	def delete_status_by_id(self, status_id=None):
+		"""
+		Delete a status
+		:ref:`to-api-status-id`
+		:param status_id: The status to delete
+		:type status_id: int
+		:rtype: Tuple[Union[Dict[str, Any], List[Dict[str, Any]]], requests.Response]
+		:raises: Union[LoginError, OperationError]
+		"""
+
 	#
 	# System
 	#
diff --git a/traffic_ops/testing/api_contract/v4/conftest.py b/traffic_ops/testing/api_contract/v4/conftest.py
index a44520e935..4e61af218c 100644
--- a/traffic_ops/testing/api_contract/v4/conftest.py
+++ b/traffic_ops/testing/api_contract/v4/conftest.py
@@ -844,3 +844,31 @@ def server_post_data(to_session: TOSession, request_template_data: list[JSONData
 	response: tuple[JSONData, requests.Response] = to_session.create_server(data=server)
 	resp_obj = check_template_data(response, "server")
 	return resp_obj
+
+@pytest.fixture()
+def status_post_data(to_session: TOSession, request_template_data: list[JSONData]
+		  ) -> dict[str, object]:
+	"""
+	PyTest Fixture to create POST data for statuses endpoint.
+
+	:param to_session: Fixture to get Traffic Ops session.
+	:param request_template_data: Fixture to get Status request template data from a prerequisite file.
+	:returns: Sample POST data and the actual API response.
+	"""
+	status = check_template_data(request_template_data["status"], "status")
+
+	# Return new post data and post response from statuses POST request
+	randstr = str(randint(0, 1000))
+	try:
+		name = status["name"]
+		if not isinstance(name, str):
+			raise TypeError(f"name must be str, not '{type(name)}'")
+		status["name"] = name[:4] + randstr
+	except KeyError as e:
+		raise TypeError(f"missing Status property '{e.args[0]}'") from e
+
+	logger.info("New status data to hit POST method %s", status)
+	# Hitting statuses POST methed
+	response: tuple[JSONData, requests.Response] = to_session.create_statuses(data=status)
+	resp_obj = check_template_data(response, "statuses")
+	return resp_obj
diff --git a/traffic_ops/testing/api_contract/v4/data/response_template.json b/traffic_ops/testing/api_contract/v4/data/response_template.json
index 50f5abe26d..ecc4440235 100644
--- a/traffic_ops/testing/api_contract/v4/data/response_template.json
+++ b/traffic_ops/testing/api_contract/v4/data/response_template.json
@@ -588,5 +588,28 @@
                 "type": "string"
             }
         }
+    },
+    "statuses": {
+        "type": "object",
+        "required": [
+            "description",
+            "name",
+            "id",
+            "lastUpdated"
+        ],
+        "properties": {
+            "description": {
+                "type": "string"
+            },
+            "name": {
+                "type": "string"
+            },
+            "id": {
+                "type": "integer"
+            },
+            "lastUpdated": {
+                "type": "string"
+            }
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/traffic_ops/testing/api_contract/v4/test_statuses.py b/traffic_ops/testing/api_contract/v4/test_statuses.py
new file mode 100644
index 0000000000..d12d0586f8
--- /dev/null
+++ b/traffic_ops/testing/api_contract/v4/test_statuses.py
@@ -0,0 +1,84 @@
+#
+# 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 statuses endpoint."""
+import logging
+from typing import Union
+
+import pytest
+import requests
+from jsonschema import validate
+
+from trafficops.tosession import TOSession
+
+# Create and configure logger
+logger = logging.getLogger()
+
+Primitive = Union[bool, int, float, str, None]
+
+
+def test_status_contract(
+	to_session: TOSession,
+	response_template_data: dict[str, Union[Primitive, list[Union[Primitive, dict[str, object],
+	list[object]]], dict[object, object]]], status_post_data: dict[str, object]) -> None:
+	"""
+	Test step to validate keys, values and data types from statuses endpoint
+	response.
+	:param to_session: Fixture to get Traffic Ops session.
+	:param response_template_data: Fixture to get response template data from a prerequisites file.
+	:param status_post_data: Fixture to get sample Status data and actual Status response.
+	"""
+	# validate Status keys from statuses get response
+	logger.info("Accessing /statuses endpoint through Traffic ops session.")
+
+	status_name = status_post_data.get("name")
+	if not isinstance(status_name, str):
+		raise TypeError("malformed status in prerequisite data; 'name' not a string")
+
+	status_get_response: tuple[
+		Union[dict[str, object], list[Union[dict[str, object], list[object], Primitive]], Primitive],
+		requests.Response
+	] = to_session.get_statuses(query_params={"name": status_name})
+	try:
+		status_data = status_get_response[0]
+		if not isinstance(status_data, list):
+			raise TypeError("malformed API response; 'response' property not an array")
+
+		first_status = status_data[0]
+		if not isinstance(first_status, dict):
+			raise TypeError("malformed API response; first Status in response is not an object")
+		logger.info("Status Api get response %s", first_status)
+		status_response_template = response_template_data.get("statuses")
+		if not isinstance(status_response_template, dict):
+			raise TypeError(
+				f"Status response template data must be a dict, not '{type(status_response_template)}'")
+
+		# validate status values from prereq data in statuses get response.
+		keys = ["name", "description"]
+		prereq_values = [status_post_data[key] for key in keys]
+		get_values = [first_status[key] for key in keys]
+
+		assert validate(instance=first_status, schema=status_response_template) is None
+		assert get_values == prereq_values
+	except IndexError:
+		logger.error("Either prerequisite data or API response was malformed")
+		pytest.fail("API contract test failed for status endpoint: API response was malformed")
+	finally:
+		# Delete Status after test execution to avoid redundancy.
+		try:
+			status_id = status_post_data["id"]
+			to_session.delete_status_by_id(status_id=status_id)
+		except IndexError:
+			logger.error("Status returned by Traffic Ops is missing an 'id' property")
+			pytest.fail("Response from delete request is empty, Failing test_get_status")