From 60c3919debea5bdad0f1f44c9e52fc64f08894de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20H=C3=B6nig?= Date: Mon, 4 Jun 2018 18:22:22 +0200 Subject: [PATCH] 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. --- .../bots/trello/fixtures/invalid_key.json | 15 +++++++++ .../zulip_bots/bots/trello/test_trello.py | 3 +- zulip_bots/zulip_bots/request_test_lib.py | 31 ++++++++++++------- 3 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 zulip_bots/zulip_bots/bots/trello/fixtures/invalid_key.json diff --git a/zulip_bots/zulip_bots/bots/trello/fixtures/invalid_key.json b/zulip_bots/zulip_bots/bots/trello/fixtures/invalid_key.json new file mode 100644 index 0000000..ae06ce3 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/trello/fixtures/invalid_key.json @@ -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": { + } +} diff --git a/zulip_bots/zulip_bots/bots/trello/test_trello.py b/zulip_bots/zulip_bots/bots/trello/test_trello.py index b54656e..3df36cf 100644 --- a/zulip_bots/zulip_bots/bots/trello/test_trello.py +++ b/zulip_bots/zulip_bots/bots/trello/test_trello.py @@ -27,7 +27,8 @@ class TestTrelloBot(BotTestCase): def test_bot_quit_with_invalid_config(self) -> None: with self.mock_config_info(mock_config), self.assertRaises(StubBotHandler.BotQuitException): - TrelloHandler().initialize(StubBotHandler()) + with self.mock_http_conversation('invalid_key'): + TrelloHandler().initialize(StubBotHandler()) def test_invalid_command(self) -> None: with self.mock_config_info(mock_config), patch('requests.get'): diff --git a/zulip_bots/zulip_bots/request_test_lib.py b/zulip_bots/zulip_bots/request_test_lib.py index e8651f1..9eb9cee 100644 --- a/zulip_bots/zulip_bots/request_test_lib.py +++ b/zulip_bots/zulip_bots/request_test_lib.py @@ -18,18 +18,21 @@ def mock_http_conversation(http_data): http_data should be fixtures data formatted like the data in zulip_bots/zulip_bots/bots/giphy/fixtures/test_normal.json """ - def get_response(http_response, http_headers): - # type: (Dict[str, Any], Dict[str, Any]) -> Any + def get_response(http_response, http_headers, is_raw_response): + # type: (Dict[str, Any], Dict[str, Any], bool) -> Any """Creates a fake `requests` Response with a desired HTTP response and response headers. """ 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) return mock_result - def assert_called_with_fields(mock_result, http_request, fields): - # type: (Any, Dict[str, Any], List[str]) -> None + def assert_called_with_fields(mock_result, http_request, fields, meta): + # type: (Any, Dict[str, Any], List[str], Dict[str, Any]) -> None """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 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") raise + meta = http_data.get('meta', dict()) + is_raw_response = meta.get('is_raw_response', False) + http_method = http_request.get('method', 'GET') if http_method == '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 assert_called_with_fields( mock_get, http_request, - ['params', 'headers'] + ['params', 'headers'], + meta ) elif http_method == '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 assert_called_with_fields( mock_patch, http_request, - ['params', 'headers', 'json', 'data'] + ['params', 'headers', 'json', 'data'], + meta ) else: 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 assert_called_with_fields( mock_post, http_request, - ['params', 'headers', 'json', 'data'] + ['params', 'headers', 'json', 'data'], + meta ) @contextmanager