Skip to content

Commit

Permalink
Add test, modify mock method returns
Browse files Browse the repository at this point in the history
This commit adds a negative test for the timeout on exporting a
manifest. It also modifies the return values of the mocked HTTP verb
methods to reuse the same RhsmApiStub object throughout each execution,
rather than returning a new RhsmApiStub object with each method call. It
also modifies the manifester class and helper methods to support this
change.
  • Loading branch information
synkd committed Nov 21, 2023
1 parent d7c4861 commit 6f34d36
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 24 deletions.
4 changes: 2 additions & 2 deletions manifester/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from logzero import logger


def simple_retry(cmd, cmd_args=None, cmd_kwargs=None, max_timeout=240, _cur_timeout=1):
def simple_retry(cmd, cmd_args=None, cmd_kwargs=None, max_timeout=2, _cur_timeout=1):
"""Re(Try) a function given its args and kwargs up until a max timeout"""

cmd_args = cmd_args if cmd_args else []
Expand All @@ -19,7 +19,7 @@ def simple_retry(cmd, cmd_args=None, cmd_kwargs=None, max_timeout=240, _cur_time
if response.status_code in [429, 500, 504]:
new_wait = _cur_timeout * 2
if new_wait > max_timeout:
raise Exception("Timeout exceeded")
raise Exception("Retry timeout exceeded")
logger.debug(f"Trying again in {_cur_timeout} seconds")
time.sleep(_cur_timeout)
response = simple_retry(cmd, cmd_args, cmd_kwargs, max_timeout, new_wait)
Expand Down
26 changes: 19 additions & 7 deletions manifester/manifester.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ def access_token(self):
cmd_args=[f"{self.token_request_url}"],
cmd_kwargs=token_request_data,
).json()
self._access_token = token_data["access_token"]
if self.is_mock:
self._access_token = token_data.access_token
else:
self._access_token = token_data["access_token"]
return self._access_token

@cached_property
Expand Down Expand Up @@ -131,6 +134,8 @@ def delete_subscription_allocation(self):
"proxies": self.manifest_data.get("proxies", settings.proxies),
"params": {"force": "true"},
}
if self.is_mock:
self.allocation_uuid = self.allocation_uuid.uuid
response = simple_retry(
self.requester.delete,
cmd_args=[f"{self.allocations_url}/{self.allocation_uuid}"],
Expand Down Expand Up @@ -241,11 +246,14 @@ def verify_allocation_entitlements(self, entitlement_quantity, subscription_name

def process_subscription_pools(self, subscription_pools, subscription_data):
logger.debug(f"Finding a matching pool for {subscription_data['name']}.")
matching = [
d
for d in subscription_pools["body"]
if d["subscriptionName"] == subscription_data["name"]
]
if self.is_mock:
matching = [d for d in subscription_pools.body if d["subscriptionName"] == subscription_data["name"]]
else:
matching = [
d
for d in subscription_pools["body"]
if d["subscriptionName"] == subscription_data["name"]
]
logger.debug(
f"The following pools are matches for this subscription: {matching}"
)
Expand Down Expand Up @@ -355,13 +363,17 @@ def trigger_manifest_export(self):
"Manifest export job status check limit exceeded. This may indicate an "
"upstream issue with Red Hat Subscription Management."
)
raise Exception("Export timeout exceeded")
break
request_count += 1
if limit_exceeded:
self.content = None
return self
export_job = export_job.json()
export_href = export_job["body"]["href"]
if self.is_mock:
export_href = export_job.body["href"]
else:
export_href = export_job["body"]["href"]
manifest = simple_retry(
self.requester.get,
cmd_args=[f"{export_href}"],
Expand Down
80 changes: 65 additions & 15 deletions tests/test_manifester.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from unittest.mock import Mock

from functools import cached_property
from requests import request
from manifester import Manifester
from manifester.settings import settings
Expand All @@ -23,60 +24,109 @@ def __init__(self, in_dict=None, **kwargs):
# self.status_code = kwargs.get("status_code") or fake_http_response_code(self._good_codes, self._bad_codes, self._fail_rate)
super().__init__(in_dict)

@property
@cached_property
def status_code(self):
return fake_http_response_code(self._good_codes, self._bad_codes, self._fail_rate)

def post(self, *args, **kwargs):
"""Simulate responses to POST requests for RHSM API endpoints used by Manifester"""

if args[0].endswith("openid-connect/token"):
return RhsmApiStub(in_dict={"access_token": "this is a simulated access token"})
self.access_token = "this is a simulated access token"
return self
if args[0].endswith("allocations"):
return RhsmApiStub(in_dict={"uuid": "1234567890"})
self.uuid = "1234567890"
return self
if args[0].endswith("entitlements"):
return RhsmApiStub(in_dict={"params": kwargs["params"]}, status_code=200)
self.params = kwargs["params"]
return self
# if args[0].endswith("openid-connect/token"):
# return RhsmApiStub(in_dict={"access_token": "this is a simulated access token"})
# if args[0].endswith("allocations"):
# return RhsmApiStub(in_dict={"uuid": "1234567890"})
# if args[0].endswith("entitlements"):
# return RhsmApiStub(in_dict={"params": kwargs["params"]}, status_code=200)

def get(self, *args, **kwargs):
""""Simulate responses to GET requests for RHSM API endpoints used by Manifester"""

if args[0].endswith("versions"):
return RhsmApiStub(in_dict={"valid_sat_versions": ["sat-6.12", "sat-6.13", "sat-6.14"]})
self.valid_sat_versions = ["sat-6.12", "sat-6.13", "sat-6.14"]
return self
if args[0].endswith("pools"):
# question: how to fake > 50 pools to test use of offset parameter?
return RhsmApiStub(in_dict={'body': [{'id': '987adf2a8977', 'subscriptionName': 'Red Hat Satellite Infrastructure Subscription', 'entitlementsAvailable': 13}]})
self.body = [{'id': '987adf2a8977', 'subscriptionName': 'Red Hat Satellite Infrastructure Subscription', 'entitlementsAvailable': 13}]
return self
if "allocations" in args[0] and not ("export" in args[0] or "pools" in args[0]):
return RhsmApiStub(in_dict={"allocation_data": "this allocation data also includes entitlement data"})
self.allocation_data = "this allocation data also includes entitlement data"
return self
if args[0].endswith("export"):
return RhsmApiStub(in_dict={'body': {'exportJobID': '123456', 'href': 'exportJob'}})
self.body = {'exportJobID': '123456', 'href': 'exportJob'}
return self
if "exportJob" in args[0]:
responses = [202, 200]
return RhsmApiStub(in_dict={'body': {'exportID': 27, 'href': 'https://example.com/export/98ef892ac11'}}, status_code=random.choice(responses))
del self.status_code
if self.force_export_failure:
self._good_codes = [202]
else:
self._good_codes = [202, 200]
self.body = {'exportID': 27, 'href': 'https://example.com/export/98ef892ac11'}
return self
if "export" in args[0] and not args[0].endswith("export"):
del self.status_code
self._good_codes = [200]
# Manifester expets a bytes-type object to be returned as the manifest
return RhsmApiStub(in_dict={"content": b"this is a simulated manifest"})
self.content = b"this is a simulated manifest"
return self
# if args[0].endswith("versions"):
# return RhsmApiStub(in_dict={"valid_sat_versions": ["sat-6.12", "sat-6.13", "sat-6.14"]})
# if args[0].endswith("pools"):
# # question: how to fake > 50 pools to test use of offset parameter?
# return RhsmApiStub(in_dict={'body': [{'id': '987adf2a8977', 'subscriptionName': 'Red Hat Satellite Infrastructure Subscription', 'entitlementsAvailable': 13}]})
# if "allocations" in args[0] and not ("export" in args[0] or "pools" in args[0]):
# return RhsmApiStub(in_dict={"allocation_data": "this allocation data also includes entitlement data"})
# if args[0].endswith("export"):
# return RhsmApiStub(in_dict={'body': {'exportJobID': '123456', 'href': 'exportJob'}})
# if "exportJob" in args[0]:
# responses = [202, 200]
# return RhsmApiStub(in_dict={'body': {'exportID': 27, 'href': 'https://example.com/export/98ef892ac11'}}, status_code=random.choice(responses))
# if "export" in args[0] and not args[0].endswith("export"):
# # Manifester expets a bytes-type object to be returned as the manifest
# return RhsmApiStub(in_dict={"content": b"this is a simulated manifest"})

def delete(self, *args, **kwargs):
"""Simulate responses to DELETE requests for RHSM API endpoints used by Manifester"""

if args[0].endswith("allocations/1234567890") and kwargs["params"]["force"] == "true":
return RhsmApiStub(in_dict={"content": b""}, good_codes=[204])
del self.status_code
self.content = b""
self._good_codes=[204]
return self
# if args[0].endswith("allocations/1234567890") and kwargs["params"]["force"] == "true":
# return RhsmApiStub(in_dict={"content": b""}, good_codes=[204])


def test_create_allocation():
"""Test that manifester's create_subscription_allocation method returns a UUID"""

manifester = Manifester(manifest_category="golden_ticket", requester=RhsmApiStub(in_dict=None))
allocation_uuid = manifester.create_subscription_allocation()
assert allocation_uuid == "1234567890"
assert allocation_uuid.uuid == "1234567890"

def test_negative_simple_retry_timeout():
"""Test that exceeding the attempt limit when retrying a failed API call results in an exception"""

# TODO: figure out why this test fails despite raising the expected exception
manifester = Manifester(manifest_category="golden_ticket", requester=RhsmApiStub(in_dict=None, fail_rate=100))
with pytest.raises(Exception) as exception:
manifester.create_subscription_allocation()
assert str(exception.value) == "Retry timeout exceeded"

def test_negative_manifest_export_timeout():
"""Test that exceeding the attempt limit when exporting a manifest results in an exception"""

manifester = Manifester(manifest_category="golden_ticket", requester=RhsmApiStub(in_dict={"force_export_failure": True}))
with pytest.raises(Exception) as exception:
manifester.get_manifest()
assert str(exception.value) == "Timeout exceeded"
assert str(exception.value) == "Export timeout exceeded"

def test_get_manifest():
"""Test that manifester's get_manifest method returns a manifest"""
Expand Down

0 comments on commit 6f34d36

Please sign in to comment.