-
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.
Add database interface and in-memory database infrastructure
- Loading branch information
Showing
10 changed files
with
165 additions
and
0 deletions.
There are no files selected for viewing
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.
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,32 @@ | ||
from abc import ABC, abstractmethod | ||
from typing import Generic, Protocol, TypeVar, Optional | ||
from uuid import UUID | ||
|
||
|
||
class HasID(Protocol): | ||
id: UUID # Enforce that any entity passed to the repository must have an `id` attribute | ||
|
||
|
||
T = TypeVar("T", bound=HasID) | ||
|
||
|
||
class Database(ABC, Generic[T]): | ||
@abstractmethod | ||
async def add(self, entity: T) -> None: | ||
pass | ||
|
||
@abstractmethod | ||
async def get(self, id: UUID) -> Optional[T]: | ||
pass | ||
|
||
@abstractmethod | ||
async def update(self, entity: T) -> None: | ||
pass | ||
|
||
@abstractmethod | ||
async def delete(self, id: UUID) -> None: | ||
pass | ||
|
||
@abstractmethod | ||
async def list_all(self) -> list[T]: | ||
pass |
Empty file.
Empty file.
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,26 @@ | ||
from typing import Optional | ||
from uuid import UUID | ||
|
||
from application.interfaces.database import T, Database | ||
|
||
|
||
class InMemoryDatabase(Database[T]): | ||
def __init__(self) -> None: | ||
self._data: dict[UUID, T] = {} | ||
|
||
async def add(self, entity: T) -> None: | ||
self._data[entity.id] = entity | ||
|
||
async def get(self, id: UUID) -> Optional[T]: | ||
return self._data.get(id) | ||
|
||
async def update(self, entity: T) -> None: | ||
if entity.id in self._data: | ||
self._data[entity.id] = entity | ||
|
||
async def delete(self, id: UUID) -> None: | ||
if id in self._data: | ||
del self._data[id] | ||
|
||
async def list_all(self) -> list[T]: | ||
return list(self._data.values()) |
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,42 @@ | ||
from uuid import UUID | ||
|
||
from application.entities.user import User | ||
|
||
|
||
def test_user_creation() -> None: | ||
user = User( | ||
name="Alice Smith", email="[email protected]", password="securepassword", confirmed=False | ||
) | ||
|
||
assert user.name == "Alice Smith" | ||
assert user.email == "[email protected]" | ||
assert user.password == "securepassword" | ||
assert user.confirmed is False | ||
assert isinstance(user.id, UUID) | ||
|
||
|
||
def test_user_default_id() -> None: | ||
user1 = User( | ||
name="Alice Smith", email="[email protected]", password="securepassword", confirmed=False | ||
) | ||
user2 = User( | ||
name="Bob Brown", email="[email protected]", password="anothersecurepassword", confirmed=True | ||
) | ||
|
||
assert user1.id != user2.id | ||
|
||
|
||
def test_user_email_format() -> None: | ||
user = User( | ||
name="Alice Smith", email="[email protected]", password="securepassword", confirmed=False | ||
) | ||
|
||
assert "@" in user.email | ||
assert "." in user.email.split("@")[-1] | ||
|
||
|
||
def test_user_confirmation() -> None: | ||
user = User( | ||
name="Alice Smith", email="[email protected]", password="securepassword", confirmed=True | ||
) | ||
assert user.confirmed is True |
Empty file.
Empty file.
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,63 @@ | ||
import pytest | ||
|
||
from application.entities.user import User | ||
from infrastructure.databases.in_memory import InMemoryDatabase | ||
|
||
|
||
@pytest.fixture | ||
def in_memory_db() -> InMemoryDatabase[User]: | ||
return InMemoryDatabase[User]() | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_in_memory_database_operations(in_memory_db: InMemoryDatabase[User]) -> None: | ||
# Test data | ||
user1 = User(name="John Doe", email="[email protected]", password="password123", confirmed=True) | ||
user2 = User( | ||
name="Jane Smith", email="[email protected]", password="password456", confirmed=False | ||
) | ||
|
||
# 1. Add user1 | ||
await in_memory_db.add(user1) | ||
fetched_user1 = await in_memory_db.get(user1.id) | ||
assert fetched_user1 is not None | ||
assert fetched_user1.name == user1.name | ||
assert fetched_user1.email == user1.email | ||
|
||
# 2. Update user1's name | ||
user1.name = "John Doe Updated" | ||
await in_memory_db.update(user1) | ||
updated_user1 = await in_memory_db.get(user1.id) | ||
assert updated_user1 | ||
assert updated_user1.name == "John Doe Updated" | ||
|
||
# 3. Add user2 | ||
await in_memory_db.add(user2) | ||
fetched_user2 = await in_memory_db.get(user2.id) | ||
assert fetched_user2 is not None | ||
assert fetched_user2.name == user2.name | ||
|
||
# 4. List all users | ||
all_users = await in_memory_db.list_all() | ||
assert len(all_users) == 2 | ||
assert user1 in all_users | ||
assert user2 in all_users | ||
|
||
# 5. Delete user1 | ||
await in_memory_db.delete(user1.id) | ||
deleted_user1 = await in_memory_db.get(user1.id) | ||
assert deleted_user1 is None | ||
|
||
# 6. Check remaining users | ||
remaining_users = await in_memory_db.list_all() | ||
assert len(remaining_users) == 1 | ||
assert user2 in remaining_users | ||
assert user1 not in remaining_users | ||
|
||
# 7. Delete user2 | ||
await in_memory_db.delete(user2.id) | ||
deleted_user2 = await in_memory_db.get(user2.id) | ||
assert deleted_user2 is None | ||
|
||
# 8. Final check for empty database | ||
assert await in_memory_db.list_all() == [] |