From 1e2393eeeed836ec1a8e3bb3dbf187ad14250aeb Mon Sep 17 00:00:00 2001 From: Gildo Junior <1gildojunior@gmail.com> Date: Mon, 8 Sep 2025 21:05:34 -0300 Subject: [PATCH 1/2] Implement new fields on schema and database model --- app/routers/libraries/routes.py | 5 ++- app/schemas.py | 30 +++++++++++------ app/services/database/models/libraries.py | 6 +++- app/services/database/models/subscriptions.py | 4 +-- tests/test_libraries.py | 32 +++++++++++++++---- tests/test_subscriptions.py | 10 +++--- 6 files changed, 62 insertions(+), 25 deletions(-) diff --git a/app/routers/libraries/routes.py b/app/routers/libraries/routes.py index bec7acc..de8e874 100644 --- a/app/routers/libraries/routes.py +++ b/app/routers/libraries/routes.py @@ -36,8 +36,11 @@ async def create_library( library = Library( library_name=body.library_name, user_email="", # TODO: Considerar obter o email do usuário autenticado - releases_url=body.releases_url.encoded_string(), logo=body.logo.encoded_string(), + version=body.version, + release_date=body.release_date, + releases_doc_url=body.releases_doc_url.encoded_string(), + fixed_release_url=body.fixed_release_url.encoded_string(), ) try: await insert_library(library, request.app.db_session_factory) diff --git a/app/schemas.py b/app/schemas.py index 0d46056..20406ea 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -1,13 +1,31 @@ +from datetime import date from enum import Enum from typing import List from pydantic import BaseModel, HttpUrl +class LibraryTagEnum(str, Enum): + UPDATES = "updates" + BUG_FIX = "bug_fix" + NEW_FEATURE = "new_feature" + SECURITY_FIX = "security_fix" + DEPRECATION = "deprecation" + + +class LibraryNews(BaseModel): + tag: LibraryTagEnum + description: str + + class Library(BaseModel): library_name: str - releases_url: HttpUrl + news: List[LibraryNews] logo: HttpUrl + version: str + release_date: date + releases_doc_url: HttpUrl + fixed_release_url: HttpUrl # Community / User Class @@ -31,15 +49,7 @@ class TokenPayload(BaseModel): username: str -# Subscription Class -class SubscriptionTagEnum(str, Enum): - UPDATE = "update" - BUG_FIX = "bug_fix" - NEW_FEATURE = "new_feature" - SECURITY_FIX = "security_fix" - - class Subscription(BaseModel): email: str - tags: List[SubscriptionTagEnum] + tags: List[LibraryTagEnum] libraries_list: List[str] diff --git a/app/services/database/models/libraries.py b/app/services/database/models/libraries.py index d5680fc..4ae8540 100644 --- a/app/services/database/models/libraries.py +++ b/app/services/database/models/libraries.py @@ -1,3 +1,4 @@ +from datetime import date from typing import Optional from sqlmodel import Field, SQLModel @@ -9,8 +10,11 @@ class Library(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) library_name: str user_email: str - releases_url: str logo: str + version: str + release_date: date + releases_doc_url: str + fixed_release_url: str community_id: Optional[int] = Field( default=None, foreign_key="communities.id" ) diff --git a/app/services/database/models/subscriptions.py b/app/services/database/models/subscriptions.py index 4311923..2a99167 100644 --- a/app/services/database/models/subscriptions.py +++ b/app/services/database/models/subscriptions.py @@ -1,6 +1,6 @@ from typing import List, Optional -from schemas import SubscriptionTagEnum +from schemas import LibraryTagEnum from sqlalchemy import JSON, Column from sqlmodel import Field, SQLModel @@ -10,7 +10,7 @@ class Subscription(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) email: str - tags: List[SubscriptionTagEnum] = Field(sa_column=Column(JSON)) + tags: List[LibraryTagEnum] = Field(sa_column=Column(JSON)) community_id: Optional[int] = Field( default=None, foreign_key="communities.id" ) diff --git a/tests/test_libraries.py b/tests/test_libraries.py index 9323006..cb80db6 100755 --- a/tests/test_libraries.py +++ b/tests/test_libraries.py @@ -1,3 +1,5 @@ +from datetime import date + import pytest import pytest_asyncio from httpx import AsyncClient @@ -20,8 +22,11 @@ async def test_insert_libraries(session: AsyncSession, community: Community): library = Library( library_name="Python", user_email="teste@teste.com", - releases_url="http://teste.com", - logo="logo", + logo="http://teste.com", + version="3.12", + release_date=date.today(), + releases_doc_url="http://teste.com", + fixed_release_url="http://teste.com", community_id=community.id, ) session.add(library) @@ -34,8 +39,11 @@ async def test_insert_libraries(session: AsyncSession, community: Community): assert found is not None assert found.library_name == "Python" assert found.user_email == "teste@teste.com" - assert found.releases_url == "http://teste.com" - assert found.logo == "logo" + assert found.logo == "http://teste.com" + assert found.version == "3.12" + assert found.release_date == date.today() + assert found.releases_doc_url == "http://teste.com" + assert found.fixed_release_url == "http://teste.com" assert found.community_id == community.id @@ -45,8 +53,15 @@ async def test_post_libraries_endpoint( ): body = { "library_name": "Python from API", - "releases_url": "http://teste.com/", + "news": [ + {"tag": "updates", "description": "New feature"}, + {"tag": "bug_fix", "description": "Fixed bug"}, + ], "logo": "http://teste.com/", + "version": "3.12", + "release_date": "2023-01-01", + "releases_doc_url": "http://teste.com/", + "fixed_release_url": "http://teste.com/", } response = await async_client.post( @@ -65,5 +80,10 @@ async def test_post_libraries_endpoint( created_library = result.first() assert created_library is not None - assert created_library.releases_url == body["releases_url"] assert created_library.logo == body["logo"] + assert created_library.version == body["version"] + assert created_library.release_date == date.fromisoformat( + body["release_date"] + ) + assert created_library.releases_doc_url == body["releases_doc_url"] + assert created_library.fixed_release_url == body["fixed_release_url"] diff --git a/tests/test_subscriptions.py b/tests/test_subscriptions.py index acfa785..3da3600 100755 --- a/tests/test_subscriptions.py +++ b/tests/test_subscriptions.py @@ -1,7 +1,7 @@ import pytest import pytest_asyncio from httpx import AsyncClient -from schemas import SubscriptionTagEnum +from schemas import LibraryTagEnum from services.database.models import Community, Subscription from sqlmodel import select from sqlmodel.ext.asyncio.session import AsyncSession @@ -20,7 +20,7 @@ async def community(session: AsyncSession): async def test_insert_subscription(session: AsyncSession, community: Community): subscription = Subscription( email="teste@teste.com", - tags=[SubscriptionTagEnum.BUG_FIX, SubscriptionTagEnum.UPDATE], + tags=[LibraryTagEnum.BUG_FIX, LibraryTagEnum.UPDATES], community_id=community.id, ) session.add(subscription) @@ -38,8 +38,8 @@ async def test_insert_subscription(session: AsyncSession, community: Community): assert found is not None assert found.email == "teste@teste.com" assert found.tags == [ - SubscriptionTagEnum.BUG_FIX, - SubscriptionTagEnum.UPDATE, + LibraryTagEnum.BUG_FIX, + LibraryTagEnum.UPDATES, ] assert found.community_id == community.id @@ -79,7 +79,7 @@ async def preset_library(async_client: AsyncClient): async def test_post_subscribe_endpoint(async_client: AsyncClient): body = { "email": "teste@teste.com", - "tags": ["bug_fix", "update"], + "tags": ["bug_fix", "updates"], "libraries_list": ["Python", "Django"], } From 9635c5764401337031164e25fea5449cd3b619e6 Mon Sep 17 00:00:00 2001 From: Gildo Junior <1gildojunior@gmail.com> Date: Tue, 9 Sep 2025 23:46:16 -0300 Subject: [PATCH 2/2] Create libraries_request table and enpoints --- app/routers/libraries/routes.py | 101 +++++++++++++----- app/schemas.py | 6 +- app/services/database/models/__init__.py | 4 +- app/services/database/models/communities.py | 6 ++ app/services/database/models/libraries.py | 7 +- .../database/models/libraries_request.py | 21 ++++ app/services/database/models/subscriptions.py | 8 +- app/services/database/orm/library_request.py | 23 ++++ app/services/database/orm/subscription.py | 13 ++- tests/test_libraries_request.py | 65 +++++++++++ tests/test_subscriptions.py | 79 +++++++++++--- 11 files changed, 285 insertions(+), 48 deletions(-) create mode 100644 app/services/database/models/libraries_request.py create mode 100644 app/services/database/orm/library_request.py create mode 100644 tests/test_libraries_request.py diff --git a/app/routers/libraries/routes.py b/app/routers/libraries/routes.py index 0ba73dd..6bcf4bb 100644 --- a/app/routers/libraries/routes.py +++ b/app/routers/libraries/routes.py @@ -1,17 +1,20 @@ -from typing import List +from typing import Annotated, List -from fastapi import APIRouter, HTTPException, Request, status +from fastapi import APIRouter, Header, HTTPException, Request, status from pydantic import BaseModel from app.schemas import Library as LibrarySchema from app.schemas import LibraryNews +from app.schemas import LibraryRequest as LibraryRequestSchema from app.schemas import Subscription as SubscriptionSchema from app.services.database.models import Library, Subscription +from app.services.database.models.libraries_request import LibraryRequest from app.services.database.orm.library import ( get_libraries_by_language, get_library_ids_by_multiple_names, insert_library, ) +from app.services.database.orm.library_request import insert_library_request from app.services.database.orm.subscription import upsert_multiple_subscription @@ -34,27 +37,32 @@ def setup(): description="Get libraries by language", ) async def get_by_language(request: Request, language: str): - libraryList = await get_libraries_by_language( - language=language, session=request.app.db_session_factory - ) - return [ - LibrarySchema( - library_name=libraryDb.library_name, - news=[ - LibraryNews( - tag=news["tag"], description=news["description"] - ) - for news in libraryDb.news - ], - logo=libraryDb.logo, - version=libraryDb.version, - release_date=libraryDb.release_date, - releases_doc_url=libraryDb.releases_doc_url, - fixed_release_url=libraryDb.fixed_release_url, - language=libraryDb.language, + try: + libraryList = await get_libraries_by_language( + language=language, session=request.app.db_session_factory ) - for libraryDb in libraryList - ] + return [ + LibrarySchema( + library_name=libraryDb.library_name, + news=[ + LibraryNews( + tag=news["tag"], description=news["description"] + ) + for news in libraryDb.news + ], + logo=libraryDb.logo, + version=libraryDb.version, + release_date=libraryDb.release_date, + releases_doc_url=libraryDb.releases_doc_url, + fixed_release_url=libraryDb.fixed_release_url, + language=libraryDb.language, + ) + for libraryDb in libraryList + ] + except HTTPException as e: + raise e + except Exception as e: + HTTPException(status_code=500, detail=f"Unexpected error: {e}") @router.post( "", @@ -80,9 +88,11 @@ async def create_library( try: await insert_library(library, request.app.db_session_factory) return LibraryResponse() + except HTTPException as e: + raise e except Exception as e: raise HTTPException( - status_code=500, detail=f"Failed to create library: {e}" + status_code=500, detail=f"Unexpected error: {e}" ) @router.post( @@ -97,14 +107,22 @@ async def create_library( async def subscribe_libraries( request: Request, body: SubscriptionSchema, + user_email: Annotated[str, Header(alias="user-email")], ): try: library_ids = await get_library_ids_by_multiple_names( body.libraries_list, request.app.db_session_factory ) + if (library_ids is None) or (len(library_ids) == 0): + raise HTTPException( + status_code=404, detail="Libraries not found" + ) + subscriptions = [ - Subscription(email=body.email, tags=body.tags, library_id=id) + Subscription( + user_email=user_email, tags=body.tags, library_id=id + ) for id in library_ids ] @@ -113,9 +131,42 @@ async def subscribe_libraries( ) return SubscribeLibraryResponse() + except HTTPException as e: + raise e + except Exception as e: + raise HTTPException( + status_code=500, detail=f"Unexpected error: {e}" + ) + + @router.post( + "/request", + response_model=LibraryResponse, + status_code=status.HTTP_200_OK, + summary="Request a library", + description="Request a library to follow", + ) + async def request_library( + request: Request, + body: LibraryRequestSchema, + user_email: Annotated[str, Header(alias="user-email")], + ): + try: + library_request = LibraryRequest( + user_email=user_email, + library_name=body.library_name, + library_home_page=body.library_home_page, + ) + + await insert_library_request( + library_request, request.app.db_session_factory + ) + + return LibraryResponse() + except HTTPException as e: + raise e except Exception as e: raise HTTPException( - status_code=500, detail=f"Subscription failed: {e}" + status_code=500, detail=f"Unexpected error: {e}" ) return router diff --git a/app/schemas.py b/app/schemas.py index d896b0f..279c23e 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -23,6 +23,11 @@ class Library(BaseModel): language: str +class LibraryRequest(BaseModel): + library_name: str + library_home_page: str + + # Community / User Class class Community(BaseModel): username: str @@ -55,6 +60,5 @@ class TokenPayload(BaseModel): class Subscription(BaseModel): - email: str tags: List[LibraryTagUpdatesEnum] libraries_list: List[str] diff --git a/app/services/database/models/__init__.py b/app/services/database/models/__init__.py index 5ef92ba..59f1a7e 100644 --- a/app/services/database/models/__init__.py +++ b/app/services/database/models/__init__.py @@ -1,7 +1,7 @@ from app.services.database.models.communities import Community from app.services.database.models.libraries import Library +from app.services.database.models.libraries_request import LibraryRequest from app.services.database.models.news import News from app.services.database.models.subscriptions import Subscription - -__all__ = ["Community", "Library","News", "Subscription"] +__all__ = ["Community", "Library", "News", "Subscription", "LibraryRequest"] diff --git a/app/services/database/models/communities.py b/app/services/database/models/communities.py index 22d8c12..e6502a0 100644 --- a/app/services/database/models/communities.py +++ b/app/services/database/models/communities.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import Optional from sqlmodel import Field, SQLModel @@ -10,3 +11,8 @@ class Community(SQLModel, table=True): username: str email: str password: str + created_at: Optional[datetime] = Field(default_factory=datetime.now) + updated_at: Optional[datetime] = Field( + default_factory=datetime.now, + sa_column_kwargs={"onupdate": datetime.now}, + ) diff --git a/app/services/database/models/libraries.py b/app/services/database/models/libraries.py index 44ab33a..4d56e38 100644 --- a/app/services/database/models/libraries.py +++ b/app/services/database/models/libraries.py @@ -1,4 +1,4 @@ -from datetime import date +from datetime import date, datetime from typing import List, Optional from sqlalchemy import JSON, Column @@ -20,3 +20,8 @@ class Library(SQLModel, table=True): community_id: Optional[int] = Field( default=None, foreign_key="communities.id" ) + created_at: Optional[datetime] = Field(default_factory=datetime.now) + updated_at: Optional[datetime] = Field( + default_factory=datetime.now, + sa_column_kwargs={"onupdate": datetime.now}, + ) diff --git a/app/services/database/models/libraries_request.py b/app/services/database/models/libraries_request.py new file mode 100644 index 0000000..ac872e5 --- /dev/null +++ b/app/services/database/models/libraries_request.py @@ -0,0 +1,21 @@ +from datetime import datetime +from typing import Optional + +from sqlmodel import Field, SQLModel + + +class LibraryRequest(SQLModel, table=True): + __tablename__ = "libraries_request" + + id: Optional[int] = Field(default=None, primary_key=True) + user_email: str + library_name: str + library_home_page: str + community_id: Optional[int] = Field( + default=None, foreign_key="communities.id" + ) + created_at: Optional[datetime] = Field(default_factory=datetime.now) + updated_at: Optional[datetime] = Field( + default_factory=datetime.now, + sa_column_kwargs={"onupdate": datetime.now}, + ) diff --git a/app/services/database/models/subscriptions.py b/app/services/database/models/subscriptions.py index f15ca22..1a36b05 100644 --- a/app/services/database/models/subscriptions.py +++ b/app/services/database/models/subscriptions.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import List, Optional from sqlalchemy import JSON, Column @@ -10,9 +11,14 @@ class Subscription(SQLModel, table=True): __tablename__ = "subscriptions" # type: ignore id: Optional[int] = Field(default=None, primary_key=True) - email: str + user_email: str tags: List[LibraryTagUpdatesEnum] = Field(sa_column=Column(JSON)) community_id: Optional[int] = Field( default=None, foreign_key="communities.id" ) library_id: Optional[int] = Field(default=None, foreign_key="libraries.id") + created_at: Optional[datetime] = Field(default_factory=datetime.now) + updated_at: Optional[datetime] = Field( + default_factory=datetime.now, + sa_column_kwargs={"onupdate": datetime.now}, + ) diff --git a/app/services/database/orm/library_request.py b/app/services/database/orm/library_request.py new file mode 100644 index 0000000..35728c1 --- /dev/null +++ b/app/services/database/orm/library_request.py @@ -0,0 +1,23 @@ +from typing import List + +from sqlmodel import select +from sqlmodel.ext.asyncio.session import AsyncSession + +from app.services.database.models.libraries_request import LibraryRequest + + +async def insert_library_request( + library_request: LibraryRequest, + session: AsyncSession, +): + session.add(library_request) + await session.commit() + await session.refresh(library_request) + + +async def get_all_library_requests( + session: AsyncSession, +) -> List[LibraryRequest]: + statement = select(LibraryRequest) + result = await session.exec(statement) + return [library_request for library_request in result.all()] diff --git a/app/services/database/orm/subscription.py b/app/services/database/orm/subscription.py index 79873c1..4c59bc0 100644 --- a/app/services/database/orm/subscription.py +++ b/app/services/database/orm/subscription.py @@ -14,18 +14,21 @@ async def upsert_multiple_subscription( if not subscriptions: return [] - incoming_map: Dict[Tuple[str, int], Subscription] = { - (sub.email, sub.library_id): sub for sub in subscriptions + incoming_map: Dict[Tuple[str, int | None], Subscription] = { + (sub.user_email, sub.library_id): sub for sub in subscriptions } keys_to_check = incoming_map.keys() stmt = select(Subscription).where( - tuple_(Subscription.email, Subscription.library_id).in_(keys_to_check) + tuple_(Subscription.user_email, Subscription.library_id).in_( + keys_to_check + ) ) + result = await session.exec(stmt) existing_subscriptions = result.all() - existing_map: Dict[Tuple[str, int], Subscription] = { - (sub.email, sub.library_id): sub for sub in existing_subscriptions + existing_map: Dict[Tuple[str, int | None], Subscription] = { + (sub.user_email, sub.library_id): sub for sub in existing_subscriptions } new_subscriptions: List[Subscription] = [] diff --git a/tests/test_libraries_request.py b/tests/test_libraries_request.py new file mode 100644 index 0000000..7a0c48a --- /dev/null +++ b/tests/test_libraries_request.py @@ -0,0 +1,65 @@ +import pytest +import pytest_asyncio +from httpx import AsyncClient +from sqlmodel import select +from sqlmodel.ext.asyncio.session import AsyncSession + +from app.services.database.models import Community, LibraryRequest + + +@pytest_asyncio.fixture +async def community(session: AsyncSession): + community = Community(username="admin", email="a@a.com", password="123") + session.add(community) + await session.commit() + await session.refresh(community) + return community + + +@pytest.mark.asyncio +async def test_insert_libraries(session: AsyncSession, community: Community): + library = LibraryRequest( + library_name="Flask", + user_email="teste@teste.com", + library_home_page="http://teste.com/", + community_id=community.id, + ) + session.add(library) + await session.commit() + + statement = select(LibraryRequest).where( + LibraryRequest.library_name == "Flask" + ) + result = await session.exec(statement) + found = result.first() + + assert found is not None + assert found.user_email == "teste@teste.com" + assert found.library_home_page == "http://teste.com/" + assert found.community_id == community.id + + +@pytest.mark.asyncio +async def test_post_libraries_endpoint( + async_client: AsyncClient, session: AsyncSession +): + body = {"library_name": "FastAPI", "library_home_page": "http://teste.com/"} + + response = await async_client.post( + "/api/libraries/request", + json=body, + headers={"Content-Type": "application/json", "user-email": "a@a.com"}, + ) + + assert response.status_code == 200 + assert response.json()["status"] == "Library created successfully" + + statement = select(LibraryRequest).where( + LibraryRequest.library_name == body["library_name"] + ) + result = await session.exec(statement) + created_request = result.first() + + assert created_request is not None + assert created_request.user_email == "a@a.com" + assert created_request.library_home_page == "http://teste.com/" diff --git a/tests/test_subscriptions.py b/tests/test_subscriptions.py index 723a11b..06cdfb1 100755 --- a/tests/test_subscriptions.py +++ b/tests/test_subscriptions.py @@ -20,7 +20,7 @@ async def community(session: AsyncSession): @pytest.mark.asyncio async def test_insert_subscription(session: AsyncSession, community: Community): subscription = Subscription( - email="teste@teste.com", + user_email="teste@teste.com", tags=[LibraryTagUpdatesEnum.BUG_FIX, LibraryTagUpdatesEnum.UPDATE], community_id=community.id, ) @@ -28,16 +28,16 @@ async def test_insert_subscription(session: AsyncSession, community: Community): await session.commit() statement = select(Subscription).where( - Subscription.email == "teste@teste.com" + Subscription.user_email == "teste@teste.com" ) statement = select(Subscription).where( - Subscription.email == "teste@teste.com" + Subscription.user_email == "teste@teste.com" ) result = await session.exec(statement) found = result.first() assert found is not None - assert found.email == "teste@teste.com" + assert found.user_email == "teste@teste.com" assert found.tags == [ LibraryTagUpdatesEnum.BUG_FIX, LibraryTagUpdatesEnum.UPDATE, @@ -45,12 +45,19 @@ async def test_insert_subscription(session: AsyncSession, community: Community): assert found.community_id == community.id -@pytest_asyncio.fixture -async def preset_library(async_client: AsyncClient): +async def preset_libraries_with_http_post(async_client: AsyncClient): body1 = { - "library_name": "Python", - "releases_url": "http://teste.com/", + "library_name": "Flask", + "news": [ + {"tag": "updates", "description": "Updated"}, + {"tag": "bug_fix", "description": "Fixed"}, + ], "logo": "http://teste.com/", + "version": "3.11", + "release_date": "2025-01-01", + "releases_doc_url": "http://teste.com/", + "fixed_release_url": "http://teste.com/", + "language": "Python", } response1 = await async_client.post( @@ -63,8 +70,16 @@ async def preset_library(async_client: AsyncClient): body2 = { "library_name": "Django", - "releases_url": "http://teste.com/", + "news": [ + {"tag": "updates", "description": "Updated"}, + {"tag": "bug_fix", "description": "Fixed"}, + ], "logo": "http://teste.com/", + "version": "3.11", + "release_date": "2025-01-01", + "releases_doc_url": "http://teste.com/", + "fixed_release_url": "http://teste.com/", + "language": "Python", } response2 = await async_client.post( @@ -77,18 +92,56 @@ async def preset_library(async_client: AsyncClient): @pytest.mark.asyncio -async def test_post_subscribe_endpoint(async_client: AsyncClient): +async def test_post_subscribe_endpoint( + async_client: AsyncClient, session: AsyncSession +): + await preset_libraries_with_http_post(async_client=async_client) + body = { - "email": "teste@teste.com", "tags": ["bug_fix", "updates"], - "libraries_list": ["Python", "Django"], + "libraries_list": ["Flask", "Django"], } response = await async_client.post( "/api/libraries/subscribe", json=body, - headers={"Content-Type": "application/json"}, + headers={"Content-Type": "application/json", "user-email": "a@a.com"}, ) assert response.status_code == 200 assert response.json()["status"] == "Subscribed in libraries successfully" + + statement = select(Subscription).where(Subscription.user_email == "a@a.com") + result = await session.exec(statement) + created_subscriptions = result.all() + + assert created_subscriptions is not None + assert len(created_subscriptions) == 2 + assert created_subscriptions[0].tags == [ + LibraryTagUpdatesEnum.BUG_FIX, + LibraryTagUpdatesEnum.UPDATE, + ] + assert created_subscriptions[1].tags == [ + LibraryTagUpdatesEnum.BUG_FIX, + LibraryTagUpdatesEnum.UPDATE, + ] + + +@pytest.mark.asyncio +async def test_post_subscribe_endpoint_with_unexistents_libraries( + async_client: AsyncClient, +): + await preset_libraries_with_http_post(async_client=async_client) + + body = { + "tags": ["bug_fix", "updates"], + "libraries_list": ["Python", "NodeJS"], + } + + response = await async_client.post( + "/api/libraries/subscribe", + json=body, + headers={"Content-Type": "application/json", "user-email": "a@a.com"}, + ) + + assert response.status_code == 404