aboutsummaryrefslogblamecommitdiffstats
path: root/tests/test_http_request.py
blob: 1d87df9182999b1150ca4867c5ccc6533c8e00fd (plain) (tree)
1
2
3
4
5
6
7
8
9
                           




                                          

                                                                                      
                                        

 
                                  




                                                      
 
                                 


























                                                                                     





                                            
                                           


                  
                                       
                                        

                                                 
                            














                                                                 

                                                 
                                         

 
                                                  
                              
                                                



                                            

                                             
                            


                                          
                                                 


                                    
                                             
                              
                                                



                                            

                                             
                            


                                         
                                                 


                                       
                                          
                              
                                        

                                 
                                        
 


                                                                                

                                             
                            

                                   
                                            
                                                
                                            




                                                       
 
                                   

                                                      





                                            
                                          
 


                                                                                

                                                 
                            
                                            




                                                       
 
                                   

                                                      





                                            

                                                      





                                            

                                                      

                                             
                            

                                   

                                                 
                                         

 
                                             
                              





                                            
               
























                                                                                     


                                            

               
               




                                                         
                    




                              
                                        

                                                 
                              




                                                                 

 



                                                        

                                   












                                                           
 


                                            
 
                                        
                                                                    
     
 
                                   
                                          
 


                                                                                
 
                                                 
                            
                                            
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"