encode / starlette

The little ASGI framework that shines. 🌟

Home Page:https://www.starlette.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Broken tests on 32-bit platforms: `test_set_cookie` and `test_expires_on_set_cookie`

kraptor opened this issue · comments

The following test will never success in 32-bit platforms, as the date goes beyond the maximum value that time_t can hold, causing an overflow exception. I suggest to add a skip on these platforms for these tests or rework them to not to break.

Broken tests:

We are skipping these tests in openSUSE as we build for i586 and armv7l archs, but it would be nice to take them into account.

Here is an excerpt of one of the errors:


[   40s] ___________________ test_expires_on_set_cookie[asyncio-int] ____________________
[   40s] 
[   40s] test_client_factory = functools.partial(<class 'starlette.testclient.TestClient'>, backend='asyncio', backend_options={})
[   40s] monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xf38e7988>
[   40s] expires = 10
[   40s] 
[   40s]     @pytest.mark.parametrize(
[   40s]         "expires",
[   40s]         [
[   40s]             pytest.param(
[   40s]                 dt.datetime(2100, 1, 22, 12, 0, 10, tzinfo=dt.timezone.utc), id="datetime"
[   40s]             ),
[   40s]             pytest.param("Fri, 22 Jan 2100 12:00:10 GMT", id="str"),
[   40s]             pytest.param(10, id="int"),
[   40s]         ],
[   40s]     )
[   40s]     def test_expires_on_set_cookie(test_client_factory, monkeypatch, expires):
[   40s]         # Mock time used as a reference for `Expires` by stdlib `SimpleCookie`.
[   40s]         mocked_now = dt.datetime(2100, 1, 22, 12, 0, 0, tzinfo=dt.timezone.utc)
[   40s]         monkeypatch.setattr(time, "time", lambda: mocked_now.timestamp())
[   40s]     
[   40s]         async def app(scope, receive, send):
[   40s]             response = Response("Hello, world!", media_type="text/plain")
[   40s]             response.set_cookie("mycookie", "myvalue", expires=expires)
[   40s]             await response(scope, receive, send)
[   40s]     
[   40s]         client = test_client_factory(app)
[   40s] >       response = client.get("/")
[   40s] 
[   40s] tests/test_responses.py:345: 
[   40s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
[   40s] starlette/testclient.py:492: in get
[   40s]     return super().get(
[   40s] /usr/lib/python3.8/site-packages/httpx/_client.py:1045: in get
[   40s]     return self.request(
[   40s] starlette/testclient.py:458: in request
[   40s]     return super().request(
[   40s] /usr/lib/python3.8/site-packages/httpx/_client.py:821: in request
[   40s]     return self.send(request, auth=auth, follow_redirects=follow_redirects)
[   40s] /usr/lib/python3.8/site-packages/httpx/_client.py:908: in send
[   40s]     response = self._send_handling_auth(
[   40s] /usr/lib/python3.8/site-packages/httpx/_client.py:936: in _send_handling_auth
[   40s]     response = self._send_handling_redirects(
[   40s] /usr/lib/python3.8/site-packages/httpx/_client.py:973: in _send_handling_redirects
[   40s]     response = self._send_single_request(request)
[   40s] /usr/lib/python3.8/site-packages/httpx/_client.py:1009: in _send_single_request
[   40s]     response = transport.handle_request(request)
[   40s] starlette/testclient.py:337: in handle_request
[   40s]     raise exc
[   40s] starlette/testclient.py:334: in handle_request
[   40s]     portal.call(self.app, scope, receive, send)
[   40s] /usr/lib/python3.8/site-packages/anyio/from_thread.py:283: in call
[   40s]     return cast(T_Retval, self.start_task_soon(func, *args).result())
[   40s] /usr/lib/python3.8/concurrent/futures/_base.py:437: in result
[   40s]     return self.__get_result()
[   40s] /usr/lib/python3.8/concurrent/futures/_base.py:389: in __get_result
[   40s]     raise self._exception
[   40s] /usr/lib/python3.8/site-packages/anyio/from_thread.py:219: in _call_func
[   40s]     retval = await retval
[   40s] tests/test_responses.py:341: in app
[   40s]     response.set_cookie("mycookie", "myvalue", expires=expires)
[   40s] starlette/responses.py:140: in set_cookie
[   40s]     cookie_val = cookie.output(header="").strip()
[   40s] /usr/lib/python3.8/http/cookies.py:502: in output
[   40s]     result.append(value.output(attrs, header))
[   40s] /usr/lib/python3.8/http/cookies.py:372: in output
[   40s]     return "%s %s" % (header, self.OutputString(attrs))
[   40s] /usr/lib/python3.8/http/cookies.py:408: in OutputString
[   40s]     append("%s=%s" % (self._reserved[key], _getdate(value)))
[   40s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
[   40s] 
[   40s] future = 10, weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', ...]
[   40s] monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', ...]
[   40s] 
[   40s]     def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
[   40s]         from time import gmtime, time
[   40s]         now = time()
[   40s] >       year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
[   40s] E       OverflowError: timestamp out of range for platform time_t

We just need to change the year?

We just need to change the year?

Yeah, that will do it.

Would you like to open a PR? Since you can verify the date it will work on those OSs.

Sure, let me do it.

Isn't the test wrong anyway? Shouldn't set-cookie be automatically propagated to subsequent requests made by the TestClient?

I had to work around this limitation in my tests using something like this:

from requests.models import Response
from starlette.testclient import TestClient


def collect_set_cookies(response: Response):
    cookies = SimpleCookie()
    cookies.load(response.headers["set-cookie"])
    return {name: cookie.value for name, cookie in cookies.items()}


def test_set_cookies(client: TestClient):
    response_A = client.post("/apiA")
    jsonA = response.json()
    cookies = collect_set_cookies(response)
    responseB = client.post("/apiB", json=jsonA, cookies=cookies)
    assert True