Skip to content

Commit 7e4f68b

Browse files
Jade WibbelsJade Wibbels
authored andcommitted
better tests and create conditions
1 parent ee23e8f commit 7e4f68b

File tree

6 files changed

+388
-215
lines changed

6 files changed

+388
-215
lines changed

app/routes/posts.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import boto3
44
from botocore.exceptions import ClientError
55

6+
from app.routes.users import is_existing_user_id
7+
68
router = APIRouter()
79

810
# Initialize the DynamoDB client
@@ -17,6 +19,14 @@ class Post(BaseModel):
1719
author_id: str
1820

1921

22+
def is_existing_post_id(post_Id: str):
23+
try:
24+
response = table.scan(FilterExpression="Id = :i", ExpressionAttributeValues={":i": post_Id})
25+
return len(response["Items"]) == 1
26+
except ClientError as e:
27+
raise HTTPException(status_code=500, detail=e.response["Error"]["Message"])
28+
29+
2030
@router.get("/")
2131
def read_posts():
2232
"""
@@ -72,6 +82,10 @@ def create_post(post: Post):
7282
``{"message": "Post created"}``
7383
"""
7484
try:
85+
if not is_existing_user_id(post.author_id):
86+
raise HTTPException(status_code=400, detail="Cannot create post for unknown User Id.")
87+
if is_existing_post_id(post.Id):
88+
raise HTTPException(status_code=400, detail="Cannot create post for duplicate Id.")
7589
table.put_item(Item={k: v for k, v in post.model_dump().items()})
7690

7791
return {"message": "Post created"}
@@ -97,6 +111,8 @@ def update_post(Id: str, post: Post):
97111
``{"message": "Post with id {Id} updated", "updated_attributes": {updated_attributes}}``
98112
"""
99113
try:
114+
if not is_existing_post_id(post.Id):
115+
raise HTTPException(status_code=400, detail="Cannot update post for unknown Id.")
100116
response = table.update_item(
101117
Key={"Id": Id},
102118
UpdateExpression="set title=:t, content=:c, author_id=:a",
@@ -123,6 +139,8 @@ def delete_post(Id: str):
123139
``{"message":"Post with id 1 deleted"}
124140
"""
125141
try:
142+
if not is_existing_post_id(Id):
143+
raise HTTPException(status_code=400, detail="Cannot delete post for unknown Id.")
126144
table.delete_item(Key={"Id": Id})
127145
return {"message": f"Post with id {Id} deleted"}
128146
except ClientError as e:

app/routes/users.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,36 @@ class User(BaseModel):
1717
email_address: str
1818

1919

20+
def is_existing_user_id(user_Id: str):
21+
try:
22+
response = table.scan(FilterExpression="Id = :i", ExpressionAttributeValues={":i": user_Id})
23+
return len(response["Items"]) == 1
24+
except ClientError as e:
25+
raise HTTPException(status_code=500, detail=e.response["Error"]["Message"])
26+
27+
28+
def is_existing_user_name(user_name: str):
29+
try:
30+
response = table.scan(
31+
FilterExpression="user_name = :u",
32+
ExpressionAttributeValues={":u": user_name},
33+
)
34+
return len(response["Items"]) > 0
35+
except ClientError as e:
36+
raise HTTPException(status_code=500, detail=e.response["Error"]["Message"])
37+
38+
39+
def is_existing_email_address(email_address: str):
40+
try:
41+
response = table.scan(
42+
FilterExpression="email_address = :e",
43+
ExpressionAttributeValues={":e": email_address},
44+
)
45+
return len(response["Items"]) > 0
46+
except ClientError as e:
47+
raise HTTPException(status_code=500, detail=e.response["Error"]["Message"])
48+
49+
2050
@router.post("/")
2151
def create_user(user: User):
2252
"""
@@ -33,6 +63,12 @@ def create_user(user: User):
3363
Response: ``{"message":"User created"}``
3464
"""
3565
try:
66+
# Check if user_name or email_address already exists
67+
if is_existing_user_name(user.user_name):
68+
raise HTTPException(status_code=400, detail="User name already exists")
69+
if is_existing_email_address(user.email_address):
70+
raise HTTPException(status_code=400, detail="Email address already exists")
71+
3672
table.put_item(
3773
Item={
3874
"Id": user.Id,
@@ -47,7 +83,7 @@ def create_user(user: User):
4783

4884

4985
@router.get("/{Id}")
50-
def read_user(Id: str):
86+
def read_user_by_id(Id: str):
5187
"""
5288
Reads a user from the DynamoDB table "users" by Id
5389
@@ -98,6 +134,8 @@ def update_user(Id: str, user: User):
98134
Response: ``{"message":"User with id 1 updated","updated_attributes": ...}``
99135
"""
100136
try:
137+
if not is_existing_user_id(Id):
138+
raise HTTPException(status_code=400, detail="User Id not found.")
101139
response = table.update_item(
102140
Key={"Id": Id},
103141
UpdateExpression="set user_name=:u, password=:p, email_address=:e",
@@ -126,6 +164,8 @@ def delete_user(Id: str):
126164
Response: ``{"message":"User with id 1 deleted"}``
127165
"""
128166
try:
167+
if not is_existing_user_id(Id):
168+
raise HTTPException(status_code=400, detail="User Id not found.")
129169
table.delete_item(Key={"Id": Id})
130170
return {"message": f"User with id {Id} deleted"}
131171
except ClientError as e:

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
moto == 5.0.22
12
pytest
23
pytest-asyncio
34
pytest-cov

tests/conftest.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
import pytest
2-
from botocore.stub import Stubber
32
from fastapi.testclient import TestClient
3+
from moto import mock_aws
4+
import boto3
45

56
from app.main import app
67

78

89
@pytest.fixture
9-
def client():
10+
def client(ddb_client):
11+
dynamodb = get_dynamodb(ddb_client) # noqa F841
1012
with TestClient(app) as client:
1113
yield client
1214

1315

14-
@pytest.fixture
15-
def mock_dynamodb(request):
16-
table = request.param
17-
with Stubber(table.meta.client) as stubber:
18-
yield stubber
19-
stubber.assert_no_pending_responses()
16+
@pytest.fixture(scope="function")
17+
def ddb_client():
18+
with mock_aws():
19+
yield boto3.client("dynamodb")
20+
21+
22+
def get_dynamodb(ddb_client):
23+
tables = ["users", "posts"]
24+
for table_name in tables:
25+
ddb_client.create_table(
26+
TableName=table_name,
27+
KeySchema=[{"AttributeName": "Id", "KeyType": "HASH"}],
28+
AttributeDefinitions=[{"AttributeName": "Id", "AttributeType": "S"}],
29+
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
30+
)

0 commit comments

Comments
 (0)