Skip to content

Commit

Permalink
adding mock util (#176)
Browse files Browse the repository at this point in the history
  • Loading branch information
John-Scira authored Dec 16, 2024
1 parent 83dff1b commit ff58fea
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 3 deletions.
25 changes: 25 additions & 0 deletions mock_output.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
responses:
- response:
auto_calculate_content_length: false
body: '{"catalog":false,"workspaceSubmissionStats":{"runningSubmissionsCount":0},"accessLevel":"OWNER","owners":["[email protected]"],"workspace":{"attributes":{"description":""},"authorizationDomain":[],"billingAccount":"billingAccounts/006351-1F5067-BF7B50","bucketName":"fc-912e0ea1-de65-4c99-93c0-2b914063ba22","cloudPlatform":"Gcp","createdBy":"[email protected]","createdDate":"2024-04-26T17:18:03.425Z","googleProject":"terra-e5e03853","googleProjectNumber":"873795997732","isLocked":false,"lastModified":"2024-10-29T20:28:12.501Z","name":"js_test","namespace":"dsp-ops-gp-development-terra","state":"Ready","workflowCollectionName":"912e0ea1-de65-4c99-93c0-2b914063ba22","workspaceId":"912e0ea1-de65-4c99-93c0-2b914063ba22","workspaceType":"rawls","workspaceVersion":"v2"},"canShare":true,"bucketOptions":{"location":"US-CENTRAL1","requesterPays":false},"canCompute":true,"policies":[]}'
content_type: text/plain
headers:
Access-Control-Allow-Headers: authorization,content-type,accept,origin,x-app-id
Access-Control-Allow-Methods: GET,POST,PUT,PATCH,DELETE,OPTIONS,HEAD
Access-Control-Allow-Origin: '*'
Access-Control-Max-Age: '1728000'
Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000, h3=":443"; ma=2592000,h3-29=":443";
ma=2592000
Cache-control: no-store
Pragma: no-cache
Referrer-Policy: strict-origin-when-cross-origin
Strict-Transport-Security: max-age=31536000; includeSubDomains
Transfer-Encoding: chunked
Vary: Accept-Encoding
Via: 1.1 google, 1.1 google
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
method: GET
status: 200
url: https://api.firecloud.org/api/workspaces/dsp-ops-gp-development-terra/js_test
3 changes: 2 additions & 1 deletion python/tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pytest
responses
responses
gitpython
77 changes: 77 additions & 0 deletions python/tests/utils/mock_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@

import functools
import inspect
from pathlib import Path
import responses
import responses._recorder
import git


def make_filename(func):
module = inspect.getmodule(func)

file_path = Path(module.__file__)
repo = git.Repo(file_path, search_parent_directories=True)
git_root = repo.git.rev_parse("--show-toplevel")
return Path(git_root).joinpath("mock_output.yaml")


def activate_responses():
def outer_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
with responses.RequestsMock() as rsp:
rsp._add_from_file(file_path=make_filename(func))
return func(*args, **kwargs)

return wrapper

return outer_decorator


def activate_recorder():
def outer_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
recorder = responses._recorder.Recorder()
with recorder:
try:
result = func(*args, **kwargs)
finally:
recorder.dump_to_file(
file_path=make_filename(func),
registered=recorder.get_registry().registered,
)
return result

return wrapper

return outer_decorator


def mock_responses(activate=False, update_results=False):
"""Decorator to record then mock requests made with the requests module.
When update_results is True, will store requests to a yaml file. When it
is false, it will retrieve the results, allowing to run tests offline.
Usage:
import requests
from python.tests.utils.mock_responses import mock_responses
class MyTestCase(TestCase):
@mock_responses(update_results=settings.TESTS_UPDATE_STORED_RESULTS)
def test_mytest(self):
request.get("https://example.com)
...
"""
def conditional_decorator(func):
if activate:
if update_results:
return activate_recorder()
else:
return activate_responses()
else:
return func
return conditional_decorator
11 changes: 9 additions & 2 deletions python/utils/request_util.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from typing import Any, Optional
from python.tests.utils.mock_util import mock_responses
import requests
import backoff


GET = "GET"
POST = "POST"
DELETE = "DELETE"
Expand All @@ -10,18 +12,21 @@


class RunRequest:
def __init__(self, token: Any, max_retries: int = 5, max_backoff_time: int = 5 * 60):
def __init__(self, token: Any, max_retries: int = 5, max_backoff_time: int = 5 * 60, create_mocks: bool = False):
"""
Initialize the RunRequest class.
Args:
token (Any): The token used for authentication.
max_retries (int, optional): The maximum number of retries for a request. Defaults to 5.
max_backoff_time (int, optional): The maximum backoff time in seconds. Defaults to 5 * 60.
create_mocks (bool, optional): Used to capture responses for use with unit tests,
outputs to a yaml file. Defaults to False.
"""
self.max_retries = max_retries
self.max_backoff_time = max_backoff_time
self.token = token
self.create_mocks = create_mocks

@staticmethod
def _create_backoff_decorator(max_tries: int, factor: int, max_time: int) -> Any:
Expand Down Expand Up @@ -69,14 +74,16 @@ def run_request(
Returns:
requests.Response: The response from the request.
"""

# Create a custom backoff decorator with the provided parameters
backoff_decorator = self._create_backoff_decorator(
max_tries=self.max_retries,
factor=factor,
max_time=self.max_backoff_time
)

# Apply the backoff decorator to the actual request execution
# Apply decorators to request execution
@mock_responses(activate=self.create_mocks, update_results=True)
@backoff_decorator
def _make_request() -> requests.Response:
if method == GET:
Expand Down

0 comments on commit ff58fea

Please sign in to comment.