Skip to content

Commit ba8d0c7

Browse files
committed
updates
1 parent 914ee94 commit ba8d0c7

File tree

6 files changed

+214
-13
lines changed

6 files changed

+214
-13
lines changed

project/app/api/crud.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
from typing import List, Union
22

3-
from app.models.pydantic import SummaryPayloadSchema
43
from app.models.tortoise import TextSummary
54

5+
from app.models.pydantic import ( # isort:skip
6+
SummaryPayloadSchema,
7+
SummaryUpdatePayloadSchema,
8+
)
9+
610

711
async def get(id: int) -> Union[dict, None]:
812
summary = await TextSummary.filter(id=id).first().values()
@@ -23,3 +27,18 @@ async def post(payload: SummaryPayloadSchema) -> int:
2327
)
2428
await summary.save()
2529
return summary.id
30+
31+
32+
async def put(id: int, payload: SummaryUpdatePayloadSchema) -> Union[dict, None]:
33+
summary = await TextSummary.filter(id=id).update(
34+
url=payload.url, summary=payload.summary
35+
)
36+
if summary:
37+
updated_summary = await TextSummary.filter(id=id).first().values()
38+
return updated_summary
39+
return None
40+
41+
42+
async def delete(id: int) -> int:
43+
summary = await TextSummary.filter(id=id).first().delete()
44+
return summary

project/app/api/ping.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
@router.get("/ping")
99
async def pong(settings: Settings = Depends(get_settings)):
1010
return {
11-
"ping": "pong",
11+
"ping": "pong!",
1212
"environment": settings.environment,
1313
"testing": settings.testing,
1414
}

project/app/api/summaries.py

+30-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
from typing import List
22

3-
from fastapi import APIRouter, HTTPException
3+
from fastapi import APIRouter, HTTPException, Path
44

55
from app.api import crud
6-
from app.models.pydantic import SummaryPayloadSchema, SummaryResponseSchema
76
from app.models.tortoise import SummarySchema
87

8+
from app.models.pydantic import ( # isort:skip
9+
SummaryPayloadSchema,
10+
SummaryResponseSchema,
11+
SummaryUpdatePayloadSchema,
12+
)
13+
914
router = APIRouter()
1015

1116

1217
@router.get("/{id}/", response_model=SummarySchema)
13-
async def read_summary(id: int) -> SummarySchema:
18+
async def read_summary(id: int = Path(..., gt=0)) -> SummarySchema:
1419
summary = await crud.get(id)
1520
if not summary:
1621
raise HTTPException(status_code=404, detail="Summary not found")
@@ -29,3 +34,25 @@ async def create_summary(payload: SummaryPayloadSchema) -> SummaryResponseSchema
2934

3035
response_object = {"id": summary_id, "url": payload.url}
3136
return response_object
37+
38+
39+
@router.put("/{id}/", response_model=SummarySchema)
40+
async def update_summary(
41+
payload: SummaryUpdatePayloadSchema, id: int = Path(..., gt=0)
42+
) -> SummarySchema:
43+
summary = await crud.put(id, payload)
44+
if not summary:
45+
raise HTTPException(status_code=404, detail="Summary not found")
46+
47+
return summary
48+
49+
50+
@router.delete("/{id}/", response_model=SummaryResponseSchema)
51+
async def delete_summary(id: int = Path(..., gt=0)) -> SummaryResponseSchema:
52+
summary = await crud.get(id)
53+
if not summary:
54+
raise HTTPException(status_code=404, detail="Summary not found")
55+
56+
await crud.delete(id)
57+
58+
return summary

project/app/models/pydantic.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
from pydantic import BaseModel
1+
from pydantic import AnyHttpUrl, BaseModel
22

33

44
class SummaryPayloadSchema(BaseModel):
5-
url: str
5+
url: AnyHttpUrl
66

77

88
class SummaryResponseSchema(SummaryPayloadSchema):
99
id: int
10+
11+
12+
class SummaryUpdatePayloadSchema(SummaryPayloadSchema):
13+
summary: str

project/tests/test_ping.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
def test_ping(test_app):
22
response = test_app.get("/ping")
33
assert response.status_code == 200
4-
assert response.json() == {"environment": "dev", "ping": "pong", "testing": True}
4+
assert response.json() == {"environment": "dev", "ping": "pong!", "testing": True}

project/tests/test_summaries.py

+156-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import json
22

3+
import pytest
4+
35

46
def test_create_summary(test_app_with_db):
57
response = test_app_with_db.post(
6-
"/summaries/", data=json.dumps({"url": "https://foo.bar"})
8+
"/summaries/", data=json.dumps({"url": "https://foo.bar/"})
79
)
810

911
assert response.status_code == 201
10-
assert response.json()["url"] == "https://foo.bar"
12+
assert response.json()["url"] == "https://foo.bar/"
1113

1214

1315
def test_create_summaries_invalid_json(test_app):
@@ -24,10 +26,16 @@ def test_create_summaries_invalid_json(test_app):
2426
]
2527
}
2628

29+
response = test_app.post("/summaries/", data=json.dumps({"url": "invalid://url"}))
30+
assert response.status_code == 422
31+
assert (
32+
response.json()["detail"][0]["msg"] == "URL scheme should be 'http' or 'https'"
33+
)
34+
2735

2836
def test_read_summary(test_app_with_db):
2937
response = test_app_with_db.post(
30-
"/summaries/", data=json.dumps({"url": "https://foo.bar"})
38+
"/summaries/", data=json.dumps({"url": "https://foo.bar/"})
3139
)
3240
summary_id = response.json()["id"]
3341

@@ -36,7 +44,7 @@ def test_read_summary(test_app_with_db):
3644

3745
response_dict = response.json()
3846
assert response_dict["id"] == summary_id
39-
assert response_dict["url"] == "https://foo.bar"
47+
assert response_dict["url"] == "https://foo.bar/"
4048
assert response_dict["summary"]
4149
assert response_dict["created_at"]
4250

@@ -46,10 +54,24 @@ def test_read_summary_incorrect_id(test_app_with_db):
4654
assert response.status_code == 404
4755
assert response.json()["detail"] == "Summary not found"
4856

57+
response = test_app_with_db.get("/summaries/0/")
58+
assert response.status_code == 422
59+
assert response.json() == {
60+
"detail": [
61+
{
62+
"ctx": {"gt": 0},
63+
"input": "0",
64+
"loc": ["path", "id"],
65+
"msg": "Input should be greater than 0",
66+
"type": "greater_than",
67+
}
68+
]
69+
}
70+
4971

5072
def test_read_all_summaries(test_app_with_db):
5173
response = test_app_with_db.post(
52-
"/summaries/", data=json.dumps({"url": "https://foo.bar"})
74+
"/summaries/", data=json.dumps({"url": "https://foo.bar/"})
5375
)
5476
summary_id = response.json()["id"]
5577

@@ -58,3 +80,132 @@ def test_read_all_summaries(test_app_with_db):
5880

5981
response_list = response.json()
6082
assert len(list(filter(lambda d: d["id"] == summary_id, response_list))) == 1
83+
84+
85+
def test_remove_summary(test_app_with_db):
86+
response = test_app_with_db.post(
87+
"/summaries/", data=json.dumps({"url": "https://foo.bar/"})
88+
)
89+
summary_id = response.json()["id"]
90+
91+
response = test_app_with_db.delete(f"/summaries/{summary_id}/")
92+
assert response.status_code == 200
93+
assert response.json() == {"id": summary_id, "url": "https://foo.bar/"}
94+
95+
96+
def test_remove_summary_incorrect_id(test_app_with_db):
97+
response = test_app_with_db.delete("/summaries/999/")
98+
assert response.status_code == 404
99+
assert response.json()["detail"] == "Summary not found"
100+
101+
response = test_app_with_db.delete("/summaries/0/")
102+
assert response.status_code == 422
103+
assert response.json() == {
104+
"detail": [
105+
{
106+
"ctx": {"gt": 0},
107+
"input": "0",
108+
"loc": ["path", "id"],
109+
"msg": "Input should be greater than 0",
110+
"type": "greater_than",
111+
}
112+
]
113+
}
114+
115+
116+
def test_update_summary(test_app_with_db):
117+
response = test_app_with_db.post(
118+
"/summaries/", data=json.dumps({"url": "https://foo.bar/"})
119+
)
120+
summary_id = response.json()["id"]
121+
122+
response = test_app_with_db.put(
123+
f"/summaries/{summary_id}/",
124+
data=json.dumps({"url": "https://foo.bar/", "summary": "updated!"}),
125+
)
126+
assert response.status_code == 200
127+
128+
response_dict = response.json()
129+
assert response_dict["id"] == summary_id
130+
assert response_dict["url"] == "https://foo.bar/"
131+
assert response_dict["summary"] == "updated!"
132+
assert response_dict["created_at"]
133+
134+
135+
@pytest.mark.parametrize(
136+
"summary_id, payload, status_code, detail",
137+
[
138+
[
139+
999,
140+
{"url": "https://foo.bar/", "summary": "updated!"},
141+
404,
142+
"Summary not found",
143+
],
144+
[
145+
0,
146+
{"url": "https://foo.bar/", "summary": "updated!"},
147+
422,
148+
[
149+
{
150+
"type": "greater_than",
151+
"loc": ["path", "id"],
152+
"msg": "Input should be greater than 0",
153+
"input": "0",
154+
"ctx": {"gt": 0},
155+
}
156+
],
157+
],
158+
[
159+
1,
160+
{},
161+
422,
162+
[
163+
{
164+
"type": "missing",
165+
"loc": ["body", "url"],
166+
"msg": "Field required",
167+
"input": {},
168+
},
169+
{
170+
"type": "missing",
171+
"loc": ["body", "summary"],
172+
"msg": "Field required",
173+
"input": {},
174+
},
175+
],
176+
],
177+
[
178+
1,
179+
{"url": "https://foo.bar/"},
180+
422,
181+
[
182+
{
183+
"type": "missing",
184+
"loc": ["body", "summary"],
185+
"msg": "Field required",
186+
"input": {"url": "https://foo.bar/"},
187+
}
188+
],
189+
],
190+
],
191+
)
192+
def test_update_summary_invalid(
193+
test_app_with_db, summary_id, payload, status_code, detail
194+
):
195+
response = test_app_with_db.put(
196+
f"/summaries/{summary_id}/", data=json.dumps(payload)
197+
)
198+
assert response.status_code == status_code
199+
print(response.json()["detail"])
200+
assert response.json()["detail"] == detail
201+
202+
203+
def test_update_summary_invalid_url(test_app):
204+
response = test_app.put(
205+
"/summaries/1/",
206+
data=json.dumps({"url": "invalid://url", "summary": "updated!"}),
207+
)
208+
assert response.status_code == 422
209+
assert (
210+
response.json()["detail"][0]["msg"] == "URL scheme should be 'http' or 'https'"
211+
)

0 commit comments

Comments
 (0)