bot tests: Allow raw responses in fixtures.

Previously, the responses set in bot test fixtures
where handled as JSON objects. This works fine for
most bot tests, because most of the APIs that bots
are calling return a JSON-formatted response object.
However, some, like Trello, do return raw data.
This hasn't been noticed so far, because the respective
Trello test needed internet access. Tests shouldn't
need internet access.
This commit makes that Trello test use a fixture. To
work properly, it also adds a way to make http_mock_config
parse the response object as raw data.
This can now be done by modifying the "is_raw_response"
property in a newly introduced "meta" object that can
be used to specify how a fixture should be handled.
This commit is contained in:
Robert Hönig 2018-06-04 18:22:22 +02:00 committed by Tim Abbott
parent e638cdd4a9
commit 60c3919deb
3 changed files with 37 additions and 12 deletions

View file

@ -0,0 +1,15 @@
{
"meta": {
"is_raw_response": true
},
"request": {
"api_url": "https://api.trello.com/1/members/TEST/",
"params": {
"key": "TEST",
"token": "TEST"
}
},
"response": "invalid key",
"response-headers": {
}
}

View file

@ -27,6 +27,7 @@ class TestTrelloBot(BotTestCase):
def test_bot_quit_with_invalid_config(self) -> None: def test_bot_quit_with_invalid_config(self) -> None:
with self.mock_config_info(mock_config), self.assertRaises(StubBotHandler.BotQuitException): with self.mock_config_info(mock_config), self.assertRaises(StubBotHandler.BotQuitException):
with self.mock_http_conversation('invalid_key'):
TrelloHandler().initialize(StubBotHandler()) TrelloHandler().initialize(StubBotHandler())
def test_invalid_command(self) -> None: def test_invalid_command(self) -> None:

View file

@ -18,18 +18,21 @@ def mock_http_conversation(http_data):
http_data should be fixtures data formatted like the data http_data should be fixtures data formatted like the data
in zulip_bots/zulip_bots/bots/giphy/fixtures/test_normal.json in zulip_bots/zulip_bots/bots/giphy/fixtures/test_normal.json
""" """
def get_response(http_response, http_headers): def get_response(http_response, http_headers, is_raw_response):
# type: (Dict[str, Any], Dict[str, Any]) -> Any # type: (Dict[str, Any], Dict[str, Any], bool) -> Any
"""Creates a fake `requests` Response with a desired HTTP response and """Creates a fake `requests` Response with a desired HTTP response and
response headers. response headers.
""" """
mock_result = requests.Response() mock_result = requests.Response()
mock_result._content = json.dumps(http_response).encode() # type: ignore # This modifies a "hidden" attribute. if is_raw_response:
mock_result._content = http_response.encode() # type: ignore # This modifies a "hidden" attribute.
else:
mock_result._content = json.dumps(http_response).encode() # type: ignore # See above.
mock_result.status_code = http_headers.get('status', 200) mock_result.status_code = http_headers.get('status', 200)
return mock_result return mock_result
def assert_called_with_fields(mock_result, http_request, fields): def assert_called_with_fields(mock_result, http_request, fields, meta):
# type: (Any, Dict[str, Any], List[str]) -> None # type: (Any, Dict[str, Any], List[str], Dict[str, Any]) -> None
"""Calls `assert_called_with` on a mock object using an HTTP request. """Calls `assert_called_with` on a mock object using an HTTP request.
Uses `fields` to determine which keys to look for in HTTP request and Uses `fields` to determine which keys to look for in HTTP request and
to test; if a key is in `fields`, e.g., 'headers', it will be used in to test; if a key is in `fields`, e.g., 'headers', it will be used in
@ -51,34 +54,40 @@ def mock_http_conversation(http_data):
print("ERROR: Failed to find 'request', 'response' or 'response-headers' fields in fixture") print("ERROR: Failed to find 'request', 'response' or 'response-headers' fields in fixture")
raise raise
meta = http_data.get('meta', dict())
is_raw_response = meta.get('is_raw_response', False)
http_method = http_request.get('method', 'GET') http_method = http_request.get('method', 'GET')
if http_method == 'GET': if http_method == 'GET':
with patch('requests.get') as mock_get: with patch('requests.get') as mock_get:
mock_get.return_value = get_response(http_response, http_headers) mock_get.return_value = get_response(http_response, http_headers, is_raw_response)
yield yield
assert_called_with_fields( assert_called_with_fields(
mock_get, mock_get,
http_request, http_request,
['params', 'headers'] ['params', 'headers'],
meta
) )
elif http_method == 'PATCH': elif http_method == 'PATCH':
with patch('requests.patch') as mock_patch: with patch('requests.patch') as mock_patch:
mock_patch.return_value = get_response(http_response, http_headers) mock_patch.return_value = get_response(http_response, http_headers, is_raw_response)
yield yield
assert_called_with_fields( assert_called_with_fields(
mock_patch, mock_patch,
http_request, http_request,
['params', 'headers', 'json', 'data'] ['params', 'headers', 'json', 'data'],
meta
) )
else: else:
with patch('requests.post') as mock_post: with patch('requests.post') as mock_post:
mock_post.return_value = get_response(http_response, http_headers) mock_post.return_value = get_response(http_response, http_headers, is_raw_response)
yield yield
assert_called_with_fields( assert_called_with_fields(
mock_post, mock_post,
http_request, http_request,
['params', 'headers', 'json', 'data'] ['params', 'headers', 'json', 'data'],
meta
) )
@contextmanager @contextmanager