From 4f917116601f229b57a440d944c586daf4a7c95c Mon Sep 17 00:00:00 2001 From: Kash Blanc <100367078+Kashatlast2@users.noreply.github.com> Date: Wed, 26 Jul 2023 14:58:11 -0400 Subject: [PATCH] Added api contract profile parameters test case (#7604) * added profile parameters test case * Refactored coordinates to match updated merge in conftest.py * Added get request for profile and parameters in conftest.py * Update request_template.json for profileId and parameterId * Added lastUpdated to response_template.json * Fixed assertion test_profile_parameters.py * Fixed indentation on line 1685 conftest.py * removed unnecessary profile_id var conftest.py * removed unnecessary declaration for parameter_Id conftest.py * fixed indentation conftest.py * Fixing inconsistent tabbing indentation conftest.py --------- Co-authored-by: yblanc545 --- .../clients/python/trafficops/tosession.py | 9 ++ .../testing/api_contract/v4/conftest.py | 36 ++++++++ .../v4/data/request_template.json | 10 +++ .../v4/data/response_template.json | 21 +++++ .../v4/test_profile_parameters.py | 90 +++++++++++++++++++ 5 files changed, 166 insertions(+) create mode 100644 traffic_ops/testing/api_contract/v4/test_profile_parameters.py diff --git a/traffic_control/clients/python/trafficops/tosession.py b/traffic_control/clients/python/trafficops/tosession.py index 11cd785f0b..083b6480ae 100644 --- a/traffic_control/clients/python/trafficops/tosession.py +++ b/traffic_control/clients/python/trafficops/tosession.py @@ -1645,6 +1645,15 @@ def delete_profile_by_id(self, profile_id=None): # # Profile Parameters # + @api_request('get', 'profileparameters', ('3.0', '4.0', '4.1', '5.0')) + def get_profile_parameters(self, data=None): + """ + Retrieve all Parameter/Profile assignments. + :ref:`to-api-profileparameters` + :rtype: Tuple[Union[Dict[str, Any], List[Dict[str, Any]]], requests.Response] + :raises: Union[LoginError, OperationError] + """ + @api_request('post', 'profileparameters', ('3.0', '4.0', '4.1', '5.0')) def associate_paramater_to_profile(self, data=None): """ diff --git a/traffic_ops/testing/api_contract/v4/conftest.py b/traffic_ops/testing/api_contract/v4/conftest.py index 74849b5ff1..9a073dd5aa 100644 --- a/traffic_ops/testing/api_contract/v4/conftest.py +++ b/traffic_ops/testing/api_contract/v4/conftest.py @@ -1643,3 +1643,39 @@ def delivery_services_regex_data_post(to_session: TOSession, request_template_da if msg is None: logger.error("delivery_services_regex returned by Traffic Ops is missing an 'id' property") pytest.fail("Response from delete request is empty, Failing test_case") + + +@pytest.fixture(name="profile_parameters_post_data") +def profile_parameters_post_data(to_session: TOSession, request_template_data: list[JSONData], + profile_post_data:dict[str, object], parameter_post_data:dict[str, object] + ) -> dict[str, object]: + """ + PyTest Fixture to create POST data for profile parameters endpoint. + :param to_session: Fixture to get Traffic Ops session. + :param request_template_data: Fixture to get profile parameters request template from a prerequisites file. + :returns: Sample POST data and the actual API response. + """ + + profile_parameters = check_template_data(request_template_data["profile_parameters"], "profile_parameters") + + # Return new post data and post response from profile parameters POST request + profile_get_response = to_session.get_profiles() + profile_data = profile_get_response [0][0] + profile_id = profile_data.get("id") + + profile_parameters["profileId"] = profile_post_data["id"] + profile_parameters["parameterId"] = parameter_post_data["id"] + + logger.info("New profile_parameter data to hit POST method %s", profile_parameters) + + # Hitting profile parameters POST method + response: tuple[JSONData, requests.Response] = to_session.associate_paramater_to_profile(profile_id=profile_id, data=profile_parameters) + resp_obj = check_template_data(response, "profile_parameters") + yield resp_obj + profile_id = resp_obj.get("profileId") + parameter_id = resp_obj.get("parameterId") + msg = to_session.delete_profile_parameter_association_by_id(profile_id=profile_id, parameter_id=parameter_id) + logger.info("Deleting Profile Parameters data... %s", msg) + if msg is None: + logger.error("Profile Parameter returned by Traffic Ops is missing a 'profile_id' property") + pytest.fail("Response from delete request is empty, Failing test_case") diff --git a/traffic_ops/testing/api_contract/v4/data/request_template.json b/traffic_ops/testing/api_contract/v4/data/request_template.json index 3ad04676bc..98c054a361 100644 --- a/traffic_ops/testing/api_contract/v4/data/request_template.json +++ b/traffic_ops/testing/api_contract/v4/data/request_template.json @@ -395,5 +395,15 @@ "type": 33, "setNumber": 1 } + ], + "profile_parameters": [ + { + "profileId": 18, + "parameterId": 2 + }, + { + "profileId": 18, + "parameterId": 3 + } ] } 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 84f1fca2a6..e66a309537 100644 --- a/traffic_ops/testing/api_contract/v4/data/response_template.json +++ b/traffic_ops/testing/api_contract/v4/data/response_template.json @@ -1680,6 +1680,27 @@ }, "pattern": { "type": "string" + + } + } + }, + "profile_parameters": { + "type": "object", + "required":[ + "lastUpdated", + "profile", + "parameter" + ], + "properties":{ + "lastUpdated":{ + "type": "string" + }, + "profile":{ + "type": "string" + + }, + "parameter":{ + "type": "integer" } } } diff --git a/traffic_ops/testing/api_contract/v4/test_profile_parameters.py b/traffic_ops/testing/api_contract/v4/test_profile_parameters.py new file mode 100644 index 0000000000..a950a6f287 --- /dev/null +++ b/traffic_ops/testing/api_contract/v4/test_profile_parameters.py @@ -0,0 +1,90 @@ +# +# 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 Profile Parameters 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_profile_parameters_contract( + to_session: TOSession, + response_template_data: dict[str, Union[Primitive, list[Union[Primitive, dict[str, object], + list[object]]], dict[object, object]]], profile_parameters_post_data: dict[str, object]) -> None: + """ + Test step to validate keys, values and data types from profile parameters 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 profile_parameter_post_data: Fixture to get sample profile parameter data + and actual Profile Parameter response. + """ + # validate Profile Parameter keys from profile parameters get response + logger.info( + "Accessing /profile parameters endpoint through Traffic ops session.") + + profile_id = profile_parameters_post_data.get("profileId") + if not isinstance(profile_id, int): + raise TypeError( + "malformed profile parameters in prerequisite data; 'profileId' not an integer") + + parameter_id = profile_parameters_post_data.get("parameterId") + if not isinstance(parameter_id, int): + raise TypeError( + "malformed profile parameters in prerequisite data; 'parameterId' not an integer") + + profile_parameter_get_response: tuple[ + Union[dict[str, object], + list[Union[dict[str, object], list[object], Primitive]], Primitive], + requests.Response + ] = to_session.get_profile_parameters(profile_id=profile_id, query_params={"profile": profile_id}) + try: + profile_parameter_data = profile_parameter_get_response[0] + if not isinstance(profile_parameter_data, list): + raise TypeError("malformed API response; 'response' property not an array") + + first_profile_parameter = profile_parameter_data[0] + if not isinstance(first_profile_parameter, dict): + raise TypeError( + "malformed API response; first Profile Parameter in response is not an object") + logger.info("Profile Parameter Api get response %s", first_profile_parameter) + profile_parameter_response_template = response_template_data.get("profile_parameters") + if not isinstance(profile_parameter_response_template, dict): + raise TypeError( + f"Profile Parameter response template data must be a dict, not '{type(profile_parameter_response_template)}'") + + profile_parameters_post_data["profile"] = first_profile_parameter["profile"] + profile_parameters_post_data["parameter"] = first_profile_parameter["parameter"] + + # validate profile_parameter values from prereq data in profile parameters get response. + keys = ["profile", "parameter"] + prereq_values = [profile_parameters_post_data[key] for key in keys] + get_values = [first_profile_parameter[key] for key in keys] + + assert validate(instance=first_profile_parameter, + schema=profile_parameter_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 profile_parameter endpoint: API response was malformed")