diff --git a/industry_game/handlers/games/lobby/read_lobby.py b/industry_game/handlers/games/lobby/read_lobby.py index 5c5b585..31c86e9 100644 --- a/industry_game/handlers/games/lobby/read_lobby.py +++ b/industry_game/handlers/games/lobby/read_lobby.py @@ -1,4 +1,4 @@ -from aiohttp.web import HTTPNotFound, Response, View +from aiohttp.web import Response, View from industry_game.utils.http.auth.base import ( AuthMixin, @@ -7,6 +7,7 @@ from industry_game.utils.http.deps import DependenciesMixin from industry_game.utils.http.params import parse_path_param from industry_game.utils.http.response import msgspec_json_response +from industry_game.utils.lobby.models import LobbyStatus, LobbyStatusType class ReadGameUserLobbyHandler(View, DependenciesMixin, AuthMixin): @@ -14,7 +15,12 @@ class ReadGameUserLobbyHandler(View, DependenciesMixin, AuthMixin): async def get(self) -> Response: game_id = parse_path_param(self.request, "game_id", int) - game = await self.game_storage.read_by_id(game_id=game_id) - if game is None: - raise HTTPNotFound - return msgspec_json_response(game) + lobby = await self.lobby_storage.read_by_id( + game_id=game_id, + user_id=self.user.id, + ) + if lobby is None: + status = LobbyStatusType.NOT_CHECKED_IN + else: + status = LobbyStatusType.CHECKED_IN + return msgspec_json_response(LobbyStatus(status=status)) diff --git a/industry_game/utils/lobby/models.py b/industry_game/utils/lobby/models.py index 892647c..6935446 100644 --- a/industry_game/utils/lobby/models.py +++ b/industry_game/utils/lobby/models.py @@ -1,3 +1,5 @@ +from enum import StrEnum, unique + import msgspec from industry_game.db.models import UserGameLobby as UserGameLobbyDb @@ -20,3 +22,13 @@ def from_model(cls, obj: UserGameLobbyDb) -> "Lobby": class LobbyPagination(msgspec.Struct, frozen=True): meta: MetaPagination items: list[ShortUser] + + +@unique +class LobbyStatusType(StrEnum): + CHECKED_IN = "CHECKED_IN" + NOT_CHECKED_IN = "NOT_CHECKED_IN" + + +class LobbyStatus(msgspec.Struct, frozen=True): + status: StrEnum diff --git a/industry_game/utils/users/base.py b/industry_game/utils/users/base.py index 8071cf3..f22cfe5 100644 --- a/industry_game/utils/users/base.py +++ b/industry_game/utils/users/base.py @@ -1,6 +1,6 @@ from enum import StrEnum -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel, ConfigDict, Field from industry_game.utils.msgspec import CustomStruct @@ -11,8 +11,10 @@ class UserType(StrEnum): class RegisterPlayerModel(BaseModel): - username: str - password: str + username: str = Field(min_length=8) + password: str = Field(min_length=8) + telegram: str = Field(min_length=2) + name: str = Field(min_length=5) class AuthUserModel(BaseModel): diff --git a/industry_game/utils/users/models.py b/industry_game/utils/users/models.py index a6492f4..f1866d1 100644 --- a/industry_game/utils/users/models.py +++ b/industry_game/utils/users/models.py @@ -29,6 +29,8 @@ class FullUser(CustomStruct, frozen=True): id: int type: UserType username: str + telegram: str + name: str @classmethod def from_model(self, obj: UserDb) -> "FullUser": @@ -36,4 +38,6 @@ def from_model(self, obj: UserDb) -> "FullUser": id=obj.id, type=obj.type, username=obj.username, + telegram=obj.properties.get("telegram", ""), + name=obj.properties.get("name", ""), ) diff --git a/industry_game/utils/users/processor.py b/industry_game/utils/users/processor.py index 487f8d8..103e414 100644 --- a/industry_game/utils/users/processor.py +++ b/industry_game/utils/users/processor.py @@ -28,6 +28,10 @@ async def register(self, player: RegisterPlayerModel) -> AuthToken: user = await self.player_storage.create( username=player.username, password_hash=self.passgen.hashpw(player.password), + properties={ + "telegram": player.telegram, + "name": player.name, + }, ) token = self.authorization_provider.generate_token(user=user) return AuthToken(token=token) diff --git a/industry_game/utils/users/storage.py b/industry_game/utils/users/storage.py index a028449..4b5e9ec 100644 --- a/industry_game/utils/users/storage.py +++ b/industry_game/utils/users/storage.py @@ -1,4 +1,5 @@ import asyncio +from collections.abc import Mapping from sqlalchemy import func, insert, select from sqlalchemy.ext.asyncio import AsyncSession @@ -18,6 +19,7 @@ async def create( *, username: str, password_hash: str, + properties: Mapping[str, str], commit: bool = True, ) -> FullUser: stmt = ( @@ -25,6 +27,7 @@ async def create( .values( username=username, password_hash=password_hash, + properties=properties, ) .returning(UserDb) ) @@ -50,10 +53,7 @@ async def read_by_username( session: AsyncSession, username: str, ) -> FullUser | None: - stmt = select(UserDb).where( - UserDb.type == UserType.PLAYER, - UserDb.username == username, - ) + stmt = select(UserDb).where(UserDb.username == username) obj = (await session.scalars(stmt)).first() return FullUser.from_model(obj) if obj else None diff --git a/tests/test_api/__init__.py b/tests/api/__init__.py similarity index 100% rename from tests/test_api/__init__.py rename to tests/api/__init__.py diff --git a/tests/test_api/test_games/__init__.py b/tests/api/games/__init__.py similarity index 100% rename from tests/test_api/test_games/__init__.py rename to tests/api/games/__init__.py diff --git a/tests/test_api/test_games/test_game_create.py b/tests/api/games/test_game_create.py similarity index 100% rename from tests/test_api/test_games/test_game_create.py rename to tests/api/games/test_game_create.py diff --git a/tests/test_api/test_games/test_game_details.py b/tests/api/games/test_game_details.py similarity index 100% rename from tests/test_api/test_games/test_game_details.py rename to tests/api/games/test_game_details.py diff --git a/tests/test_api/test_games/test_game_list.py b/tests/api/games/test_game_list.py similarity index 100% rename from tests/test_api/test_games/test_game_list.py rename to tests/api/games/test_game_list.py diff --git a/tests/test_api/test_games/test_lobby/__init__.py b/tests/api/games/test_lobby/__init__.py similarity index 100% rename from tests/test_api/test_games/test_lobby/__init__.py rename to tests/api/games/test_lobby/__init__.py diff --git a/tests/test_api/test_players/__init__.py b/tests/api/players/__init__.py similarity index 100% rename from tests/test_api/test_players/__init__.py rename to tests/api/players/__init__.py diff --git a/tests/test_api/test_players/test_login_player.py b/tests/api/players/test_player_login.py similarity index 100% rename from tests/test_api/test_players/test_login_player.py rename to tests/api/players/test_player_login.py diff --git a/tests/api/players/test_player_register.py b/tests/api/players/test_player_register.py new file mode 100644 index 0000000..4a53d8b --- /dev/null +++ b/tests/api/players/test_player_register.py @@ -0,0 +1,66 @@ +from http import HTTPStatus + +import pytest +from aiohttp.test_utils import TestClient +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession +from yarl import URL + +from industry_game.db.models import User +from industry_game.utils.users.base import UserType + +API_URL = URL("/api/v1/players/register/") + + +async def test_player_register_successful_status_created( + api_client: TestClient, +): + response = await api_client.post( + API_URL, + json={ + "username": "username", + "password": "password", + "telegram": "telegram", + "name": "your name", + }, + ) + assert response.status == HTTPStatus.CREATED + + +async def test_player_register_successful_check_db( + api_client: TestClient, + session: AsyncSession, +): + await api_client.post( + API_URL, + json={ + "username": "username", + "password": "password", + "telegram": "telegram", + "name": "your name", + }, + ) + user = ( + await session.scalars(select(User).where(User.username == "username")) + ).one() + + assert user.username == "username" + assert user.type == UserType.PLAYER + + +@pytest.mark.parametrize("user_type", (UserType.ADMIN, UserType.PLAYER)) +async def test_player_register_same_username_error_conflict( + api_client: TestClient, create_user, user_type +): + user = await create_user(type=user_type) + + response = await api_client.post( + API_URL, + json={ + "username": user.username, + "password": "password", + "telegram": "telegram", + "name": "your name", + }, + ) + assert response.status == HTTPStatus.BAD_REQUEST diff --git a/tests/test_api/test_ping.py b/tests/api/test_ping.py similarity index 100% rename from tests/test_api/test_ping.py rename to tests/api/test_ping.py diff --git a/tests/test_db/__init__.py b/tests/db/__init__.py similarity index 100% rename from tests/test_db/__init__.py rename to tests/db/__init__.py diff --git a/tests/test_db/test_migrations.py b/tests/db/test_migrations.py similarity index 100% rename from tests/test_db/test_migrations.py rename to tests/db/test_migrations.py