from textwrap import dedent from unittest.mock import MagicMock, patch import pytest import weechat from slack.http import HttpError, http_request, http_request_process, http_request_url from slack.task import FutureProcess, FutureTimer, FutureUrl, weechat_task_cb from slack.util import get_callback_name @patch.object(weechat, "hook_url") def test_http_request_success(mock_method: MagicMock): url = "http://example.com" options = {"option": "1"} timeout = 123 coroutine = http_request(url, options, timeout) future = coroutine.send(None) assert isinstance(future, FutureUrl) mock_method.assert_called_once_with( url, options, timeout, get_callback_name(weechat_task_cb), future.id, ) future.set_result( (url, options, {"response_code": "200", "headers": "", "output": "response"}) ) with pytest.raises(StopIteration) as excinfo: coroutine.send(None) assert excinfo.value.value == "response" @patch.object(weechat, "hook_process_hashtable") def test_http_request_process_success(mock_method: MagicMock): url = "http://example.com" options = {"option": "1"} timeout = 123 coroutine = http_request_process(url, options, timeout) future = coroutine.send(None) assert isinstance(future, FutureProcess) mock_method.assert_called_once_with( f"url:{url}", {**options, "header": "1"}, timeout, get_callback_name(weechat_task_cb), future.id, ) body = "HTTP/2 200\r\n\r\nresponse" future.set_result(("", 0, body, "")) with pytest.raises(StopIteration) as excinfo: coroutine.send(None) assert excinfo.value.value == (200, "HTTP/2 200", "response") def test_http_request_url_error(): url = "http://example.com" coroutine = http_request_url(url, {}, 0) future = coroutine.send(None) assert isinstance(future, FutureUrl) future.set_result((url, {}, {"error": "error"})) with pytest.raises(HttpError) as excinfo: coroutine.send(None) assert excinfo.value.url == url assert excinfo.value.return_code is None assert excinfo.value.http_status_code is None assert excinfo.value.error == "error" def test_http_request_process_error_return_code(): url = "http://example.com" coroutine = http_request_process(url, {}, 0) future = coroutine.send(None) assert isinstance(future, FutureProcess) future.set_result(("", -2, "", "")) with pytest.raises(HttpError) as excinfo: coroutine.send(None) assert excinfo.value.url == url assert excinfo.value.return_code == -2 assert excinfo.value.http_status_code is None assert excinfo.value.error == "" def test_http_request_process_error_stderr(): url = "http://example.com" coroutine = http_request_process(url, {}, 0) future = coroutine.send(None) assert isinstance(future, FutureProcess) future.set_result(("", 0, "", "err")) with pytest.raises(HttpError) as excinfo: coroutine.send(None) assert excinfo.value.url == url assert excinfo.value.return_code == 0 assert excinfo.value.http_status_code is None assert excinfo.value.error == "err" def test_http_request_error_http_status(): url = "http://example.com" coroutine = http_request(url, {}, 0) future = coroutine.send(None) assert isinstance(future, FutureUrl) future.set_result( (url, {}, {"response_code": "400", "headers": "", "output": "response"}) ) with pytest.raises(HttpError) as excinfo: coroutine.send(None) assert excinfo.value.url == url assert excinfo.value.return_code is None assert excinfo.value.http_status_code == 400 assert excinfo.value.error == "response" def test_http_request_error_retry_success(): url = "http://example.com" coroutine = http_request(url, {}, 0, max_retries=2) future_1 = coroutine.send(None) assert isinstance(future_1, FutureUrl) future_1.set_result((url, {}, {"error": "error"})) future_2 = coroutine.send(None) assert isinstance(future_2, FutureTimer) future_2.set_result((0,)) future_3 = coroutine.send(None) assert isinstance(future_3, FutureUrl) future_3.set_result( (url, {}, {"response_code": "200", "headers": "", "output": "response"}) ) with pytest.raises(StopIteration) as excinfo: coroutine.send(None) assert excinfo.value.value == "response" def test_http_request_error_retry_error(): url = "http://example.com" coroutine = http_request(url, {}, 0, max_retries=2) future_1 = coroutine.send(None) assert isinstance(future_1, FutureUrl) future_1.set_result((url, {}, {"error": "error"})) future_2 = coroutine.send(None) assert isinstance(future_2, FutureTimer) future_2.set_result((0,)) future_3 = coroutine.send(None) assert isinstance(future_3, FutureUrl) future_3.set_result((url, {}, {"error": "error"})) future_4 = coroutine.send(None) assert isinstance(future_4, FutureTimer) future_4.set_result((0,)) future_5 = coroutine.send(None) assert isinstance(future_5, FutureUrl) future_5.set_result((url, {}, {"error": "error"})) with pytest.raises(HttpError) as excinfo: coroutine.send(None) assert excinfo.value.url == url assert excinfo.value.return_code is None assert excinfo.value.http_status_code is None assert excinfo.value.error == "error" def test_http_request_url_multiple_headers(): url = "http://example.com" coroutine = http_request_url(url, {}, 0) future = coroutine.send(None) assert isinstance(future, FutureUrl) headers = ( dedent( """ HTTP/1.1 200 Connection established HTTP/2 200 content-type: application/json; charset=utf-8 """ ) .strip() .replace("\n", "\r\n") ) future.set_result( (url, {}, {"response_code": "200", "headers": headers, "output": "response"}) ) with pytest.raises(StopIteration) as excinfo: coroutine.send(future) assert excinfo.value.value == ( 200, "2 200\r\ncontent-type: application/json; charset=utf-8", "response", ) def test_http_request_process_multiple_headers(): url = "http://example.com" coroutine = http_request_process(url, {}, 0) future = coroutine.send(None) assert isinstance(future, FutureProcess) body = ( dedent( """ HTTP/1.1 200 Connection established HTTP/2 200 content-type: application/json; charset=utf-8 response """ ) .strip() .replace("\n", "\r\n") ) future.set_result(("", 0, body, "")) with pytest.raises(StopIteration) as excinfo: coroutine.send(future) assert excinfo.value.value == ( 200, "2 200\r\ncontent-type: application/json; charset=utf-8", "response", ) @patch.object(weechat, "hook_timer") def test_http_request_ratelimit(mock_method: MagicMock): url = "http://example.com" coroutine = http_request(url, {}, 0) future_1 = coroutine.send(None) assert isinstance(future_1, FutureUrl) future_1.set_result( ( url, {}, { "response_code": "429", "headers": "HTTP/2 429\r\nRetry-After: 12", "output": "response", }, ) ) future_2 = coroutine.send(None) assert isinstance(future_2, FutureTimer) future_2.set_result((0,)) mock_method.assert_called_once_with( 12000, 0, 1, get_callback_name(weechat_task_cb), future_2.id ) future_3 = coroutine.send(None) assert isinstance(future_3, FutureUrl) future_3.set_result( (url, {}, {"response_code": "200", "headers": "", "output": "response"}) ) with pytest.raises(StopIteration) as excinfo: coroutine.send(None) assert excinfo.value.value == "response"