Skip to content
Open
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
30 changes: 28 additions & 2 deletions app/routers/news/routes.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
from fastapi import APIRouter, status
from fastapi import APIRouter, Request, status
from pydantic import BaseModel
from services.database.orm.news import get_news_by_query_params


class NewsPostResponse(BaseModel):
status: str = "News Criada"


class NewsGetResponse(BaseModel):
status: str = "Lista de News Obtida"
news_list: list = []


def setup():
router = APIRouter(prefix="/news", tags=["news"])

Expand All @@ -16,10 +22,30 @@ def setup():
summary="News endpoint",
description="Creates news and returns a confirmation message",
)
async def news():
async def post_news():
"""
News endpoint that creates news and returns a confirmation message.
"""
return NewsPostResponse()

@router.get(
"",
response_model=NewsGetResponse,
status_code=status.HTTP_200_OK,
summary="Get News",
description="Retrieves news filtered by user and query params",
)
async def get_news(request: Request):
"""
Get News endpoint that retrieves news filtered by user and query params.
"""
news_list = await get_news_by_query_params(
session=request.app.db_session_factory,
id=request.query_params.get("id"),
user_email=request.headers.get("user-email"),
category=request.query_params.get("category"),
tags=request.query_params.get("tags"),
)
return NewsGetResponse(news_list=news_list)

return router
30 changes: 30 additions & 0 deletions app/services/database/orm/news.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from typing import Optional

from services.database.models import News
from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession


async def get_news_by_query_params(
session: AsyncSession,
user_email: Optional[str] = None,
category: Optional[str] = None,
tags: Optional[str] = None,
id: Optional[str] = None,
) -> list[News]:
filters = []
if user_email is not None:
filters.append(News.user_email == user_email)
if category is not None:
filters.append(News.category == category)
if tags is not None:
filters.append(News.tags == tags)
if id is not None:
filters.append(News.id == id)

print("user_email:", user_email)
print("Filters:", filters)

statement = select(News).where(*filters)
results = await session.exec(statement)
return results.all()
13 changes: 8 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from collections.abc import AsyncGenerator, Generator
from unittest.mock import AsyncMock
from collections.abc import AsyncGenerator

import pytest
import pytest_asyncio
Expand All @@ -10,7 +9,8 @@
from sqlmodel.ext.asyncio.session import AsyncSession

from app.main import app
#from app.main import get_db_session

# from app.main import get_db_session

# Importar todos os modelos SQLModel a serem usados
# (necessários para as validações de modelo)
Expand Down Expand Up @@ -40,7 +40,9 @@ async def get_db_session_test() -> AsyncGenerator[AsyncSession, None]:
async def setup_database():
async with test_engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.create_all)
yield test_engine
yield test_engine
async with test_engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.drop_all)


@pytest_asyncio.fixture(scope="function")
Expand All @@ -50,10 +52,11 @@ async def session() -> AsyncGenerator[AsyncSession, None]:
yield session
await session.close()


@pytest_asyncio.fixture
async def test_app(session) -> FastAPI:
mock_db_connection = session
setattr(app, 'db_session_factory', mock_db_connection)
setattr(app, "db_session_factory", mock_db_connection)
return app


Expand Down
163 changes: 142 additions & 21 deletions tests/test_news.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,49 @@


@pytest_asyncio.fixture
async def community(session: AsyncSession):
async def community(session: AsyncSession) -> Community:
community = Community(username="admin", email="[email protected]", password="123")
session.add(community)
await session.commit()
await session.refresh(community)
return community


@pytest_asyncio.fixture
async def news_list(community: Community) -> list[News]:
news_list = [
News(
title="Python 3.12 Lançado!",
content="A nova versão do Python traz melhorias ...",
category="release",
user_email="[email protected]",
source_url="https://python.org/news",
tags="python, release, programming",
social_media_url="https://linkedin.com/pythonista",
community_id=community.id, # Usando o ID da comunidade do fixture
),
News(
title="FastAPI 0.100 Lançado!",
content="FastAPI agora suporta novas funcionalidades ...",
category="release",
user_email="[email protected]",
source_url="https://fastapi.com/news",
tags="fastapi, release, web",
social_media_url="https://twitter.com/fastapi",
likes=100,
),
]
return news_list


@pytest.mark.asyncio
async def test_insert_news(session: AsyncSession, community: Community):
async def test_insert_news(
session: AsyncSession, community: Community, news_list: list
):
"""
Testa a inserção de uma notícia no banco de dados.
"""
news = News(
title="Python 3.12 Lançado!",
content="A nova versão do Python traz melhorias ...",
category="release",
user_email="[email protected]",
source_url="https://python.org/news",
tags="python, release, programming",
social_media_url="https://linkedin.com/pythonista",
community_id=community.id, # Usando o ID da comunidade do fixture
)
session.add(news)
session.add(news_list[0])
await session.commit()

statement = select(News).where(News.title == "Python 3.12 Lançado!")
Expand All @@ -57,23 +76,125 @@ async def test_insert_news(session: AsyncSession, community: Community):
assert found_news.updated_at >= found_news.created_at


# ADD like test case for News model

@pytest.mark.asyncio
async def test_news_endpoint(
async def test_post_news_endpoint(
async_client: AsyncClient, mock_headers: Mapping[str, str]
):
"""Test the news endpoint returns correct status and version."""
"""Test the news endpoint returns correct status."""
response = await async_client.post("/api/news", headers=mock_headers)

assert response.status_code == status.HTTP_200_OK
assert response.json() == {"status": "News Criada"}


@pytest.mark.asyncio
async def test_news_endpoint_without_auth(async_client: AsyncClient):
"""Test the news endpoint without authentication headers."""
response = await async_client.post("/api/news")
async def test_get_news_endpoint(
session: AsyncSession,
async_client: AsyncClient,
mock_headers: Mapping[str, str],
news_list: list,
):
session.add(news_list[0])
session.add(news_list[1])
await session.commit()

"""Test the news endpoint returns correct status and version."""
response = await async_client.get(
"/api/news",
headers=mock_headers,
)

assert response.status_code == status.HTTP_200_OK
assert response.json() == {"status": "News Criada"}
assert "news_list" in response.json()
assert len(response.json()["news_list"]) == 2


@pytest.mark.asyncio
async def test_get_news_by_category(
session: AsyncSession,
async_client: AsyncClient,
mock_headers: Mapping[str, str],
news_list: list,
):
# Add news to DB
session.add_all(news_list)
await session.commit()

# Filter by category
response = await async_client.get(
"/api/news",
params={"category": "release"},
headers={"Content-Type": "application/json"},
)
data = response.json()
assert response.status_code == status.HTTP_200_OK
assert "news_list" in data
assert len(data["news_list"]) == 2
titles = [news["title"] for news in data["news_list"]]
assert "Python 3.12 Lançado!" in titles
assert "FastAPI 0.100 Lançado!" in titles


@pytest.mark.asyncio
async def test_get_news_by_user_email(
session: AsyncSession, async_client: AsyncClient, news_list: list
):
session.add_all(news_list)
await session.commit()

response = await async_client.get(
"/api/news",
params={},
headers={
"Content-Type": "application/json",
"user-email": "[email protected]",
},
)
data = response.json()
assert response.status_code == status.HTTP_200_OK
assert len(data["news_list"]) == 1
assert data["news_list"][0]["user_email"] == "[email protected]"
assert data["news_list"][0]["title"] == "Python 3.12 Lançado!"


@pytest.mark.asyncio
async def test_get_news_by_id(
session: AsyncSession,
async_client: AsyncClient,
mock_headers: Mapping[str, str],
news_list: list,
):
session.add_all(news_list)
await session.commit()
# Get the id from DB
statement = select(News).where(News.title == "Python 3.12 Lançado!")
result = await session.exec(statement)
news = result.first()
response = await async_client.get(
"/api/news",
params={"id": news.id},
headers=mock_headers,
)
data = response.json()
assert response.status_code == status.HTTP_200_OK
assert len(data["news_list"]) == 1
assert data["news_list"][0]["id"] == news.id
assert data["news_list"][0]["title"] == "Python 3.12 Lançado!"


@pytest.mark.asyncio
async def test_get_news_empty_result(
async_client: AsyncClient, mock_headers: Mapping[str, str]
):
response = await async_client.get(
"/api/news",
params={"category": "notfound"},
headers=mock_headers,
)
data = response.json()
assert response.status_code == status.HTTP_200_OK
assert "news_list" in data
assert data["news_list"] == []


# ADD like test case for News model