File: test_async_httpx.py

package info (click to toggle)
hishel 1.1.9-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,340 kB
  • sloc: python: 6,600; sh: 24; makefile: 5
file content (121 lines) | stat: -rw-r--r-- 4,454 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import gzip
from datetime import datetime
from zoneinfo import ZoneInfo

import anysqlite
import httpx
import pytest
from httpx import ByteStream, MockTransport
from inline_snapshot import snapshot
from time_machine import travel

from hishel import AsyncSqliteStorage
from hishel._policies import FilterPolicy
from hishel.httpx import AsyncCacheClient, AsyncCacheTransport


@pytest.mark.anyio
@travel(datetime(2024, 1, 1, 0, 0, 0, tzinfo=ZoneInfo("UTC")), tick=False)
async def test_simple_caching(caplog: pytest.LogCaptureFixture) -> None:
    client = AsyncCacheClient(
        storage=AsyncSqliteStorage(connection=await anysqlite.connect(":memory:")),
    )

    with caplog.at_level("DEBUG", logger="hishel"):
        await client.get("https://hishel.com")
        response = await client.get("https://hishel.com")

    assert caplog.messages == snapshot(
        [
            "Handling state: IdleClient",
            "Handling state: CacheMiss",
            "Storing response in cache",
            "Handling state: StoreAndUse",
            "Handling state: IdleClient",
            "Handling state: FromCache",
        ]
    )
    assert response.extensions == snapshot(
        {
            "hishel_from_cache": True,
            "hishel_created_at": 1704067200.0,
            "hishel_revalidated": False,
            "hishel_stored": False,
        }
    )


@pytest.mark.anyio
@travel(datetime(2024, 1, 1, 0, 0, 0, tzinfo=ZoneInfo("UTC")), tick=False)
async def test_simple_caching_ignoring_spec(caplog: pytest.LogCaptureFixture) -> None:
    client = AsyncCacheClient(
        storage=AsyncSqliteStorage(connection=await anysqlite.connect(":memory:")),
        policy=FilterPolicy(),
    )

    with caplog.at_level("DEBUG", logger="hishel"):
        await client.get("https://hishel.com", extensions={"hishel_spec_ignore": True})
        response = await client.get("https://hishel.com", extensions={"hishel_spec_ignore": True})

    assert caplog.messages == snapshot(
        [
            "Trying to get cached response ignoring specification",
            "Found 0 cached entries for the request",
            "Storing response in cache ignoring specification",
            "Trying to get cached response ignoring specification",
            "Found 1 cached entries for the request",
            "Found matching cached response for the request",
        ]
    )
    assert response.extensions == snapshot(
        {
            "hishel_from_cache": True,
            "hishel_created_at": 1704067200.0,
            "hishel_revalidated": False,
            "hishel_stored": False,
        }
    )


@pytest.mark.anyio
async def test_encoded_content_caching() -> None:
    data = gzip.compress(b"a" * 1000)
    compressed_data = ByteStream(data)
    mocked_responses = [
        httpx.Response(
            200,
            stream=compressed_data,
            headers={
                "Content-Encoding": "gzip",
                "Content-Type": "text/plain",
                "Content-Length": str(len(data)),
            },
        )
    ]

    async def handler(request: httpx.Request) -> httpx.Response:
        if not mocked_responses:
            raise RuntimeError("No more mocked responses available")
        return mocked_responses.pop(0)

    storage = AsyncSqliteStorage(connection=await anysqlite.connect(":memory:"))

    client = AsyncCacheClient(
        transport=AsyncCacheTransport(
            next_transport=MockTransport(handler=handler), storage=storage, policy=FilterPolicy()
        ),
    )

    # First request - should fetch from the mocked transport and store in cache
    async with client.stream("get", "https://localhost", extensions={"hishel_spec_ignore": True}) as response:
        response_data = b"".join([chunk async for chunk in response.aiter_raw()])
        assert data == response_data
        assert response.headers.get("Content-Length") == str(len(data)) == str(len(response_data))
        assert response.headers.get("Content-Encoding") == "gzip"

    # Second request - should fetch from cache
    async with client.stream("get", "https://localhost", extensions={"hishel_spec_ignore": True}) as response:
        response_data = b"".join([chunk async for chunk in response.aiter_raw()])
        assert data == response_data
        assert response.headers.get("Content-Length") == str(len(data)) == str(len(response_data))
        assert response.headers.get("Content-Encoding") == "gzip"