Skip to content

Commit 87419e6

Browse files
committed
🔧 Add fixture for multiple user creation and test concurrent login/logout
1 parent de51253 commit 87419e6

File tree

2 files changed

+75
-2
lines changed

2 files changed

+75
-2
lines changed

.github/copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,4 @@ This document provides guidelines and best practices for using GitHub Copilot in
5252
- [Python Coding Conventions](../docs/coding-conventions.md)
5353
- [Environment Variables Guide](../docs/env-vars.md)
5454
- [Steps to Upgrade Python](../docs/steps-to-upgrade-python.md)
55-
- [Node.js Installation Script](../scripts/install_nodejs_14.bash)
55+
- [Pydantic Annotated fields](../docs/llm-prompts/pydantic-annotated-fields.md)

services/web/server/tests/unit/with_dbs/03/login/test_login_auth.py

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
# pylint: disable=unused-argument
33
# pylint: disable=unused-variable
44

5+
import asyncio
56
import json
67
import time
8+
from collections.abc import AsyncIterator, Callable
9+
from contextlib import AsyncExitStack
710
from http import HTTPStatus
811

912
import pytest
10-
from aiohttp.test_utils import TestClient
13+
from aiohttp.test_utils import TestClient, TestServer
1114
from cryptography import fernet
1215
from faker import Faker
1316
from pytest_simcore.helpers.assert_checks import assert_status
@@ -198,3 +201,73 @@ def _build_proxy_session_cookie(identity: str):
198201

199202
if not error:
200203
assert data["login"] == user["email"]
204+
205+
206+
@pytest.fixture
207+
async def multiple_users(
208+
client: TestClient, num_users: int = 5
209+
) -> AsyncIterator[list[dict[str, str]]]:
210+
"""Fixture that creates multiple test users with an AsyncExitStack for cleanup."""
211+
async with AsyncExitStack() as exit_stack:
212+
users = []
213+
for _ in range(num_users):
214+
# Use enter_async_context to properly register each NewUser context manager
215+
user_ctx = await exit_stack.enter_async_context(NewUser(app=client.app))
216+
users.append(
217+
{
218+
"email": user_ctx["email"],
219+
"password": user_ctx["raw_password"],
220+
}
221+
)
222+
223+
yield users
224+
# AsyncExitStack will automatically clean up all users when exiting
225+
226+
227+
async def test_multiple_users_login_logout_concurrently(
228+
web_server: TestServer,
229+
client: TestClient,
230+
multiple_users: list[dict[str, str]],
231+
aiohttp_client: Callable,
232+
):
233+
"""Test multiple users can login concurrently and properly get logged out."""
234+
assert client.app
235+
236+
# URLs
237+
login_url = client.app.router["auth_login"].url_for().path
238+
profile_url = client.app.router["get_my_profile"].url_for().path
239+
logout_url = client.app.router["auth_logout"].url_for().path
240+
241+
async def user_session_flow(user_creds):
242+
# Create a new client for each user to ensure isolated sessions
243+
user_client = await aiohttp_client(web_server)
244+
245+
# Login
246+
login_resp = await user_client.post(
247+
login_url,
248+
json={"email": user_creds["email"], "password": user_creds["password"]},
249+
)
250+
login_data, _ = await assert_status(login_resp, status.HTTP_200_OK)
251+
assert MSG_LOGGED_IN in login_data["message"]
252+
253+
# Access profile (cookies are automatically sent by the client)
254+
profile_resp = await user_client.get(profile_url)
255+
profile_data, _ = await assert_status(profile_resp, status.HTTP_200_OK)
256+
assert profile_data["login"] == user_creds["email"]
257+
258+
# Logout
259+
logout_resp = await user_client.post(logout_url)
260+
await assert_status(logout_resp, status.HTTP_200_OK)
261+
262+
# Try to access profile after logout
263+
profile_after_logout_resp = await user_client.get(profile_url)
264+
_, error = await assert_status(
265+
profile_after_logout_resp, status.HTTP_401_UNAUTHORIZED
266+
)
267+
268+
# No need to manually close the client as aiohttp_client fixture handles cleanup
269+
270+
await user_session_flow(multiple_users[0])
271+
272+
# Run all user flows concurrently
273+
await asyncio.gather(*(user_session_flow(user) for user in multiple_users))

0 commit comments

Comments
 (0)