-
|
I'm trying to test something in my codebase. This is the test I have written: from contextlib import aclosing, suppress
from threading import Thread
import pytest
from fakeredis import TcpFakeServer
from litestar import Litestar
from litestar.testing import AsyncTestClient
from sqlalchemy.exc import InvalidRequestError
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Session
from sqlalchemy.orm.exc import ObjectDeletedError
from app.db import app_db_config
from app.models import Devices, Organizations, OrganizationUsers, UserDevicePermissionAssociation, Users
from app.util.testutil import disable_event_listener
@pytest.fixture(autouse=True, scope="session")
def prepare_db():
from litestar.plugins.sqlalchemy import AlembicCommands
alembic_commands = AlembicCommands(sqlalchemy_config=app_db_config)
alembic_commands.upgrade()
@pytest.fixture(autouse=True)
def redis_server():
server = TcpFakeServer(("127.0.0.1", 6381), server_type="redis")
th = Thread(target=server.serve_forever, daemon=True)
th.start()
yield
server.shutdown()
server.server_close()
th.join()
@pytest.fixture
async def client():
from app import app, session_config
async with AsyncTestClient(
app,
session_config=session_config,
# https://anyio.readthedocs.io/en/stable/basics.html#backend-specific-options
backend_options={"use_uvloop": True},
) as client:
app.debug = True
app.state.is_test_mode = True
yield client
app.debug = False
app.state.is_test_mode = False
@pytest.fixture
async def db_session():
session_maker = app_db_config.create_session_maker()
db_objs = []
async with aclosing(session_maker()) as session:
session.info["is_test_mode"] = True
yield session
for obj in session.identity_map.values():
with suppress(InvalidRequestError):
await session.refresh(obj)
db_objs.append(obj)
async with app_db_config.get_session() as session:
for obj in db_objs:
with suppress(ObjectDeletedError):
if obj not in session.deleted:
await session.delete(obj)
await session.commit()
async def test_get_devices_returns_users_devices(client: AsyncTestClient[Litestar], db_session: AsyncSession):
from app.models.users import create_cognito_user_on_user_create_or_delete
with disable_event_listener(Session, "before_flush", create_cognito_user_on_user_create_or_delete):
user = Users(name="test", email="user@testemail.com", username="test_user")
org = Organizations(name="test_org", s3_bucket="test_bucket", s3_region="test_region")
db_session.add_all([user, org])
await db_session.flush()
devices = (Devices(name="device-01", organization_id=org.id), Devices(name="device-02", organization_id=org.id))
db_session.add_all(devices)
await db_session.flush()
user_org = OrganizationUsers(user_id=user.id, organization_id=org.id)
user_devices = [UserDevicePermissionAssociation(user_id=user.id, device_id=d.id, read=True) for d in devices]
db_session.add_all([user_org, *user_devices])
await db_session.flush()
# !! problematic code !!
await client.set_session_data({"user_id": user.id, "access_token": "1", "id_token": "1"})
await db_session.commit()
await db_session.refresh(user)
response = await client.get("/devices")
assert response.status_code == 200When I run the test above, the request fails with 500 error code. The output shows: Very similar to #1920 Tbh, I didn't realize it was the line above that was causing this issue, but today I was trying to make the test work, and decided to replace that line with a call to blocking portal: with client.portal() as portal:
portal.call(client.set_session_data, {"user_id": user.id, "access_token": "1", "id_token": "1"})To my surprise, it kinda worked! The test got past that line. However, the session data was not set. In the middleware within my app which handles authentication, it fails to read any of the attributes I set for the session. class AppAuthMiddleware(AbstractAuthenticationMiddleware):
scopes = {ScopeType.HTTP}
@override
async def authenticate_request(self, connection):
with contextlib.suppress(ImproperlyConfiguredException, InvalidTokenError):
user_id = cast(str | None, connection.session.get("user_id"))
access_token = cast(str | None, connection.session.get("access_token"))
id_token = cast(str | None, connection.session.get("id_token"))
if not (user_id and access_token and id_token):
raise NotAuthorizedException(detail="User is not logged in")
...At this point, I'm now stuck. What am I doing wrong? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 6 replies
-
|
Just following up here. I investigated further and can confirm that after the call to This leads me to believe that the problem is that the client is not even sending the cookie along when the request is sent. |
Beta Was this translation helpful? Give feedback.
-
|
Despite having discovered a solution, I still want to point out that the initial problem that plagued this line is still there, and needs to be investigated: # !! problematic code !!
await client.set_session_data({"user_id": user.id, "access_token": "1", "id_token": "1"}) |
Beta Was this translation helpful? Give feedback.
I think I discovered the problem.
session_configis defined as:The problem seems to arise from the fact that I set
secure=True, which means the cookie is not available in nonhttpscontexts. By changing thebase_urlof theTestClienttohttps://testserver.local, the session data is now available in the middleware.