-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added a User base model + BaseUserRepository + User actions (#6)
* Added a User base model + BaseUserRepository + User actions (create, list, retrieve) + tests * Added user delete action and repository action + tests * Fixing Black issues * Making code async * running iSort
- Loading branch information
Showing
8 changed files
with
263 additions
and
2 deletions.
There are no files selected for viewing
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
from collections.abc import Collection | ||
|
||
from meldingen_core.models import User | ||
from meldingen_core.repositories import BaseUserRepository | ||
|
||
|
||
class UserCreateAction: | ||
"""Action that stores a user.""" | ||
|
||
repository: BaseUserRepository | ||
|
||
def __init__(self, repository: BaseUserRepository): | ||
self.repository = repository | ||
|
||
async def __call__(self, user: User) -> None: | ||
await self.repository.add(user) | ||
|
||
|
||
class UserListAction: | ||
"""Action that retrieves a list of users.""" | ||
|
||
repository: BaseUserRepository | ||
|
||
def __init__(self, repository: BaseUserRepository): | ||
self.repository = repository | ||
|
||
async def __call__(self, *, limit: int | None = None, offset: int | None = None) -> Collection[User]: | ||
return await self.repository.list(limit=limit, offset=offset) | ||
|
||
|
||
class UserRetrieveAction: | ||
"""Action that retrieves a user.""" | ||
|
||
repository: BaseUserRepository | ||
|
||
def __init__(self, repository: BaseUserRepository): | ||
self.repository = repository | ||
|
||
async def __call__(self, pk: int) -> User | None: | ||
return await self.repository.retrieve(pk=pk) | ||
|
||
|
||
class UserDeleteAction: | ||
"""Action that deletes a user.""" | ||
|
||
repository: BaseUserRepository | ||
|
||
def __init__(self, repository: BaseUserRepository): | ||
self.repository = repository | ||
|
||
async def __call__(self, pk: int) -> None: | ||
await self.repository.delete(pk=pk) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
2 changes: 1 addition & 1 deletion
2
tests/test_meldingen/test_actions.py → tests/test_actions/test_melding_actions.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
import pytest | ||
import pytest_asyncio | ||
|
||
from meldingen_core.actions.user import UserCreateAction, UserDeleteAction, UserListAction, UserRetrieveAction | ||
from meldingen_core.models import User | ||
from meldingen_core.repositories import BaseUserRepository | ||
|
||
# Fixtures | ||
|
||
|
||
@pytest.fixture | ||
def unpopulated_users_repository() -> BaseUserRepository: | ||
class TestUserRepository(BaseUserRepository): | ||
data: list[User] | ||
|
||
def __init__(self) -> None: | ||
self.data = [] | ||
|
||
async def add(self, user: User) -> None: | ||
self.data.append(user) | ||
|
||
async def list(self, *, limit: int | None = None, offset: int | None = None) -> list[User]: | ||
if limit and offset: | ||
return self.data[offset : offset + limit] | ||
elif limit and not offset: | ||
return self.data[:limit] | ||
elif not limit and offset: | ||
return self.data[offset:] | ||
else: | ||
return self.data | ||
|
||
async def retrieve(self, pk: int) -> User | None: | ||
for _user in self.data: | ||
if _user.username == str(pk): | ||
return _user | ||
return None | ||
|
||
async def find_by_email(self, email: str) -> User | None: | ||
for _user in self.data: | ||
if _user.email == email: | ||
return _user | ||
return None | ||
|
||
async def delete(self, pk: int) -> None: | ||
for _user in self.data[:]: | ||
if _user.username == str(pk): | ||
self.data.remove(_user) | ||
return None | ||
|
||
repository = TestUserRepository() | ||
return repository | ||
|
||
|
||
@pytest_asyncio.fixture | ||
async def populated_users_repository( | ||
unpopulated_users_repository: BaseUserRepository, | ||
) -> BaseUserRepository: | ||
for _pk in range(10): | ||
user = User() | ||
user.username = f"{_pk}" | ||
user.email = f"user-{_pk}@example.com" | ||
await unpopulated_users_repository.add(user) | ||
|
||
return unpopulated_users_repository | ||
|
||
|
||
@pytest.fixture | ||
def users_create_action( | ||
unpopulated_users_repository: BaseUserRepository, | ||
) -> UserCreateAction: | ||
return UserCreateAction(unpopulated_users_repository) | ||
|
||
|
||
@pytest.fixture | ||
def users_list_action( | ||
populated_users_repository: BaseUserRepository, | ||
) -> UserListAction: | ||
return UserListAction(populated_users_repository) | ||
|
||
|
||
@pytest.fixture | ||
def users_retrieve_action( | ||
populated_users_repository: BaseUserRepository, | ||
) -> UserRetrieveAction: | ||
return UserRetrieveAction(populated_users_repository) | ||
|
||
|
||
@pytest.fixture | ||
def users_delete_action( | ||
populated_users_repository: BaseUserRepository, | ||
) -> UserDeleteAction: | ||
return UserDeleteAction(populated_users_repository) | ||
|
||
|
||
# PyTest Classes | ||
|
||
|
||
class TestUserCreateAction: | ||
@pytest.mark.asyncio | ||
async def test_add(self, users_create_action: UserCreateAction) -> None: | ||
assert len(await users_create_action.repository.list()) == 0 | ||
|
||
user = User() | ||
user.username = "1" | ||
|
||
await users_create_action(user) | ||
|
||
assert len(await users_create_action.repository.list()) == 1 | ||
assert await users_create_action.repository.retrieve(pk=1) == user | ||
|
||
|
||
class TestUserListAction: | ||
@pytest.mark.asyncio | ||
async def test_list_all(self, users_list_action: UserListAction) -> None: | ||
users = await users_list_action() | ||
|
||
assert len(users) == 10 | ||
|
||
@pytest.mark.parametrize( | ||
"limit, expected_result", | ||
[(1, 1), (5, 5), (10, 10), (20, 10)], | ||
) | ||
@pytest.mark.asyncio | ||
async def test_list_limit(self, users_list_action: UserListAction, limit: int, expected_result: int) -> None: | ||
users = await users_list_action(limit=limit) | ||
|
||
assert len(users) == expected_result | ||
|
||
@pytest.mark.parametrize("offset, expected_result", [(1, 9), (5, 5), (10, 0), (20, 0)]) | ||
@pytest.mark.asyncio | ||
async def test_list_offset( | ||
self, | ||
users_list_action: UserListAction, | ||
offset: int, | ||
expected_result: int, | ||
) -> None: | ||
users = await users_list_action(offset=offset) | ||
|
||
assert len(users) == expected_result | ||
|
||
@pytest.mark.parametrize( | ||
"limit, offset, expected_result", | ||
[(10, 0, 10), (5, 0, 5), (10, 10, 0), (20, 0, 10)], | ||
) | ||
@pytest.mark.asyncio | ||
async def test_list_limit_offset( | ||
self, | ||
users_list_action: UserListAction, | ||
limit: int, | ||
offset: int, | ||
expected_result: int, | ||
) -> None: | ||
users = await users_list_action(limit=limit, offset=offset) | ||
|
||
assert len(users) == expected_result | ||
|
||
|
||
class TestUserRetrieveAction: | ||
@pytest.mark.parametrize("pk", [1, 2, 3, 4, 5]) | ||
@pytest.mark.asyncio | ||
async def test_retrieve_existing_users(self, users_retrieve_action: UserRetrieveAction, pk: int) -> None: | ||
user = await users_retrieve_action(pk=pk) | ||
|
||
assert user is not None | ||
assert user.username == str(pk) | ||
|
||
@pytest.mark.parametrize("pk", [101, 102, 103, 104, 105]) | ||
@pytest.mark.asyncio | ||
async def test_retrieve_non_existing_users(self, users_retrieve_action: UserRetrieveAction, pk: int) -> None: | ||
user = await users_retrieve_action(pk=pk) | ||
|
||
assert user is None | ||
|
||
|
||
class TestUserDeleteAction: | ||
@pytest.mark.parametrize("pk", [1, 2, 3, 4, 5]) | ||
@pytest.mark.asyncio | ||
async def test_delete_existing_user(self, users_delete_action: UserDeleteAction, pk: int) -> None: | ||
assert await users_delete_action.repository.retrieve(pk=pk) is not None | ||
assert len(await users_delete_action.repository.list()) == 10 | ||
|
||
await users_delete_action(pk=pk) | ||
|
||
assert await users_delete_action.repository.retrieve(pk=pk) is None | ||
assert len(await users_delete_action.repository.list()) == 9 | ||
|
||
@pytest.mark.parametrize("pk", [101, 102, 103, 104, 105]) | ||
@pytest.mark.asyncio | ||
async def test_delete_non_existing_user(self, users_delete_action: UserDeleteAction, pk: int) -> None: | ||
assert await users_delete_action.repository.retrieve(pk=pk) is None | ||
assert len(await users_delete_action.repository.list()) == 10 | ||
|
||
await users_delete_action(pk=pk) | ||
|
||
assert len(await users_delete_action.repository.list()) == 10 |