You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by sh...@apache.org on 2023/04/14 16:56:55 UTC
[trafficcontrol] branch master updated: Added Api contract test case for profiles endpoint (#7444)
This is an automated email from the ASF dual-hosted git repository.
shamrick 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 4f99343a2f Added Api contract test case for profiles endpoint (#7444)
4f99343a2f is described below
commit 4f99343a2f2e831947d36f58ef2dcff543a55e8a
Author: Gokula Krishnan <67...@users.noreply.github.com>
AuthorDate: Fri Apr 14 22:26:49 2023 +0530
Added Api contract test case for profiles endpoint (#7444)
* Added Api contract test case for profiles endpoint
* Modifed code
* updated params
* Resolved merge conflicts
---------
Co-authored-by: Gokula krishnan <go...@comcast.com>
---
traffic_ops/testing/api_contract/v4/conftest.py | 60 +++++++++++
.../testing/api_contract/v4/request_template.json | 9 ++
.../testing/api_contract/v4/response_template.json | 29 ++++++
.../testing/api_contract/v4/test_profiles.py | 110 +++++++++++++++++++++
4 files changed, 208 insertions(+)
diff --git a/traffic_ops/testing/api_contract/v4/conftest.py b/traffic_ops/testing/api_contract/v4/conftest.py
index 500c8dda10..eb9a9c0d4c 100644
--- a/traffic_ops/testing/api_contract/v4/conftest.py
+++ b/traffic_ops/testing/api_contract/v4/conftest.py
@@ -568,3 +568,63 @@ def role_post_data(to_session: TOSession, request_template_data: list[JSONData]
except IndexError:
logger.error("No Role response data from roles POST request.")
sys.exit(1)
+
+
+@pytest.fixture()
+def profile_post_data(to_session: TOSession, request_template_data: list[JSONData]
+ ) -> dict[str, object]:
+ """
+ PyTest Fixture to create POST data for profile endpoint.
+
+ :param to_session: Fixture to get Traffic Ops session.
+ :param request_template_data: Fixture to get profile data from a prerequisites file.
+ :returns: Sample POST data and the actual API response.
+ """
+
+ try:
+ profile = request_template_data[0]
+ except IndexError as e:
+ raise TypeError(
+ "malformed prerequisite data; no Profile present in 'profile' array property") from e
+
+ if not isinstance(profile, dict):
+ raise TypeError(f"malformed prerequisite data; profile must be objects, not '{type(profile)}'")
+
+ # Return new post data and post response from cachegroups POST request
+ randstr = str(randint(0, 1000))
+ try:
+ name = profile["name"]
+ if not isinstance(name, str):
+ raise TypeError(f"name must be str, not '{type(name)}'")
+ profile["name"] = name[:4] + randstr
+ except KeyError as e:
+ raise TypeError(f"missing Profile property '{e.args[0]}'") from e
+ # Hitting types GET method to access typeID for profile POST data
+ cdn_get_response: tuple[
+ dict[str, object] | list[dict[str, object] | list[object] | primitive] | primitive,
+ requests.Response
+ ] = to_session.get_cdns()
+ try:
+ cdn_data = cdn_get_response[0]
+ if not isinstance(cdn_data, list):
+ raise TypeError("malformed API response; 'response' property not an array")
+ first_cdn = cdn_data[0]
+ if not isinstance(first_cdn, dict):
+ raise TypeError("malformed API response; first CDN in response is not an object")
+ profile["cdn"] = first_cdn["id"]
+ cdn_id = profile["cdn"]
+ logger.info("extracted %s from %s", cdn_id, cdn_get_response)
+ except KeyError as e:
+ raise TypeError(f"missing CDN property '{e.args[0]}'") from e
+
+ logger.info("New profile data to hit POST method %s", request_template_data)
+ # Hitting profile POST method
+ response: tuple[JSONData, requests.Response] = to_session.create_profile(data=profile)
+ try:
+ resp_obj = response[0]
+ if not isinstance(resp_obj, dict):
+ raise TypeError("malformed API response; profile is not an object")
+ return resp_obj
+ except IndexError:
+ logger.error("No Profile response data from cdns 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 e4456d86d4..6177d6cc06 100644
--- a/traffic_ops/testing/api_contract/v4/request_template.json
+++ b/traffic_ops/testing/api_contract/v4/request_template.json
@@ -34,5 +34,14 @@
"name": "test",
"description": "quest"
}
+ ],
+ "profiles": [
+ {
+ "name": "test",
+ "description": "A test profile for API examples",
+ "cdn": 2,
+ "type": "UNK_PROFILE",
+ "routingDisabled": true
+ }
]
}
diff --git a/traffic_ops/testing/api_contract/v4/response_template.json b/traffic_ops/testing/api_contract/v4/response_template.json
index 4b2fdd66e2..5130b47045 100644
--- a/traffic_ops/testing/api_contract/v4/response_template.json
+++ b/traffic_ops/testing/api_contract/v4/response_template.json
@@ -120,5 +120,34 @@
"type": "str"
}
}
+ },
+ "profiles": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "int"
+ },
+ "lastUpdated": {
+ "type": "str"
+ },
+ "name": {
+ "type": "str"
+ },
+ "description": {
+ "type": "str"
+ },
+ "cdnName": {
+ "type": "str"
+ },
+ "cdn": {
+ "type": "int"
+ },
+ "routingDisabled": {
+ "type": "bool"
+ },
+ "type": {
+ "type": "str"
+ }
+ }
}
}
diff --git a/traffic_ops/testing/api_contract/v4/test_profiles.py b/traffic_ops/testing/api_contract/v4/test_profiles.py
new file mode 100644
index 0000000000..17164ca3ae
--- /dev/null
+++ b/traffic_ops/testing/api_contract/v4/test_profiles.py
@@ -0,0 +1,110 @@
+#
+# 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 profiles 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', ['profiles'], indirect=True)
+def test_profile_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]],
+ profile_post_data: dict[str, object]
+) -> None:
+ """
+ Test step to validate keys, values and data types from profiles 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 profile_post_data: Fixture to get sample Profile data and actual Profile response.
+ """
+ # validate Profile keys from profiles get response
+ logger.info("Accessing /profiles endpoint through Traffic ops session.")
+
+ profile = request_template_data[0]
+ if not isinstance(profile, dict):
+ raise TypeError("malformed profile in prerequisite data; not an object")
+
+ profile_name = profile.get("name")
+ if not isinstance(profile_name, str):
+ raise TypeError("malformed profile in prerequisite data; 'name' not a string")
+
+ profile_get_response: tuple[
+ dict[str, object] | list[dict[str, object] | list[object] | primitive] | primitive,
+ requests.Response
+ ] = to_session.get_profiles(query_params={"name": profile_name})
+ try:
+ profile_data = profile_get_response[0]
+ if not isinstance(profile_data, list):
+ raise TypeError("malformed API response; 'response' property not an array")
+
+ first_profile = profile_data[0]
+ if not isinstance(first_profile, dict):
+ raise TypeError("malformed API response; first Profile in response is not an object")
+ profile_keys = set(first_profile.keys())
+
+ logger.info("Profile Keys from profiles endpoint response %s", profile_keys)
+ profile_response_template = response_template_data.get("profiles")
+ if not isinstance(profile_response_template, dict):
+ raise TypeError(
+ f"Profile response template data must be a dict, not '{type(profile_response_template)}'")
+ response_template: dict[str, list[dict[str, object] | list[object] | primitive] |\
+ dict[object, object] |\
+ primitive
+ ]
+ response_template = profile_response_template.get("properties")
+ # validate profile values from prereq data in profiles get response.
+ prereq_values = [
+ profile_post_data["name"],
+ profile_post_data["cdn"]
+ ]
+ get_values = [first_profile["name"], first_profile["cdn"]]
+ get_types = {}
+ for key, value in first_profile.items():
+ get_types[key] = type(value).__name__
+ logger.info("Types from profile 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 profile response template %s", response_template_types)
+
+ assert profile_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 cdn endpoint: API response was malformed")
+ finally:
+ # Delete Profile after test execution to avoid redundancy.
+ try:
+ profile_id = profile_post_data["id"]
+ to_session.delete_profile_by_id(profile_id=profile_id)
+ except IndexError:
+ logger.error("Profile returned by Traffic Ops is missing an 'id' property")
+ pytest.fail("Response from delete request is empty, Failing test_profile_contract")