Skip to content

Commit e732292

Browse files
authored
Merge pull request #40 from PythonFloripa/22-criar-endpoint-get-news
Feature/#22
2 parents bb56834 + a44d001 commit e732292

File tree

4 files changed

+208
-28
lines changed

4 files changed

+208
-28
lines changed

app/routers/news/routes.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
from fastapi import APIRouter, status
1+
from fastapi import APIRouter, Request, status
22
from pydantic import BaseModel
3+
from services.database.orm.news import get_news_by_query_params
34

45

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

89

10+
class NewsGetResponse(BaseModel):
11+
status: str = "Lista de News Obtida"
12+
news_list: list = []
13+
14+
915
def setup():
1016
router = APIRouter(prefix="/news", tags=["news"])
1117

@@ -16,10 +22,30 @@ def setup():
1622
summary="News endpoint",
1723
description="Creates news and returns a confirmation message",
1824
)
19-
async def news():
25+
async def post_news():
2026
"""
2127
News endpoint that creates news and returns a confirmation message.
2228
"""
2329
return NewsPostResponse()
2430

31+
@router.get(
32+
"",
33+
response_model=NewsGetResponse,
34+
status_code=status.HTTP_200_OK,
35+
summary="Get News",
36+
description="Retrieves news filtered by user and query params",
37+
)
38+
async def get_news(request: Request):
39+
"""
40+
Get News endpoint that retrieves news filtered by user and query params.
41+
"""
42+
news_list = await get_news_by_query_params(
43+
session=request.app.db_session_factory,
44+
id=request.query_params.get("id"),
45+
user_email=request.headers.get("user-email"),
46+
category=request.query_params.get("category"),
47+
tags=request.query_params.get("tags"),
48+
)
49+
return NewsGetResponse(news_list=news_list)
50+
2551
return router

app/services/database/orm/news.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from typing import Optional
2+
3+
from services.database.models import News
4+
from sqlmodel import select
5+
from sqlmodel.ext.asyncio.session import AsyncSession
6+
7+
8+
async def get_news_by_query_params(
9+
session: AsyncSession,
10+
user_email: Optional[str] = None,
11+
category: Optional[str] = None,
12+
tags: Optional[str] = None,
13+
id: Optional[str] = None,
14+
) -> list[News]:
15+
filters = []
16+
if user_email is not None:
17+
filters.append(News.user_email == user_email)
18+
if category is not None:
19+
filters.append(News.category == category)
20+
if tags is not None:
21+
filters.append(News.tags == tags)
22+
if id is not None:
23+
filters.append(News.id == id)
24+
25+
print("user_email:", user_email)
26+
print("Filters:", filters)
27+
28+
statement = select(News).where(*filters)
29+
results = await session.exec(statement)
30+
return results.all()

tests/conftest.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
from collections.abc import AsyncGenerator, Generator
2-
from unittest.mock import AsyncMock
1+
from collections.abc import AsyncGenerator
32

43
import pytest
54
import pytest_asyncio
@@ -10,7 +9,8 @@
109
from sqlmodel.ext.asyncio.session import AsyncSession
1110

1211
from app.main import app
13-
#from app.main import get_db_session
12+
13+
# from app.main import get_db_session
1414

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

4547

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

55+
5356
@pytest_asyncio.fixture
5457
async def test_app(session) -> FastAPI:
5558
mock_db_connection = session
56-
setattr(app, 'db_session_factory', mock_db_connection)
59+
setattr(app, "db_session_factory", mock_db_connection)
5760
return app
5861

5962

tests/test_news.py

Lines changed: 142 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,49 @@
1111

1212

1313
@pytest_asyncio.fixture
14-
async def community(session: AsyncSession):
14+
async def community(session: AsyncSession) -> Community:
1515
community = Community(username="admin", email="[email protected]", password="123")
1616
session.add(community)
1717
await session.commit()
1818
await session.refresh(community)
1919
return community
2020

2121

22+
@pytest_asyncio.fixture
23+
async def news_list(community: Community) -> list[News]:
24+
news_list = [
25+
News(
26+
title="Python 3.12 Lançado!",
27+
content="A nova versão do Python traz melhorias ...",
28+
category="release",
29+
user_email="[email protected]",
30+
source_url="https://python.org/news",
31+
tags="python, release, programming",
32+
social_media_url="https://linkedin.com/pythonista",
33+
community_id=community.id, # Usando o ID da comunidade do fixture
34+
),
35+
News(
36+
title="FastAPI 0.100 Lançado!",
37+
content="FastAPI agora suporta novas funcionalidades ...",
38+
category="release",
39+
user_email="[email protected]",
40+
source_url="https://fastapi.com/news",
41+
tags="fastapi, release, web",
42+
social_media_url="https://twitter.com/fastapi",
43+
likes=100,
44+
),
45+
]
46+
return news_list
47+
48+
2249
@pytest.mark.asyncio
23-
async def test_insert_news(session: AsyncSession, community: Community):
50+
async def test_insert_news(
51+
session: AsyncSession, community: Community, news_list: list
52+
):
2453
"""
2554
Testa a inserção de uma notícia no banco de dados.
2655
"""
27-
news = News(
28-
title="Python 3.12 Lançado!",
29-
content="A nova versão do Python traz melhorias ...",
30-
category="release",
31-
user_email="[email protected]",
32-
source_url="https://python.org/news",
33-
tags="python, release, programming",
34-
social_media_url="https://linkedin.com/pythonista",
35-
community_id=community.id, # Usando o ID da comunidade do fixture
36-
)
37-
session.add(news)
56+
session.add(news_list[0])
3857
await session.commit()
3958

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

5978

60-
# ADD like test case for News model
61-
6279
@pytest.mark.asyncio
63-
async def test_news_endpoint(
80+
async def test_post_news_endpoint(
6481
async_client: AsyncClient, mock_headers: Mapping[str, str]
6582
):
66-
"""Test the news endpoint returns correct status and version."""
83+
"""Test the news endpoint returns correct status."""
6784
response = await async_client.post("/api/news", headers=mock_headers)
6885

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

7289

7390
@pytest.mark.asyncio
74-
async def test_news_endpoint_without_auth(async_client: AsyncClient):
75-
"""Test the news endpoint without authentication headers."""
76-
response = await async_client.post("/api/news")
91+
async def test_get_news_endpoint(
92+
session: AsyncSession,
93+
async_client: AsyncClient,
94+
mock_headers: Mapping[str, str],
95+
news_list: list,
96+
):
97+
session.add(news_list[0])
98+
session.add(news_list[1])
99+
await session.commit()
100+
101+
"""Test the news endpoint returns correct status and version."""
102+
response = await async_client.get(
103+
"/api/news",
104+
headers=mock_headers,
105+
)
77106

78107
assert response.status_code == status.HTTP_200_OK
79-
assert response.json() == {"status": "News Criada"}
108+
assert "news_list" in response.json()
109+
assert len(response.json()["news_list"]) == 2
110+
111+
112+
@pytest.mark.asyncio
113+
async def test_get_news_by_category(
114+
session: AsyncSession,
115+
async_client: AsyncClient,
116+
mock_headers: Mapping[str, str],
117+
news_list: list,
118+
):
119+
# Add news to DB
120+
session.add_all(news_list)
121+
await session.commit()
122+
123+
# Filter by category
124+
response = await async_client.get(
125+
"/api/news",
126+
params={"category": "release"},
127+
headers={"Content-Type": "application/json"},
128+
)
129+
data = response.json()
130+
assert response.status_code == status.HTTP_200_OK
131+
assert "news_list" in data
132+
assert len(data["news_list"]) == 2
133+
titles = [news["title"] for news in data["news_list"]]
134+
assert "Python 3.12 Lançado!" in titles
135+
assert "FastAPI 0.100 Lançado!" in titles
136+
137+
138+
@pytest.mark.asyncio
139+
async def test_get_news_by_user_email(
140+
session: AsyncSession, async_client: AsyncClient, news_list: list
141+
):
142+
session.add_all(news_list)
143+
await session.commit()
144+
145+
response = await async_client.get(
146+
"/api/news",
147+
params={},
148+
headers={
149+
"Content-Type": "application/json",
150+
"user-email": "[email protected]",
151+
},
152+
)
153+
data = response.json()
154+
assert response.status_code == status.HTTP_200_OK
155+
assert len(data["news_list"]) == 1
156+
assert data["news_list"][0]["user_email"] == "[email protected]"
157+
assert data["news_list"][0]["title"] == "Python 3.12 Lançado!"
158+
159+
160+
@pytest.mark.asyncio
161+
async def test_get_news_by_id(
162+
session: AsyncSession,
163+
async_client: AsyncClient,
164+
mock_headers: Mapping[str, str],
165+
news_list: list,
166+
):
167+
session.add_all(news_list)
168+
await session.commit()
169+
# Get the id from DB
170+
statement = select(News).where(News.title == "Python 3.12 Lançado!")
171+
result = await session.exec(statement)
172+
news = result.first()
173+
response = await async_client.get(
174+
"/api/news",
175+
params={"id": news.id},
176+
headers=mock_headers,
177+
)
178+
data = response.json()
179+
assert response.status_code == status.HTTP_200_OK
180+
assert len(data["news_list"]) == 1
181+
assert data["news_list"][0]["id"] == news.id
182+
assert data["news_list"][0]["title"] == "Python 3.12 Lançado!"
183+
184+
185+
@pytest.mark.asyncio
186+
async def test_get_news_empty_result(
187+
async_client: AsyncClient, mock_headers: Mapping[str, str]
188+
):
189+
response = await async_client.get(
190+
"/api/news",
191+
params={"category": "notfound"},
192+
headers=mock_headers,
193+
)
194+
data = response.json()
195+
assert response.status_code == status.HTTP_200_OK
196+
assert "news_list" in data
197+
assert data["news_list"] == []
198+
199+
200+
# ADD like test case for News model

0 commit comments

Comments
 (0)