Skip to content

Commit 228dc9c

Browse files
authored
Merge branch 'master' into is34/fix-catalog-service-inherit-group-1
2 parents 348f42a + aa71ed3 commit 228dc9c

File tree

106 files changed

+822
-521
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+822
-521
lines changed

packages/celery-library/tests/conftest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232

3333
class FakeAppServer(BaseAppServer):
3434
async def lifespan(self, startup_completed_event: threading.Event) -> None:
35-
pass
35+
startup_completed_event.set()
36+
await self.shutdown_event.wait() # wait for shutdown
3637

3738

3839
@pytest.fixture

packages/pytest-simcore/src/pytest_simcore/helpers/webserver_login.py

Lines changed: 15 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,21 @@
11
import contextlib
22
import re
33
from collections.abc import AsyncIterator
4-
from datetime import datetime
5-
from typing import Any, TypedDict
4+
from typing import Any
65

7-
from aiohttp import web
86
from aiohttp.test_utils import TestClient
9-
from models_library.users import UserID
107
from servicelib.aiohttp import status
11-
from simcore_service_webserver.db.models import UserRole, UserStatus
12-
from simcore_service_webserver.groups.api import auto_add_user_to_product_group
138
from simcore_service_webserver.login._constants import MSG_LOGGED_IN
149
from simcore_service_webserver.login._invitations_service import create_invitation_token
1510
from simcore_service_webserver.login._login_repository_legacy import (
16-
AsyncpgStorage,
1711
get_plugin_storage,
1812
)
19-
from simcore_service_webserver.products.products_service import list_products
2013
from simcore_service_webserver.security import security_service
2114
from yarl import URL
2215

2316
from .assert_checks import assert_status
24-
from .faker_factories import DEFAULT_FAKER, DEFAULT_TEST_PASSWORD, random_user
25-
26-
27-
# WARNING: DO NOT use UserDict is already in https://docs.python.org/3/library/collections.html#collections.UserDictclass UserRowDict(TypedDict):
28-
# NOTE: this is modified dict version of packages/postgres-database/src/simcore_postgres_database/models/users.py for testing purposes
29-
class _UserInfoDictRequired(TypedDict, total=True):
30-
id: int
31-
name: str
32-
email: str
33-
primary_gid: str
34-
raw_password: str
35-
status: UserStatus
36-
role: UserRole
37-
38-
39-
class UserInfoDict(_UserInfoDictRequired, total=False):
40-
created_at: datetime
41-
password_hash: str
42-
first_name: str
43-
last_name: str
44-
phone: str
45-
17+
from .faker_factories import DEFAULT_FAKER
18+
from .webserver_users import NewUser, UserInfoDict, _create_account_in_db
4619

4720
TEST_MARKS = re.compile(r"TEST (\w+):(.*)")
4821

@@ -65,76 +38,21 @@ def parse_link(text):
6538
return URL(link).path
6639

6740

68-
async def _create_user(app: web.Application, data=None) -> UserInfoDict:
69-
db: AsyncpgStorage = get_plugin_storage(app)
70-
71-
# create
72-
data = data or {}
73-
data.setdefault("status", UserStatus.ACTIVE.name)
74-
data.setdefault("role", UserRole.USER.name)
75-
data.setdefault("password", DEFAULT_TEST_PASSWORD)
76-
user = await db.create_user(random_user(**data))
77-
78-
# get
79-
user = await db.get_user({"id": user["id"]})
80-
assert "first_name" in user
81-
assert "last_name" in user
82-
83-
# adds extras
84-
extras = {"raw_password": data["password"]}
85-
86-
return UserInfoDict(
87-
**{
88-
key: user[key]
89-
for key in [
90-
"id",
91-
"name",
92-
"email",
93-
"primary_gid",
94-
"status",
95-
"role",
96-
"created_at",
97-
"password_hash",
98-
"first_name",
99-
"last_name",
100-
"phone",
101-
]
102-
},
103-
**extras,
104-
)
105-
106-
107-
async def _register_user_in_default_product(app: web.Application, user_id: UserID):
108-
products = list_products(app)
109-
assert products
110-
product_name = products[0].name
111-
112-
return await auto_add_user_to_product_group(app, user_id, product_name=product_name)
113-
114-
115-
async def _create_account(
116-
app: web.Application,
117-
user_data: dict[str, Any] | None = None,
118-
) -> UserInfoDict:
119-
# users, groups in db
120-
user = await _create_user(app, user_data)
121-
# user has default product
122-
await _register_user_in_default_product(app, user_id=user["id"])
123-
return user
124-
125-
12641
async def log_client_in(
12742
client: TestClient,
12843
user_data: dict[str, Any] | None = None,
12944
*,
130-
enable_check=True,
45+
exit_stack: contextlib.AsyncExitStack,
46+
enable_check: bool = True,
13147
) -> UserInfoDict:
13248
assert client.app
13349

13450
# create account
135-
user = await _create_account(client.app, user_data=user_data)
51+
user = await _create_account_in_db(
52+
client.app, exit_stack=exit_stack, user_data=user_data
53+
)
13654

137-
# login
55+
# login (requires)
13856
url = client.app.router["auth_login"].url_for()
13957
reponse = await client.post(
14058
str(url),
@@ -150,26 +68,6 @@ async def log_client_in(
15068
return user
15169

15270

153-
class NewUser:
154-
def __init__(
155-
self,
156-
user_data: dict[str, Any] | None = None,
157-
app: web.Application | None = None,
158-
):
159-
self.user_data = user_data
160-
self.user = None
161-
assert app
162-
self.db = get_plugin_storage(app)
163-
self.app = app
164-
165-
async def __aenter__(self) -> UserInfoDict:
166-
self.user = await _create_account(self.app, self.user_data)
167-
return self.user
168-
169-
async def __aexit__(self, *args):
170-
await self.db.delete_user(self.user)
171-
172-
17371
class LoggedUser(NewUser):
17472
def __init__(self, client: TestClient, user_data=None, *, check_if_succeeds=True):
17573
super().__init__(user_data, client.app)
@@ -179,7 +77,10 @@ def __init__(self, client: TestClient, user_data=None, *, check_if_succeeds=True
17977

18078
async def __aenter__(self) -> UserInfoDict:
18179
self.user = await log_client_in(
182-
self.client, self.user_data, enable_check=self.enable_check
80+
self.client,
81+
self.user_data,
82+
exit_stack=self.exit_stack,
83+
enable_check=self.enable_check,
18384
)
18485
return self.user
18586

@@ -231,11 +132,12 @@ def __init__(
231132
self.confirmation = None
232133
self.trial_days = trial_days
233134
self.extra_credits_in_usd = extra_credits_in_usd
135+
self.db = get_plugin_storage(self.app)
234136

235137
async def __aenter__(self) -> "NewInvitation":
236138
# creates host user
237139
assert self.client.app
238-
self.user = await _create_user(self.client.app, self.user_data)
140+
self.user = await super().__aenter__()
239141

240142
self.confirmation = await create_invitation_token(
241143
self.db,
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import contextlib
2+
from datetime import datetime
3+
from typing import Any, TypedDict
4+
5+
from aiohttp import web
6+
from models_library.users import UserID
7+
from simcore_postgres_database.models.users import users as users_table
8+
from simcore_service_webserver.db.models import UserRole, UserStatus
9+
from simcore_service_webserver.db.plugin import get_asyncpg_engine
10+
from simcore_service_webserver.groups import api as groups_service
11+
from simcore_service_webserver.products.products_service import list_products
12+
from sqlalchemy.ext.asyncio import AsyncEngine
13+
14+
from .faker_factories import DEFAULT_TEST_PASSWORD, random_user
15+
from .postgres_tools import insert_and_get_row_lifespan
16+
17+
18+
# WARNING: DO NOT use UserDict is already in https://docs.python.org/3/library/collections.html#collections.UserDictclass UserRowDict(TypedDict):
19+
# NOTE: this is modified dict version of packages/postgres-database/src/simcore_postgres_database/models/users.py for testing purposes
20+
class _UserInfoDictRequired(TypedDict, total=True):
21+
id: int
22+
name: str
23+
email: str
24+
primary_gid: str
25+
raw_password: str
26+
status: UserStatus
27+
role: UserRole
28+
29+
30+
class UserInfoDict(_UserInfoDictRequired, total=False):
31+
created_at: datetime
32+
password_hash: str
33+
first_name: str
34+
last_name: str
35+
phone: str
36+
37+
38+
async def _create_user_in_db(
39+
sqlalchemy_async_engine: AsyncEngine,
40+
exit_stack: contextlib.AsyncExitStack,
41+
data: dict | None = None,
42+
) -> UserInfoDict:
43+
44+
# create fake
45+
data = data or {}
46+
data.setdefault("status", UserStatus.ACTIVE.name)
47+
data.setdefault("role", UserRole.USER.name)
48+
data.setdefault("password", DEFAULT_TEST_PASSWORD)
49+
50+
raw_password = data["password"]
51+
52+
# inject in db
53+
user = await exit_stack.enter_async_context(
54+
insert_and_get_row_lifespan( # pylint:disable=contextmanager-generator-missing-cleanup
55+
sqlalchemy_async_engine,
56+
table=users_table,
57+
values=random_user(**data),
58+
pk_col=users_table.c.id,
59+
)
60+
)
61+
assert "first_name" in user
62+
assert "last_name" in user
63+
64+
return UserInfoDict(
65+
# required
66+
# - in db
67+
id=user["id"],
68+
name=user["name"],
69+
email=user["email"],
70+
primary_gid=user["primary_gid"],
71+
status=(
72+
UserStatus(user["status"])
73+
if not isinstance(user["status"], UserStatus)
74+
else user["status"]
75+
),
76+
role=(
77+
UserRole(user["role"])
78+
if not isinstance(user["role"], UserRole)
79+
else user["role"]
80+
),
81+
# optional
82+
# - in db
83+
created_at=(
84+
user["created_at"]
85+
if isinstance(user["created_at"], datetime)
86+
else datetime.fromisoformat(user["created_at"])
87+
),
88+
password_hash=user["password_hash"],
89+
first_name=user["first_name"],
90+
last_name=user["last_name"],
91+
phone=user["phone"],
92+
# extras
93+
raw_password=raw_password,
94+
)
95+
96+
97+
async def _register_user_in_default_product(app: web.Application, user_id: UserID):
98+
products = list_products(app)
99+
assert products
100+
product_name = products[0].name
101+
102+
return await groups_service.auto_add_user_to_product_group(
103+
app, user_id, product_name=product_name
104+
)
105+
106+
107+
async def _create_account_in_db(
108+
app: web.Application,
109+
exit_stack: contextlib.AsyncExitStack,
110+
user_data: dict[str, Any] | None = None,
111+
) -> UserInfoDict:
112+
# users, groups in db
113+
user = await _create_user_in_db(
114+
get_asyncpg_engine(app), exit_stack=exit_stack, data=user_data
115+
)
116+
117+
# user has default product
118+
await _register_user_in_default_product(app, user_id=user["id"])
119+
return user
120+
121+
122+
class NewUser:
123+
def __init__(
124+
self,
125+
user_data: dict[str, Any] | None = None,
126+
app: web.Application | None = None,
127+
):
128+
self.user_data = user_data
129+
self.user = None
130+
131+
assert app
132+
self.app = app
133+
134+
self.exit_stack = contextlib.AsyncExitStack()
135+
136+
async def __aenter__(self) -> UserInfoDict:
137+
self.user = await _create_account_in_db(
138+
self.app, self.exit_stack, self.user_data
139+
)
140+
return self.user
141+
142+
async def __aexit__(self, *args):
143+
await self.exit_stack.aclose()

packages/pytest-simcore/src/pytest_simcore/simcore_webserver_groups_fixtures.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
# pylint: disable=unused-variable
44
"""
55
6-
Fixtures for groups
6+
Fixtures for groups
77
8-
NOTE: These fixtures are used in integration and unit tests
8+
NOTE: These fixtures are used in integration and unit tests
99
"""
1010

1111

@@ -18,7 +18,7 @@
1818
from models_library.api_schemas_webserver.groups import GroupGet
1919
from models_library.groups import GroupsByTypeTuple, StandardGroupCreate
2020
from models_library.users import UserID
21-
from pytest_simcore.helpers.webserver_login import NewUser, UserInfoDict
21+
from pytest_simcore.helpers.webserver_users import NewUser, UserInfoDict
2222
from simcore_service_webserver.groups._groups_service import (
2323
add_user_in_group,
2424
create_standard_group,

0 commit comments

Comments
 (0)