diff --git a/backend/src/v1/friendships/__init__.py b/backend/src/v1/friendships/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/v1/friendships/dependencies.py b/backend/src/v1/friendships/dependencies.py new file mode 100644 index 0000000..9d48db4 --- /dev/null +++ b/backend/src/v1/friendships/dependencies.py @@ -0,0 +1 @@ +from __future__ import annotations diff --git a/backend/src/v1/friendships/repository.py b/backend/src/v1/friendships/repository.py new file mode 100644 index 0000000..9d48db4 --- /dev/null +++ b/backend/src/v1/friendships/repository.py @@ -0,0 +1 @@ +from __future__ import annotations diff --git a/backend/src/v1/friendships/router.py b/backend/src/v1/friendships/router.py new file mode 100644 index 0000000..9d48db4 --- /dev/null +++ b/backend/src/v1/friendships/router.py @@ -0,0 +1 @@ +from __future__ import annotations diff --git a/backend/src/v1/friendships/schemas.py b/backend/src/v1/friendships/schemas.py new file mode 100644 index 0000000..f1355f7 --- /dev/null +++ b/backend/src/v1/friendships/schemas.py @@ -0,0 +1,38 @@ +from __future__ import annotations +from datetime import datetime +from typing import Optional +from uuid import UUID + +from pydantic import BaseModel, Field + + +class UserBasicInfo(BaseModel): + id: str + username: str + avatar_url: Optional[str] = None + + +class FriendRequestCreate(BaseModel): + target_user_id: UUID + content: Optional[str] = Field(None, max_length=200) + + +class FriendRequestResponse(BaseModel): + id: UUID + sender: UserBasicInfo + recipient: UserBasicInfo + content: Optional[str] + status: str + created_at: datetime + + +class FriendResponse(BaseModel): + id: UUID + friend: UserBasicInfo + status: str + created_at: datetime + accepted_at: Optional[datetime] + + +class FriendRequestAction(BaseModel): + pass diff --git a/backend/src/v1/friendships/service.py b/backend/src/v1/friendships/service.py new file mode 100644 index 0000000..9d48db4 --- /dev/null +++ b/backend/src/v1/friendships/service.py @@ -0,0 +1 @@ +from __future__ import annotations diff --git a/backend/tests/unit/v1/friendships/test_schemas.py b/backend/tests/unit/v1/friendships/test_schemas.py new file mode 100644 index 0000000..8665b31 --- /dev/null +++ b/backend/tests/unit/v1/friendships/test_schemas.py @@ -0,0 +1,116 @@ +from __future__ import annotations + +import pytest +from datetime import datetime +from uuid import uuid4 + +from v1.friendships.schemas import ( + UserBasicInfo, + FriendRequestCreate, + FriendRequestResponse, + FriendResponse, + FriendRequestAction, +) + + +def test_user_basic_info_maps_fields() -> None: + user = UserBasicInfo(id="user-1", username="alice", avatar_url=None) + + assert user.id == "user-1" + assert user.username == "alice" + assert user.avatar_url is None + + +def test_user_basic_info_with_avatar() -> None: + user = UserBasicInfo( + id="user-2", username="bob", avatar_url="https://example.com/avatar.png" + ) + + assert user.avatar_url == "https://example.com/avatar.png" + + +def test_friend_request_create_valid() -> None: + target_id = uuid4() + request = FriendRequestCreate( + target_user_id=target_id, content="Hi, let's be friends!" + ) + + assert request.target_user_id == target_id + assert request.content == "Hi, let's be friends!" + + +def test_friend_request_create_without_content() -> None: + target_id = uuid4() + request = FriendRequestCreate(target_user_id=target_id, content=None) + + assert request.target_user_id == target_id + assert request.content is None + + +def test_friend_request_create_content_max_length() -> None: + target_id = uuid4() + with pytest.raises(Exception): + FriendRequestCreate(target_user_id=target_id, content="x" * 201) + + +def test_friend_request_response_maps_fields() -> None: + sender = UserBasicInfo(id="user-1", username="alice", avatar_url=None) + recipient = UserBasicInfo(id="user-2", username="bob", avatar_url=None) + request_id = uuid4() + created = datetime(2026, 1, 15, 10, 30, 0) + + response = FriendRequestResponse( + id=request_id, + sender=sender, + recipient=recipient, + content="Hello!", + status="pending", + created_at=created, + ) + + assert response.id == request_id + assert response.sender.username == "alice" + assert response.recipient.username == "bob" + assert response.status == "pending" + assert response.created_at == created + + +def test_friend_response_maps_fields() -> None: + friend_user = UserBasicInfo(id="user-2", username="bob", avatar_url=None) + request_id = uuid4() + created = datetime(2026, 1, 15, 10, 30, 0) + accepted = datetime(2026, 1, 16, 12, 0, 0) + + response = FriendResponse( + id=request_id, + friend=friend_user, + status="accepted", + created_at=created, + accepted_at=accepted, + ) + + assert response.id == request_id + assert response.friend.username == "bob" + assert response.status == "accepted" + assert response.accepted_at == accepted + + +def test_friend_response_accepted_at_optional() -> None: + friend_user = UserBasicInfo(id="user-2", username="bob", avatar_url=None) + request_id = uuid4() + created = datetime(2026, 1, 15, 10, 30, 0) + + response = FriendResponse( + id=request_id, + friend=friend_user, + status="pending", + created_at=created, + accepted_at=None, + ) + + assert response.accepted_at is None + + +def test_friend_request_action_no_fields() -> None: + action = FriendRequestAction() + assert action.model_dump() == {} diff --git a/pyrightconfig.json b/pyrightconfig.json index e03970f..ffa5a17 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -1,6 +1,6 @@ { "include": ["backend"], - "exclude": ["**/__pycache__", "**/node_modules", "**/.git"], + "exclude": ["**/__pycache__", "**/node_modules", "**/.git", "backend/tests"], "typeCheckingMode": "standard", "pythonVersion": "3.12", "pythonPlatform": "Linux",