コード例 #1
0
async def test_locmem_cache_uses_net_loc_for_separaing_namespaces():
    cache = Cache("locmem://")
    other_cache = Cache("locmem://other")

    await cache.connect()
    await other_cache.connect()

    await cache.set("test", "Ok!")
    assert await other_cache.get("test") is None
    assert await cache.get("test") == "Ok!"
コード例 #2
0
async def test_streaming_response() -> None:
    """Streaming responses should not be cached."""
    cache = Cache("locmem://null")

    async def body() -> typing.AsyncIterator[str]:
        yield "Hello, "
        yield "world!"

    async def app(scope: Scope, receive: Receive, send: Send) -> None:
        response = StreamingResponse(body())
        await response(scope, receive, send)

    spy = CacheSpy(app)
    app = CacheMiddleware(spy, cache=cache)
    client = httpx.AsyncClient(app=app, base_url="http://testserver")

    async with cache, client:
        assert spy.misses == 0

        r = await client.get("/")
        assert r.status_code == 200
        assert r.text == "Hello, world!"
        assert spy.misses == 1

        r = await client.get("/")
        assert r.status_code == 200
        assert r.text == "Hello, world!"
        assert spy.misses == 2
コード例 #3
0
async def test_use_cached_head_response_on_get() -> None:
    """
    Making a HEAD request should use the cached response for future GET requests.
    """
    cache = Cache("locmem://null")
    spy = CacheSpy(PlainTextResponse("Hello, world!"))
    app = CacheMiddleware(spy, cache=cache)
    client = httpx.AsyncClient(app=app, base_url="http://testserver")

    async with cache, client:
        assert spy.misses == 0

        r = await client.head("/")
        assert not r.text
        assert r.status_code == 200
        assert "Expires" in r.headers
        assert "Cache-Control" in r.headers
        assert spy.misses == 1

        r1 = await client.get("/")
        assert r1.text == "Hello, world!"
        assert r1.status_code == 200
        assert "Expires" in r.headers
        assert "Cache-Control" in r.headers
        assert spy.misses == 1
コード例 #4
0
async def test_not_http() -> None:
    async def app(scope: Scope, receive: Receive, send: Send) -> None:
        assert scope["type"] == "lifespan"

    cache = Cache("locmem://null")
    app = CacheMiddleware(app, cache=cache)
    await app({"type": "lifespan"}, mock_receive, mock_send)
コード例 #5
0
async def test_decorator_raw_asgi() -> None:
    cache = Cache("locmem://null", ttl=2 * 60)

    @cached(cache)
    async def app(scope: Scope, receive: Receive, send: Send) -> None:
        response = PlainTextResponse("Hello, world!")
        await response(scope, receive, send)

    spy = app.app = CacheSpy(app.app)
    client = httpx.AsyncClient(app=app, base_url="http://testserver")

    async with cache, client:
        assert spy.misses == 0

        r = await client.get("/")
        assert r.status_code == 200
        assert r.text == "Hello, world!"
        assert "Expires" in r.headers
        assert "Cache-Control" in r.headers
        assert spy.misses == 1

        r = await client.get("/")
        assert r.status_code == 200
        assert r.text == "Hello, world!"
        assert "Expires" in r.headers
        assert "Cache-Control" in r.headers
        assert spy.misses == 1
コード例 #6
0
async def test_cookies_in_response_and_cookieless_request() -> None:
    """
    Responses that set cookies shouldn't be cached
    if the request doesn't have cookies.
    """
    cache = Cache("locmem://null")

    async def app(scope: Scope, receive: Receive, send: Send) -> None:
        response = PlainTextResponse("Hello, world!")
        response.set_cookie("session_id", "1234")
        await response(scope, receive, send)

    spy = CacheSpy(app)
    app = CacheMiddleware(spy, cache=cache)
    client = httpx.AsyncClient(app=app, base_url="http://testserver")

    async with cache, client:
        r = await client.get("/")
        assert r.status_code == 200
        assert r.text == "Hello, world!"
        assert spy.misses == 1

        r = await client.get("/")
        assert r.status_code == 200
        assert r.text == "Hello, world!"
        assert spy.misses == 2
コード例 #7
0
async def test_cache_response() -> None:
    cache = Cache("locmem://null", ttl=2 * 60)
    spy = CacheSpy(PlainTextResponse("Hello, world!"))
    app = CacheMiddleware(spy, cache=cache)
    client = httpx.AsyncClient(app=app, base_url="http://testserver")

    async with cache, client:
        assert spy.misses == 0

        r = await client.get("/")
        assert r.status_code == 200
        assert r.text == "Hello, world!"
        assert spy.misses == 1

        assert "Expires" in r.headers
        expires_fmt = "%a, %d %b %Y %H:%M:%S GMT"
        expires = dt.datetime.strptime(r.headers["Expires"], expires_fmt)
        delta: dt.timedelta = expires - dt.datetime.utcnow()
        assert delta.total_seconds() == pytest.approx(120, rel=1e-2)
        assert "Cache-Control" in r.headers
        assert r.headers["Cache-Control"] == "max-age=120"

        r1 = await client.get("/")
        assert spy.misses == 1
        assert ComparableHTTPXResponse(r1) == r

        r2 = await client.get("/")
        assert spy.misses == 1
        assert ComparableHTTPXResponse(r2) == r
コード例 #8
0
ファイル: conftest.py プロジェクト: rockallite/async-caches
async def cache():
    obj = Cache("redis://localhost:6379/1")
    await obj.connect()
    await obj.clear()
    yield obj
    await obj.clear()
    await obj.disconnect()
コード例 #9
0
async def test_decorate_starlette_view() -> None:
    cache = Cache("locmem://null", ttl=2 * 60)

    with pytest.raises(ValueError):

        @cached(cache)
        async def home(request: Request) -> Response:
            ...  # pragma: no cover
コード例 #10
0
ファイル: test_cache.py プロジェクト: rafalp/async-caches
async def test_cache_can_be_used_as_context_manager():
    async with Cache("locmem://") as cache:
        await cache.set("test", "Ok!")
        assert await cache.get("test") == "Ok!"

        assert await cache.get_or_set('test2', _testing_coroutine('arg', test1='kwarg')) == "Ok!"

        assert await cache(_testing_coroutine('arg', test1='kwarg')) == 'Ok!'
コード例 #11
0
async def test_vary() -> None:
    """
    Sending different values for request headers registered as varying should
    result in different cache entries.
    """
    cache = Cache("locmem://null")

    async def gzippable_app(scope: Scope, receive: Receive,
                            send: Send) -> None:
        headers = Headers(scope=scope)

        if "gzip" in headers.getlist("accept-encoding"):
            body = gzip.compress(b"Hello, world!")
            response = PlainTextResponse(
                content=body,
                headers={
                    "Content-Encoding": "gzip",
                    "Content-Length": str(len(body))
                },
            )
        else:
            response = PlainTextResponse("Hello, world!")

        response.headers["Vary"] = "Accept-Encoding"
        await response(scope, receive, send)

    spy = CacheSpy(gzippable_app)
    app = CacheMiddleware(spy, cache=cache)
    client = httpx.AsyncClient(app=app, base_url="http://testserver")

    async with cache, client:
        r = await client.get("/", headers={"accept-encoding": "gzip"})
        assert spy.misses == 1
        assert r.status_code == 200
        assert r.text == "Hello, world!"
        assert r.headers["content-encoding"] == "gzip"
        assert "Expires" in r.headers
        assert "Cache-Control" in r.headers

        # Different "Accept-Encoding" header => the cached result
        # for "Accept-Encoding: gzip" should not be used.
        r1 = await client.get("/", headers={"accept-encoding": "identity"})
        assert spy.misses == 2
        assert r1.status_code == 200
        assert r1.text == "Hello, world!"
        assert "Expires" in r.headers
        assert "Cache-Control" in r.headers

        # This "Accept-Encoding" header has already been seen => we should
        # get a cached response.
        r2 = await client.get("/", headers={"accept-encoding": "gzip"})
        assert spy.misses == 2
        assert r.status_code == 200
        assert r.text == "Hello, world!"
        assert r2.headers["Content-Encoding"] == "gzip"
        assert "Expires" in r.headers
        assert "Cache-Control" in r.headers
コード例 #12
0
async def test_duplicate_caching() -> None:
    cache = Cache("locmem://default")
    special_cache = Cache("locmem://special")

    class DuplicateCache(HTTPEndpoint):
        pass

    app = Starlette(
        routes=[
            Route("/duplicate_cache",
                  CacheMiddleware(DuplicateCache, cache=special_cache))
        ],
        middleware=[Middleware(CacheMiddleware, cache=cache)],
    )

    client = httpx.AsyncClient(app=app, base_url="http://testserver")

    async with cache, special_cache, client:
        with pytest.raises(DuplicateCaching):
            await client.get("/duplicate_cache")
コード例 #13
0
ファイル: test_logging.py プロジェクト: snmz216/asgi-caches
async def test_logs_trace(capsys: typing.Any) -> None:
    cache = Cache("locmem://null", ttl=2 * 60)
    app = CacheMiddleware(PlainTextResponse("Hello, world!"), cache=cache)
    client = httpx.AsyncClient(app=app, base_url="http://testserver")

    async with cache, client:
        with override_log_level("trace"):
            await client.get("/")

    stderr = capsys.readouterr().err
    assert "cache_lookup MISS" in stderr
    assert "get_from_cache request.url='http://testserver/" in stderr
コード例 #14
0
async def test_cache_not_connected() -> None:
    cache = Cache("locmem://null", ttl=2 * 60)
    app = CacheMiddleware(PlainTextResponse("Hello, world!"), cache=cache)
    client = httpx.AsyncClient(app=app, base_url="http://testserver")

    async with client:
        with pytest.raises(CacheNotConnected) as ctx:
            await client.get("/")

    exc = ctx.value
    assert exc.cache is cache
    assert str(cache.url) in str(exc)
コード例 #15
0
async def test_decorator_starlette_endpoint() -> None:
    cache = Cache("locmem://null", ttl=2 * 60)

    @cached(cache)
    class CachedHome(HTTPEndpoint):
        async def get(self, request: Request) -> Response:
            return PlainTextResponse("Hello, world!")

    class UncachedUsers(HTTPEndpoint):
        async def get(self, request: Request) -> Response:
            return PlainTextResponse("Hello, users!")

    assert isinstance(CachedHome, CacheMiddleware)
    spy = CachedHome.app = CacheSpy(CachedHome.app)
    users_spy = CacheSpy(UncachedUsers)

    app = Starlette(routes=[Route("/", CachedHome), Route("/users", users_spy)])
    client = httpx.AsyncClient(app=app, base_url="http://testserver")

    async with cache, client:
        assert spy.misses == 0

        r = await client.get("/")
        assert r.status_code == 200
        assert r.text == "Hello, world!"
        assert "Expires" in r.headers
        assert "Cache-Control" in r.headers
        assert spy.misses == 1

        r = await client.get("/")
        assert r.status_code == 200
        assert r.text == "Hello, world!"
        assert "Expires" in r.headers
        assert "Cache-Control" in r.headers
        assert spy.misses == 1

        assert users_spy.misses == 0

        r = await client.get("/users")
        assert r.status_code == 200
        assert r.text == "Hello, users!"
        assert "Expires" not in r.headers
        assert "Cache-Control" not in r.headers
        assert users_spy.misses == 1

        r = await client.get("/users")
        assert r.status_code == 200
        assert r.text == "Hello, users!"
        assert "Expires" not in r.headers
        assert "Cache-Control" not in r.headers
        assert users_spy.misses == 2
コード例 #16
0
ファイル: test_logging.py プロジェクト: snmz216/asgi-caches
async def test_logs_debug(capsys: typing.Any) -> None:
    cache = Cache("locmem://null", ttl=2 * 60)
    app = CacheMiddleware(PlainTextResponse("Hello, world!"), cache=cache)
    client = httpx.AsyncClient(app=app, base_url="http://testserver")

    async with cache, client:
        with override_log_level("debug"):
            await client.get("/")
            await client.get("/")

    stderr = capsys.readouterr().err
    miss_line, store_line, hit_line, *_ = stderr.split("\n")
    assert "cache_lookup MISS" in miss_line
    assert "store_in_cache max_age=120" in store_line
    assert "cache_lookup HIT" in hit_line
    assert "get_from_cache request.url='http://testserver/" not in stderr
コード例 #17
0
async def test_not_200_ok(status_code: int) -> None:
    """Responses that don't have status code 200 should not be cached."""
    cache = Cache("locmem://null")
    spy = CacheSpy(PlainTextResponse("Hello, world!", status_code=status_code))
    app = CacheMiddleware(spy, cache=cache)
    client = httpx.AsyncClient(app=app, base_url="http://testserver")

    async with cache, client:
        r = await client.get("/")
        assert r.status_code == status_code
        assert r.text == "Hello, world!"
        assert "Expires" not in r.headers
        assert "Cache-Control" not in r.headers
        assert spy.misses == 1

        r1 = await client.get("/")
        assert ComparableHTTPXResponse(r1) == r
        assert spy.misses == 2
コード例 #18
0
async def test_non_cachable_request() -> None:
    cache = Cache("locmem://null")
    spy = CacheSpy(PlainTextResponse("Hello, world!"))
    app = CacheMiddleware(spy, cache=cache)
    client = httpx.AsyncClient(app=app, base_url="http://testserver")

    async with cache, client:
        assert spy.misses == 0

        r = await client.post("/")
        assert r.status_code == 200
        assert r.text == "Hello, world!"
        assert "Expires" not in r.headers
        assert "Cache-Control" not in r.headers
        assert spy.misses == 1

        r1 = await client.post("/")
        assert ComparableHTTPXResponse(r1) == r
        assert spy.misses == 2
コード例 #19
0
def setup(project_settings: str = None, database: bool = False) -> GraphQL:
    """Load Turbulette applications and return the GraphQL route."""
    project_settings_module = (get_project_settings_by_env()
                               if not project_settings else
                               import_module(project_settings))

    # The database connection has to be initialized before the LazySettings object to be setup
    # so we have to connect to the database before the registry to be setup
    if database:
        get_gino_instance()

    registry = Registry(project_settings_module=project_settings_module)
    conf.registry.__setup__(registry)
    schema = registry.setup()
    # At this point, settings are now available through `settings` from `turbulette.conf` module
    settings = conf.settings

    # Now that the database connection is established, we can use `settings`

    cache.__setup__(Cache(settings.CACHE))

    extensions: List[Type[Extension]] = [PolicyExtension]
    for ext in settings.ARIADNE_EXTENSIONS:
        module_class = ext.rsplit(".", 1)
        extensions.append(
            getattr(
                import_module(module_class[0]),
                module_class[1],
            ))

    graphql_route = GraphQL(
        schema,
        debug=settings.DEBUG,
        extensions=extensions,
        error_formatter=error_formatter,
    )
    return graphql_route
コード例 #20
0
def test_cache_cant_be_initialized_with_base_backend():
    with pytest.raises(AssertionError):
        Cache("base://null")
コード例 #21
0
async def test_backend_errors_if_more_than_one_connection_is_opened():
    cache = Cache("redis://localhost/1")
    await cache.connect()
    with pytest.raises(AssertionError):
        await cache.connect()
    await cache.disconnect()
コード例 #22
0
async def cache():
    obj = Cache("locmem://0")
    await obj.connect()
    return obj
コード例 #23
0
async def test_backend_errors_if_nonexistant_connection_is_closed():
    cache = Cache("redis://localhost/1")
    with pytest.raises(AssertionError):
        await cache.disconnect()
コード例 #24
0
def test_dummy_cache_can_be_initialized_without_net_loc():
    Cache("dummy://")
コード例 #25
0
def test_dummy_cache_can_be_initialized_with_net_loc():
    Cache("dummy://primary")
コード例 #26
0
async def test_dummy_cache_can_be_connected_and_disconnected():
    cache = Cache("dummy://", version=20)
    await cache.connect()
    await cache.disconnect()
コード例 #27
0
def test_dummy_cache_can_be_initialized_with_version():
    Cache("dummy://", version=20)
コード例 #28
0
def test_dummy_cache_can_be_initialized_with_ttl():
    Cache("dummy://", ttl=600)
コード例 #29
0
def test_dummy_cache_can_be_initialized_with_key_prefix():
    Cache("dummy://", key_prefix="test")
コード例 #30
0
from caches import Cache

cache = Cache("locmem://default", ttl=2 * 60)
special_cache = Cache("locmem://special", ttl=60)