From 4e8cfc1c1ae58e75492d1d4bf01efaba01910c2d Mon Sep 17 00:00:00 2001 From: Arthur Mesquita Pickcius Date: Thu, 16 Oct 2025 18:30:03 -0300 Subject: [PATCH 1/5] Create tests for PUT news endpoint - Create test_put_news_endpoint - Add publish to test_insert_news --- tests/test_news.py | 81 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 15 deletions(-) diff --git a/tests/test_news.py b/tests/test_news.py index 94a786c..e537b47 100755 --- a/tests/test_news.py +++ b/tests/test_news.py @@ -72,21 +72,22 @@ async def test_insert_news( await session.commit() statement = select(News).where(News.title == "Test news") result = await session.exec(statement) - found_news = result.first() - assert found_news is not None - assert found_news.title == news_list[0].title - assert found_news.content == news_list[0].content - assert found_news.category == news_list[0].category - assert found_news.user_email == news_list[0].user_email - assert found_news.source_url == news_list[0].source_url - assert found_news.tags == news_list[0].tags - assert found_news.social_media_url == news_list[0].social_media_url - assert found_news.likes == 0 - assert found_news.community_id == community.id - assert isinstance(found_news.created_at, datetime) - assert isinstance(found_news.updated_at, datetime) - assert found_news.created_at <= datetime.now() - assert found_news.updated_at >= found_news.created_at + stored_news = result.first() + assert stored_news is not None + assert stored_news.title == news_list[0].title + assert stored_news.content == news_list[0].content + assert stored_news.category == news_list[0].category + assert stored_news.user_email == news_list[0].user_email + assert stored_news.source_url == news_list[0].source_url + assert stored_news.tags == news_list[0].tags + assert stored_news.social_media_url == news_list[0].social_media_url + assert stored_news.likes == 0 + assert stored_news.community_id == community.id + assert isinstance(stored_news.created_at, datetime) + assert isinstance(stored_news.updated_at, datetime) + assert stored_news.created_at <= datetime.now() + assert stored_news.updated_at >= stored_news.created_at + assert stored_news.publish is False @pytest.mark.asyncio @@ -221,6 +222,7 @@ async def test_get_news_by_id( statement = select(News).where(News.title == "Test news") result = await session.exec(statement) stored_news = result.first() + assert stored_news is not None response = await async_client.get( "/api/news", @@ -288,6 +290,51 @@ async def test_news_integration( assert data["news_list"][0]["likes"] == 0 +@pytest.mark.asyncio +async def test_put_news_endpoint( + session: AsyncSession, + async_client: AsyncClient, + community: Community, + valid_auth_headers: Mapping[str, str], +): + session.add_all(news_list) + await session.commit() + + statement = select(News).where(News.title == "Test news") + result = await session.exec(statement) + stored_news = result.first() + assert stored_news is not None + + response = await async_client.put( + "/api/news", + params={ + "title": "updated title", + "content": "updated content", + "category": "updated_category", + "user_email": "updated_email@test.com", + "source_url": "https://updated_url.com", + "tags": "test_tag_updated", + "user_email_list": "updated_email@test.com", + "social_media_url": "https://updated_social_media_url.com", + "likes": 42, + }, + headers=valid_auth_headers, + json={"publish": True}, + ) + data = response.json() + assert response.status_code == status.HTTP_200_OK + assert data["tittle"] == "updated title" + assert data["content"] == "updated content" + assert data["category"] == "updated_category" + assert data["user_email"] == "updated_email@test.com" + assert data["source_url"] == "https://updated_url.com" + assert data["tags"] == "test_tag_updated" + assert data["user_email_list"] == "updated_email@test.com" + assert data["social_media_url"] == "https://updated_social_media_url.com" + assert data["likes"] == 42 + assert data["publish"] is True + + @pytest.mark.asyncio async def test_news_likes_endpoint( session: AsyncSession, @@ -325,6 +372,7 @@ async def test_news_likes_endpoint( statement = select(News).where(News.title == news_data["title"]) result = await session.exec(statement) stored_news = result.first() + assert stored_news is not None assert stored_news.likes == 1 assert stored_news.user_email_list == f"['{encode_email(emails[0])}']" @@ -336,6 +384,7 @@ async def test_news_likes_endpoint( statement = select(News).where(News.title == news_data["title"]) result = await session.exec(statement) stored_news = result.first() + assert stored_news is not None assert stored_news.likes == 2 assert ( stored_news.user_email_list @@ -351,6 +400,7 @@ async def test_news_likes_endpoint( statement = select(News).where(News.title == news_data["title"]) result = await session.exec(statement) stored_news = result.first() + assert stored_news is not None assert stored_news.likes == 1 assert stored_news.user_email_list == f"['{encode_email(emails[1])}']" @@ -362,5 +412,6 @@ async def test_news_likes_endpoint( statement = select(News).where(News.title == news_data["title"]) result = await session.exec(statement) stored_news = result.first() + assert stored_news is not None assert stored_news.likes == 0 assert stored_news.user_email_list == "[]" From ba512b3069e44fe678f8285657126ca112b6b34d Mon Sep 17 00:00:00 2001 From: Arthur Mesquita Pickcius Date: Thu, 16 Oct 2025 20:40:25 -0300 Subject: [PATCH 2/5] Create PUT news endpoint - Add PUT news to router - Add update news to orm - Add publish to news model --- app/routers/news/routes.py | 54 +++++++++++++++++++++++++--- app/schemas.py | 4 +++ app/services/database/models/news.py | 1 + app/services/database/orm/news.py | 15 +++++++- tests/test_news.py | 53 ++++++++++++++++----------- 5 files changed, 101 insertions(+), 26 deletions(-) diff --git a/app/routers/news/routes.py b/app/routers/news/routes.py index ed686aa..afef723 100644 --- a/app/routers/news/routes.py +++ b/app/routers/news/routes.py @@ -8,7 +8,7 @@ import app.services.database.orm.news as orm_news from app.routers.authentication import get_current_active_community -from app.schemas import News +from app.schemas import News, NewsPublishStatus from app.services.database.models import Community as DBCommunity from app.services.limiter import limiter @@ -16,7 +16,7 @@ ALGORITHM = os.getenv("ALGORITHM", "HS256") -class NewsPostResponse(BaseModel): +class NewsCreateResponse(BaseModel): status: str = "News Criada" @@ -25,6 +25,10 @@ class NewsGetResponse(BaseModel): news_list: list = [] +class NewsUpdateResponse(BaseModel): + status: str = "News Atualizada" + + class NewsLikeResponse(BaseModel): total_likes: int | None @@ -39,7 +43,7 @@ def setup(): @router.post( "", - response_model=NewsPostResponse, + response_model=NewsCreateResponse, status_code=status.HTTP_200_OK, summary="News endpoint", description="Creates news and returns a confirmation message", @@ -61,7 +65,7 @@ async def post_news( await orm_news.create_news( session=request.app.db_session_factory, news=news_dict ) - return NewsPostResponse() + return NewsCreateResponse() @router.get( "", @@ -93,6 +97,48 @@ async def get_news( ) return NewsGetResponse(news_list=news_list) + @router.put( + "", + response_model=NewsGetResponse, + status_code=status.HTTP_200_OK, + summary="PUT News", + description="Updates news by query params and set publish value", + ) + @limiter.limit("60/minute") + async def put_news( + request: Request, + current_community: Annotated[ + DBCommunity, Depends(get_current_active_community) + ], + publish_status: NewsPublishStatus, + id: str | None = None, + title: str | None = None, + content: str | None = None, + category: str | None = None, + user_email: str = str(Header(..., alias="user-email")), + source_url: str | None = None, + tags: str | None = None, + social_media_url: str | None = None, + ): + """ + Get News endpoint that retrieves news filtered by user and query params. + """ + news: dict = { + "id": id, + "title": title, + "content": content, + "category": category, + "user_email": user_email, + "source_url": source_url, + "tags": tags, + "social_media_url": social_media_url, + "publish": publish_status.publish, + } + await orm_news.update_news( + session=request.app.db_session_factory, news=news + ) + return NewsUpdateResponse() + @router.post( path="/{news_id}/like", response_model=NewsLikeResponse, diff --git a/app/schemas.py b/app/schemas.py index c5a064e..322800c 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -54,6 +54,10 @@ class News(BaseModel): likes: int = 0 +class NewsPublishStatus(BaseModel): + publish: bool = False + + class Token(BaseModel): access_token: str token_type: str diff --git a/app/services/database/models/news.py b/app/services/database/models/news.py index 6f1b5a5..c2508b5 100644 --- a/app/services/database/models/news.py +++ b/app/services/database/models/news.py @@ -49,6 +49,7 @@ class News(SQLModel, table=True): user_email_list: str = Field(default="[]") social_media_url: str likes: int = Field(default=0) + publish: bool = Field(default=False) # Chaves estrangeiras community_id: Optional[int] = Field( diff --git a/app/services/database/orm/news.py b/app/services/database/orm/news.py index f4e8b71..346c124 100644 --- a/app/services/database/orm/news.py +++ b/app/services/database/orm/news.py @@ -42,7 +42,20 @@ async def get_news_by_query_params( statement = select(News).where(*filters) results = await session.exec(statement) - return results.all() + return list(results.all()) + + +async def update_news(session: AsyncSession, news: dict) -> None: + statement = select(News).where(News.id == news["id"]) + results = await session.exec(statement) + news_item = results.first() + if news_item: + for key, value in news.items(): + if key != "id" and value is not None: + setattr(news_item, key, value) + session.add(news_item) + await session.commit() + await session.refresh(news_item) async def like_news( diff --git a/tests/test_news.py b/tests/test_news.py index e537b47..fae72f2 100755 --- a/tests/test_news.py +++ b/tests/test_news.py @@ -294,8 +294,8 @@ async def test_news_integration( async def test_put_news_endpoint( session: AsyncSession, async_client: AsyncClient, - community: Community, valid_auth_headers: Mapping[str, str], + news_list: list, ): session.add_all(news_list) await session.commit() @@ -304,35 +304,46 @@ async def test_put_news_endpoint( result = await session.exec(statement) stored_news = result.first() assert stored_news is not None + assert stored_news.publish is False + + data: dict = { + "title": "updated title", + "content": "updated content", + "category": "updated_category", + "user_email": "updated_email@test.com", + "source_url": "https://updated_url.com", + "tags": "test_tag_updated", + "social_media_url": "https://updated_social_media_url.com", + } response = await async_client.put( "/api/news", params={ - "title": "updated title", - "content": "updated content", - "category": "updated_category", - "user_email": "updated_email@test.com", - "source_url": "https://updated_url.com", - "tags": "test_tag_updated", - "user_email_list": "updated_email@test.com", - "social_media_url": "https://updated_social_media_url.com", - "likes": 42, + "id": stored_news.id, + "title": data["title"], + "content": data["content"], + "category": data["category"], + "user_email": data["user_email"], + "source_url": data["source_url"], + "tags": data["tags"], + "social_media_url": data["social_media_url"], }, headers=valid_auth_headers, json={"publish": True}, ) - data = response.json() assert response.status_code == status.HTTP_200_OK - assert data["tittle"] == "updated title" - assert data["content"] == "updated content" - assert data["category"] == "updated_category" - assert data["user_email"] == "updated_email@test.com" - assert data["source_url"] == "https://updated_url.com" - assert data["tags"] == "test_tag_updated" - assert data["user_email_list"] == "updated_email@test.com" - assert data["social_media_url"] == "https://updated_social_media_url.com" - assert data["likes"] == 42 - assert data["publish"] is True + + statement = select(News).where(News.title == data["title"]) + result = await session.exec(statement) + stored_news = result.first() + assert stored_news is not None + assert stored_news.content == data["content"] + assert stored_news.category == data["category"] + assert stored_news.user_email == data["user_email"] + assert stored_news.source_url == data["source_url"] + assert stored_news.tags == data["tags"] + assert stored_news.social_media_url == data["social_media_url"] + assert stored_news.publish @pytest.mark.asyncio From 51d45e24e3bbd94334373e4559e2543a31b2464e Mon Sep 17 00:00:00 2001 From: dan5e3s6ares Date: Tue, 21 Oct 2025 23:12:04 +0000 Subject: [PATCH 3/5] REF: Encrypt user email in subscription and library request handling --- app/routers/libraries/routes.py | 3 ++- tests/test_healthcheck.py | 4 ++-- tests/test_libraries_request.py | 3 ++- tests/test_subscriptions.py | 6 +++++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/routers/libraries/routes.py b/app/routers/libraries/routes.py index 259a08a..c455ecb 100644 --- a/app/routers/libraries/routes.py +++ b/app/routers/libraries/routes.py @@ -3,6 +3,7 @@ from fastapi import APIRouter, Header, HTTPException, Request, status from fastapi.params import Depends from pydantic import BaseModel +from services.encryption import encrypt_email from app.routers.authentication import get_current_active_community from app.schemas import Library as LibrarySchema @@ -145,7 +146,7 @@ async def subscribe_libraries( subscriptions = [ Subscription( - user_email=user_email, + user_email=encrypt_email(user_email), tags=body.tags, library_id=id, community_id=current_community.id, diff --git a/tests/test_healthcheck.py b/tests/test_healthcheck.py index 8623d74..ee657db 100755 --- a/tests/test_healthcheck.py +++ b/tests/test_healthcheck.py @@ -18,7 +18,7 @@ async def test_healthcheck_endpoint( ) assert response.status_code == status.HTTP_200_OK - assert response.json() == {"status": "healthy", "version": "2.0.0"} + assert response.json()["version"] == "2.0.0" @pytest.mark.asyncio @@ -28,4 +28,4 @@ async def test_healthcheck_endpoint_without_auth(async_client: AsyncClient): response = await async_client.get("/api/healthcheck") assert response.status_code == status.HTTP_200_OK - assert response.json() == {"status": "healthy", "version": "2.0.0"} + assert response.json()["version"] == "2.0.0" diff --git a/tests/test_libraries_request.py b/tests/test_libraries_request.py index ad9f213..67a2f5a 100644 --- a/tests/test_libraries_request.py +++ b/tests/test_libraries_request.py @@ -6,6 +6,7 @@ from sqlmodel.ext.asyncio.session import AsyncSession from app.services.database.models import Community, LibraryRequest +from tests.conftest import CommunityCredentials @pytest.mark.asyncio @@ -56,5 +57,5 @@ async def test_post_libraries_endpoint( created_request = result.first() assert created_request is not None - assert created_request.user_email == community.email + assert created_request.user_email == CommunityCredentials.email assert created_request.library_home_page == "http://teste.com/" diff --git a/tests/test_subscriptions.py b/tests/test_subscriptions.py index 5f2078d..eae16fa 100755 --- a/tests/test_subscriptions.py +++ b/tests/test_subscriptions.py @@ -2,6 +2,7 @@ import pytest from httpx import AsyncClient +from services.encryption import encrypt_email from sqlmodel import select from sqlmodel.ext.asyncio.session import AsyncSession @@ -111,9 +112,12 @@ async def test_post_subscribe_endpoint( assert response.status_code == 200 assert response.json()["status"] == "Subscribed in libraries successfully" + encripted_email = encrypt_email(valid_auth_headers["user-email"]) + statement = select(Subscription).where( - Subscription.user_email == community.email + Subscription.user_email == encripted_email ) + result = await session.exec(statement) created_subscriptions = result.all() From f6b785f19605718c55d1fe3a9238c8ac6f42ee7b Mon Sep 17 00:00:00 2001 From: Arthur Mesquita Pickcius Date: Wed, 22 Oct 2025 20:30:32 -0300 Subject: [PATCH 4/5] Fix PUT News endpoint by sending News data in body --- app/routers/news/routes.py | 47 ++++++++++------------------ app/schemas.py | 5 ++- app/services/database/models/news.py | 2 ++ app/services/database/orm/news.py | 12 +++++-- tests/test_news.py | 41 +++++++++++------------- 5 files changed, 48 insertions(+), 59 deletions(-) diff --git a/app/routers/news/routes.py b/app/routers/news/routes.py index afef723..55cf41a 100644 --- a/app/routers/news/routes.py +++ b/app/routers/news/routes.py @@ -8,7 +8,7 @@ import app.services.database.orm.news as orm_news from app.routers.authentication import get_current_active_community -from app.schemas import News, NewsPublishStatus +from app.schemas import News, NewsWithPublishStatus from app.services.database.models import Community as DBCommunity from app.services.limiter import limiter @@ -39,10 +39,10 @@ def encode_email(email: str) -> str: def setup(): - router = APIRouter(prefix="/news", tags=["news"]) + router: APIRouter = APIRouter(prefix="/news", tags=["news"]) @router.post( - "", + path="", response_model=NewsCreateResponse, status_code=status.HTTP_200_OK, summary="News endpoint", @@ -98,44 +98,29 @@ async def get_news( return NewsGetResponse(news_list=news_list) @router.put( - "", - response_model=NewsGetResponse, + path="/{news_id}", status_code=status.HTTP_200_OK, summary="PUT News", - description="Updates news by query params and set publish value", + description="Updates news and sets publish value", ) - @limiter.limit("60/minute") + @limiter.limit(limit_value="60/minute") async def put_news( request: Request, current_community: Annotated[ DBCommunity, Depends(get_current_active_community) ], - publish_status: NewsPublishStatus, - id: str | None = None, - title: str | None = None, - content: str | None = None, - category: str | None = None, - user_email: str = str(Header(..., alias="user-email")), - source_url: str | None = None, - tags: str | None = None, - social_media_url: str | None = None, + news_id: str, + news: NewsWithPublishStatus, + user_email: str = Header(..., alias="user-email"), ): """ Get News endpoint that retrieves news filtered by user and query params. """ - news: dict = { - "id": id, - "title": title, - "content": content, - "category": category, - "user_email": user_email, - "source_url": source_url, - "tags": tags, - "social_media_url": social_media_url, - "publish": publish_status.publish, - } await orm_news.update_news( - session=request.app.db_session_factory, news=news + session=request.app.db_session_factory, + news=news.__dict__, + news_id=news_id, + user_email=user_email, ) return NewsUpdateResponse() @@ -146,11 +131,11 @@ async def put_news( summary="News like endpoint", description="Allows user to like a news item", ) - @limiter.limit("60/minute") + @limiter.limit(limit_value="60/minute") async def post_like( request: Request, current_community: Annotated[ - DBCommunity, Depends(get_current_active_community) + DBCommunity, Depends(dependency=get_current_active_community) ], news_id: str, user_email: str = Header(..., alias="user-email"), @@ -173,7 +158,7 @@ async def post_like( summary="News undo like endpoint", description="Allows user to undo a like to a news item", ) - @limiter.limit("60/minute") + @limiter.limit(limit_value="60/minute") async def delete_like( request: Request, current_community: Annotated[ diff --git a/app/schemas.py b/app/schemas.py index 322800c..3aaeef7 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -48,13 +48,12 @@ class News(BaseModel): title: str content: str category: str - tags: str | None = None source_url: str + tags: str | None = None social_media_url: str | None = None - likes: int = 0 -class NewsPublishStatus(BaseModel): +class NewsWithPublishStatus(News): publish: bool = False diff --git a/app/services/database/models/news.py b/app/services/database/models/news.py index c2508b5..7e79f96 100644 --- a/app/services/database/models/news.py +++ b/app/services/database/models/news.py @@ -26,6 +26,8 @@ class News(SQLModel, table=True): for this news. likes (int): Number of likes this news article has received. Defaults to 0. + publish (bool): Indicates whether the news is published or not. + Defaults to False. community_id (Optional[int]): Foreign key to the associated community (communities.id). diff --git a/app/services/database/orm/news.py b/app/services/database/orm/news.py index 346c124..d1a7ffc 100644 --- a/app/services/database/orm/news.py +++ b/app/services/database/orm/news.py @@ -16,7 +16,6 @@ async def create_news(session: AsyncSession, news: dict) -> None: source_url=news["source_url"], tags=news["tags"] or "", social_media_url=news["social_media_url"] or "", - likes=news["likes"], ) session.add(_news) await session.commit() @@ -45,8 +44,15 @@ async def get_news_by_query_params( return list(results.all()) -async def update_news(session: AsyncSession, news: dict) -> None: - statement = select(News).where(News.id == news["id"]) +async def update_news( + session: AsyncSession, + news: dict, + news_id: str, + user_email: str, +) -> None: + statement = select(News).where( + News.id == news_id and News.user_email == user_email + ) results = await session.exec(statement) news_item = results.first() if news_item: diff --git a/tests/test_news.py b/tests/test_news.py index fae72f2..49a80d8 100755 --- a/tests/test_news.py +++ b/tests/test_news.py @@ -4,7 +4,7 @@ import pytest import pytest_asyncio from fastapi import status -from httpx import AsyncClient +from httpx import AsyncClient, Response from sqlmodel import select from sqlmodel.ext.asyncio.session import AsyncSession @@ -101,8 +101,8 @@ async def test_post_news_endpoint( "category": "test_category", "source_url": "https://example.com/test-news", } - response = await async_client.post( - "/api/news", headers=valid_auth_headers, json=news_data + response: Response = await async_client.post( + url="/api/news", headers=valid_auth_headers, json=news_data ) assert response.status_code == status.HTTP_200_OK assert response.json() == {"status": "News Criada"} @@ -310,26 +310,23 @@ async def test_put_news_endpoint( "title": "updated title", "content": "updated content", "category": "updated_category", - "user_email": "updated_email@test.com", "source_url": "https://updated_url.com", "tags": "test_tag_updated", "social_media_url": "https://updated_social_media_url.com", } - response = await async_client.put( - "/api/news", - params={ - "id": stored_news.id, + response: Response = await async_client.put( + url=f"/api/news/{stored_news.id}", + headers=valid_auth_headers, + json={ "title": data["title"], "content": data["content"], "category": data["category"], - "user_email": data["user_email"], "source_url": data["source_url"], "tags": data["tags"], "social_media_url": data["social_media_url"], + "publish": True, }, - headers=valid_auth_headers, - json={"publish": True}, ) assert response.status_code == status.HTTP_200_OK @@ -339,7 +336,7 @@ async def test_put_news_endpoint( assert stored_news is not None assert stored_news.content == data["content"] assert stored_news.category == data["category"] - assert stored_news.user_email == data["user_email"] + assert stored_news.user_email == valid_auth_headers["user-email"] assert stored_news.source_url == data["source_url"] assert stored_news.tags == data["tags"] assert stored_news.social_media_url == data["social_media_url"] @@ -361,8 +358,8 @@ async def test_news_likes_endpoint( "source_url": "https://example.com/test-news", "social_media_url": "https://test.com/test_news", } - response = await async_client.post( - "/api/news", json=news_data, headers=valid_auth_headers + response: Response = await async_client.post( + url="/api/news", json=news_data, headers=valid_auth_headers ) assert response.status_code == status.HTTP_200_OK statement = select(News).where(News.title == news_data["title"]) @@ -374,8 +371,8 @@ async def test_news_likes_endpoint( emails = ["like@test.com", "like2@test.com"] # Add likes - response = await async_client.post( - f"/api/news/{stored_news.id}/like", + response: Response = await async_client.post( + url=f"/api/news/{stored_news.id}/like", json={"email": emails[0]}, headers={**valid_auth_headers, "user-email": emails[0]}, ) @@ -387,8 +384,8 @@ async def test_news_likes_endpoint( assert stored_news.likes == 1 assert stored_news.user_email_list == f"['{encode_email(emails[0])}']" - response = await async_client.post( - f"/api/news/{stored_news.id}/like", + response: Response = await async_client.post( + url=f"/api/news/{stored_news.id}/like", headers={**valid_auth_headers, "user-email": emails[1]}, ) assert response.status_code == status.HTTP_200_OK @@ -403,8 +400,8 @@ async def test_news_likes_endpoint( ) # Remove likes - response = await async_client.delete( - f"/api/news/{stored_news.id}/like", + response: Response = await async_client.delete( + url=f"/api/news/{stored_news.id}/like", headers={**valid_auth_headers, "user-email": emails[0]}, ) assert response.status_code == status.HTTP_200_OK @@ -415,8 +412,8 @@ async def test_news_likes_endpoint( assert stored_news.likes == 1 assert stored_news.user_email_list == f"['{encode_email(emails[1])}']" - response = await async_client.delete( - f"/api/news/{stored_news.id}/like", + response: Response = await async_client.delete( + url=f"/api/news/{stored_news.id}/like", headers={**valid_auth_headers, "user-email": emails[1]}, ) assert response.status_code == status.HTTP_200_OK From a497bb961359f431f1ad241be161df61bd05739f Mon Sep 17 00:00:00 2001 From: Arthur Mesquita Pickcius Date: Wed, 22 Oct 2025 20:30:57 -0300 Subject: [PATCH 5/5] Add test for unauthorized access in news endpoint --- app/routers/libraries/routes.py | 2 +- tests/conftest.py | 2 +- tests/test_news.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/app/routers/libraries/routes.py b/app/routers/libraries/routes.py index c455ecb..783ebd5 100644 --- a/app/routers/libraries/routes.py +++ b/app/routers/libraries/routes.py @@ -3,7 +3,6 @@ from fastapi import APIRouter, Header, HTTPException, Request, status from fastapi.params import Depends from pydantic import BaseModel -from services.encryption import encrypt_email from app.routers.authentication import get_current_active_community from app.schemas import Library as LibrarySchema @@ -20,6 +19,7 @@ ) from app.services.database.orm.library_request import insert_library_request from app.services.database.orm.subscription import upsert_multiple_subscription +from app.services.encryption import encrypt_email from app.services.limiter import limiter diff --git a/tests/conftest.py b/tests/conftest.py index bb76e81..93a5f92 100755 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -22,7 +22,7 @@ # --- Configurações do Banco de Dados em Memória para Testes --- # Usamos engine e AsyncSessionLocal apenas para os testes. # Isso garante que os testes são isolados e usam o banco de dados em memória. -TEST_DATABASE_URL = "sqlite+aiosqlite:///:memory:" +TEST_DATABASE_URL = "sqlite+aiosqlite:////tmp/pynewsdb.db" os.environ["ADMIN_USER"] = "ADMIN_USER" os.environ["ADMIN_PASSWORD"] = "ADMIN_PASSWORD" os.environ["ADMIN_EMAIL"] = "ADMIN_EMAIL" diff --git a/tests/test_news.py b/tests/test_news.py index 49a80d8..ec7bf87 100755 --- a/tests/test_news.py +++ b/tests/test_news.py @@ -423,3 +423,35 @@ async def test_news_likes_endpoint( assert stored_news is not None assert stored_news.likes == 0 assert stored_news.user_email_list == "[]" + + +@pytest.mark.asyncio +async def test_news_endpoint_blocks_unauthorized_access( + async_client: AsyncClient, +): + news_data = { + "title": "Test News", + "content": "Test news content.", + "category": "test_category", + "tags": "test_tag", + "source_url": "https://example.com/test-news", + "social_media_url": "https://test.com/test_news", + } + response: Response = await async_client.post( + url="/api/news", json=news_data + ) + assert response.status_code == status.HTTP_401_UNAUTHORIZED + + response: Response = await async_client.get(url="/api/news") + assert response.status_code == status.HTTP_401_UNAUTHORIZED + + response: Response = await async_client.put( + url="/api/news/1", json=news_data + ) + assert response.status_code == status.HTTP_401_UNAUTHORIZED + + response: Response = await async_client.post(url="/api/news/1/like") + assert response.status_code == status.HTTP_401_UNAUTHORIZED + + response: Response = await async_client.delete(url="/api/news/1/like") + assert response.status_code == status.HTTP_401_UNAUTHORIZED \ No newline at end of file