Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a User base model + BaseUserRepository + User actions #6

Merged
merged 5 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
File renamed without changes.
52 changes: 52 additions & 0 deletions meldingen_core/actions/user.py
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)
7 changes: 7 additions & 0 deletions meldingen_core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,10 @@ class Melding:
"""This is the base model for a 'melding'."""

text: str


class User:
"""This is the base model for a 'user'."""

username: str
email: str
9 changes: 8 additions & 1 deletion meldingen_core/repositories.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from collections.abc import Collection
from typing import Generic, TypeVar

from meldingen_core.models import Melding
from meldingen_core.models import Melding, User

T = TypeVar("T")
T_co = TypeVar("T_co", covariant=True)
Expand All @@ -21,6 +21,13 @@ async def list(self, *, limit: int | None = None, offset: int | None = None) ->
async def retrieve(self, pk: int) -> T_co | None:
...

async def delete(self, pk: int) -> None:
raise NotImplemented


class BaseMeldingRepository(BaseRepository[Melding, Melding], metaclass=ABCMeta):
"""Repository for Melding."""


class BaseUserRepository(BaseRepository[User, User], metaclass=ABCMeta):
"""Repository for User."""
Empty file added tests/test_actions/__init__.py
Empty file.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest
import pytest_asyncio

from meldingen_core.actions import MeldingCreateAction, MeldingListAction, MeldingRetrieveAction
from meldingen_core.actions.melding import MeldingCreateAction, MeldingListAction, MeldingRetrieveAction
from meldingen_core.models import Melding
from meldingen_core.repositories import BaseMeldingRepository

Expand Down
195 changes: 195 additions & 0 deletions tests/test_actions/test_user_actions.py
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