Skip to content

Commit 713618d

Browse files
author
Eric Sales De Andrade
committed
Full refactor/ add unit tests
1 parent 7c8a101 commit 713618d

File tree

10 files changed

+154
-95
lines changed

10 files changed

+154
-95
lines changed

.env

Lines changed: 0 additions & 6 deletions
This file was deleted.

Makefile

Lines changed: 0 additions & 8 deletions
This file was deleted.

app/database.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from sqlalchemy.ext.declarative import declarative_base
33
from sqlalchemy.orm import sessionmaker
44

5-
SQLITE_DATABASE_URL = "sqlite:///./note.db"
5+
SQLITE_DATABASE_URL = "sqlite:///./user.db"
66

77
engine = create_engine(
88
SQLITE_DATABASE_URL, echo=True, connect_args={"check_same_thread": False}

app/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from app import models, user
22
from fastapi import FastAPI
33
from fastapi.middleware.cors import CORSMiddleware
4-
from .database import engine
4+
from app.database import engine
55

66
models.Base.metadata.create_all(bind=engine)
77

@@ -20,7 +20,7 @@
2020
)
2121

2222

23-
app.include_router(user.router, tags=['Users'], prefix='/api/users')
23+
app.include_router(user.router, tags=["Users"], prefix="/api/users")
2424

2525

2626
@app.get("/api/healthchecker")

app/models.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from database import Base
1+
from app.database import Base
22
from sqlalchemy import TIMESTAMP, Column, String, Boolean
33
from sqlalchemy.sql import func
44
from fastapi_utils.guid_type import GUID, GUID_DEFAULT_SQLITE
@@ -11,7 +11,7 @@ class User(Base):
1111
last_name = Column(String, nullable=False)
1212
address = Column(String, nullable=True)
1313
activated = Column(Boolean, nullable=False, default=True)
14-
createdAt = Column(TIMESTAMP(timezone=True),
15-
nullable=False, server_default=func.now())
16-
updatedAt = Column(TIMESTAMP(timezone=True),
17-
default=None, onupdate=func.now())
14+
createdAt = Column(
15+
TIMESTAMP(timezone=True), nullable=False, server_default=func.now()
16+
)
17+
updatedAt = Column(TIMESTAMP(timezone=True), default=None, onupdate=func.now())

app/user.py

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
1-
import schemas, models
1+
import app.schemas as schemas, app.models as models
22
from sqlalchemy.orm import Session
3-
from fastapi import Depends, HTTPException, status, APIRouter, Response
4-
from database import get_db
3+
from fastapi import Depends, HTTPException, status, APIRouter
4+
from app.database import get_db
55

66
router = APIRouter()
77

88

99
@router.get("/", status_code=status.HTTP_200_OK)
10-
def get_users(db: Session = Depends(get_db), limit: int = 10, page: int = 1, search: str = ''):
10+
def get_users(
11+
db: Session = Depends(get_db), limit: int = 10, page: int = 1, search: str = ""
12+
):
1113
skip = (page - 1) * limit
1214

13-
users = db.query(models.User).filter(
14-
models.User.title.contains(search)).limit(limit).offset(skip).all()
15+
users = (
16+
db.query(models.User)
17+
.filter(models.User.first_name.contains(search))
18+
.limit(limit)
19+
.offset(skip)
20+
.all()
21+
)
1522
return {"Status": "Success", "Results": len(users), "Users": users}
1623

1724

18-
@router.post('/', status_code=status.HTTP_201_CREATED)
25+
@router.post("/", status_code=status.HTTP_201_CREATED)
1926
def create_user(payload: schemas.UserBaseSchema, db: Session = Depends(get_db)):
2027
new_user = models.User(**payload.dict())
2128
db.add(new_user)
@@ -25,37 +32,46 @@ def create_user(payload: schemas.UserBaseSchema, db: Session = Depends(get_db)):
2532

2633

2734
@router.patch("/{userId}", status_code=status.HTTP_202_ACCEPTED)
28-
def update_user(noteId: str, payload: schemas.UserBaseSchema, db: Session = Depends(get_db)):
29-
user_query = db.query(models.User).filter(models.User.id == noteId)
35+
def update_user(
36+
userId: str, payload: schemas.UserBaseSchema, db: Session = Depends(get_db)
37+
):
38+
user_query = db.query(models.User).filter(models.User.id == userId)
3039
db_user = user_query.first()
3140

3241
if not db_user:
33-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
34-
detail=f'No note with this id: {noteId} found')
42+
raise HTTPException(
43+
status_code=status.HTTP_404_NOT_FOUND,
44+
detail=f"No User with this id: {userId} found",
45+
)
3546
update_data = payload.dict(exclude_unset=True)
36-
user_query.filter(models.User.id == noteId).update(update_data,
37-
synchronize_session=False)
47+
user_query.filter(models.User.id == userId).update(
48+
update_data, synchronize_session=False
49+
)
3850
db.commit()
3951
db.refresh(db_user)
4052
return {"Status": "Success", "User": db_user}
4153

4254

4355
@router.get("/{userId}", status_code=status.HTTP_200_OK)
44-
def get_post(userId: str, db: Session = Depends(get_db)):
56+
def get_user(userId: str, db: Session = Depends(get_db)):
4557
user = db.query(models.User).filter(models.User.id == userId).first()
4658
if not user:
47-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
48-
detail=f"No User with this id: {userId} found")
59+
raise HTTPException(
60+
status_code=status.HTTP_404_NOT_FOUND,
61+
detail=f"No User with this id: `{userId}` found",
62+
)
4963
return {"Status": "Success", "User": user}
5064

5165

5266
@router.delete("/{userId}", status_code=status.HTTP_200_OK)
53-
def delete_post(userId: str, db: Session = Depends(get_db)):
67+
def delete_user(userId: str, db: Session = Depends(get_db)):
5468
user_query = db.query(models.User).filter(models.User.id == userId)
5569
user = user_query.first()
5670
if not user:
57-
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
58-
detail=f'No user with this id: {userId} found')
71+
raise HTTPException(
72+
status_code=status.HTTP_404_NOT_FOUND,
73+
detail=f"No user with this id: {userId} found",
74+
)
5975
user_query.delete(synchronize_session=False)
6076
db.commit()
6177
return {"Status": "Success", "Message": "User deleted successfully"}

docker-compose.yml

Lines changed: 0 additions & 14 deletions
This file was deleted.

pytest.ini

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[pytest]
2+
log_cli=true
3+
log_level=INFO
4+
addopts = -p no:warnings

requirements.txt

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,39 @@
11
anyio==3.6.2
2-
autopep8==1.7.1
3-
certifi==2022.9.24
4-
charset-normalizer==2.1.1
2+
autopep8==2.0.2
3+
certifi==2022.12.7
4+
charset-normalizer==3.1.0
55
click==8.1.3
66
colorama==0.4.6
7-
dnspython==2.2.1
8-
email-validator==1.3.0
9-
fastapi==0.85.1
7+
dnspython==2.3.0
8+
email-validator==1.3.1
9+
fastapi==0.94.1
1010
fastapi-utils==0.2.1
11-
greenlet==1.1.3.post0
11+
greenlet==2.0.2
1212
h11==0.14.0
1313
httptools==0.5.0
1414
idna==3.4
1515
itsdangerous==2.1.2
1616
Jinja2==3.1.2
17-
MarkupSafe==2.1.1
18-
orjson==3.8.1
17+
MarkupSafe==2.1.2
18+
orjson==3.8.7
1919
psycopg2==2.9.5
20-
pycodestyle==2.9.1
21-
pydantic==1.10.2
22-
python-dotenv==0.21.0
23-
python-multipart==0.0.5
20+
pycodestyle==2.10.0
21+
pydantic==1.10.6
22+
python-dotenv==1.0.0
23+
python-multipart==0.0.6
2424
PyYAML==6.0
25-
requests==2.28.1
25+
requests==2.28.2
2626
six==1.16.0
2727
sniffio==1.3.0
28-
SQLAlchemy==1.4.42
29-
starlette==0.20.4
28+
SQLAlchemy==1.4.46
29+
starlette==0.26.1
3030
tomli==2.0.1
31-
typing_extensions==4.4.0
32-
ujson==5.5.0
33-
urllib3==1.26.12
34-
uvicorn==0.18.3
35-
watchfiles==0.18.0
31+
typing_extensions==4.5.0
32+
ujson==5.7.0
33+
urllib3==1.26.15
34+
uvicorn==0.21.1
35+
watchfiles==0.18.1
3636
websockets==10.4
3737
pytest==7.2.2
38+
httpx==0.23.3
39+
black==23.1.0

tests/unit/test_crud_api.py

Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,100 @@
1-
import uuid
21
from fastapi.testclient import TestClient
3-
42
from app.main import app
3+
import uuid
54

65
client = TestClient(app)
7-
note_id = str(uuid.uuid4())
6+
7+
user_id = str(uuid.uuid4())
88

99

1010
def test_root():
1111
response = client.get("/api/healthchecker")
1212
assert response.status_code == 200
13-
assert response.json() == {"message": "Welcome to FastAPI with SQLAlchemy"}
13+
assert response.json() == {"message": "The API is LIVE!!"}
1414

1515

16-
def test_get_notes():
17-
response = client.get("/api/notes/")
16+
def test_create_user():
17+
sample_payload = {
18+
"id": user_id,
19+
"first_name": "PLACEHOLDER",
20+
"last_name": "PLACEHOLDER",
21+
"address": "PLACEHOLDER",
22+
"activated": False,
23+
"createdAt": "2023-03-17T00:04:32",
24+
}
25+
response = client.post("/api/users/", json=sample_payload)
26+
assert response.status_code == 201
27+
assert response.json() == {
28+
"Status": "Success",
29+
"User": {
30+
"first_name": "PLACEHOLDER",
31+
"last_name": "PLACEHOLDER",
32+
"activated": False,
33+
"createdAt": "2023-03-17T00:04:32",
34+
"id": user_id,
35+
"address": "PLACEHOLDER",
36+
"updatedAt": None,
37+
},
38+
}
39+
40+
41+
def test_get_user():
42+
response = client.get(f"/api/users/{user_id}")
1843
assert response.status_code == 200
19-
assert response.json() == {'status': 'success', 'results': 0, 'notes': []}
44+
assert response.json() == {
45+
"Status": "Success",
46+
"User": {
47+
"first_name": "PLACEHOLDER",
48+
"last_name": "PLACEHOLDER",
49+
"activated": False,
50+
"createdAt": "2023-03-17T00:04:32",
51+
"address": "PLACEHOLDER",
52+
"id": user_id,
53+
"updatedAt": None,
54+
},
55+
}
2056

2157

22-
def test_create_note():
58+
def test_update_user():
2359
sample_payload = {
24-
"id": note_id,
25-
"title": "string",
26-
"content": "string",
27-
"category": "string",
28-
"published": False,
29-
"createdAt": "2023-03-14T09:11:48.620Z",
30-
"updatedAt": "2023-03-14T09:11:48.620Z"
31-
}
32-
response = client.post("/api/notes/", json=sample_payload)
33-
assert response.status_code == 201
34-
assert response.json() == {'status': 'success', 'note': {'published': False, 'title': 'string', 'content': 'string', 'createdAt': '2023-03-14T09:11:48.620000', 'id': note_id, 'category': 'string', 'updatedAt': '2023-03-14T09:11:48.620000'}}
60+
"id": user_id,
61+
"first_name": "PLACEHOLDER2",
62+
"last_name": "PLACEHOLDER2",
63+
"address": "PLACEHOLDER2",
64+
"activated": True,
65+
"createdAt": "2023-03-17T00:04:32",
66+
"updatedAt": "2023-03-17T00:06:32",
67+
}
68+
response = client.patch(f"/api/users/{user_id}", json=sample_payload)
69+
assert response.status_code == 202
70+
assert response.json() == {
71+
"Status": "Success",
72+
"User": {
73+
"first_name": "PLACEHOLDER2",
74+
"last_name": "PLACEHOLDER2",
75+
"activated": True,
76+
"createdAt": "2023-03-17T00:04:32",
77+
"id": user_id,
78+
"address": "PLACEHOLDER2",
79+
"updatedAt": "2023-03-17T00:06:32",
80+
},
81+
}
82+
83+
84+
def test_delete_user():
85+
response = client.delete(f"/api/users/{user_id}")
86+
assert response.status_code == 200
87+
assert response.json() == {
88+
"Status": "Success",
89+
"Message": "User deleted successfully",
90+
}
91+
3592

93+
def test_get_user_not_found():
94+
response = client.get(
95+
f"/api/users/16303002-876a-4f39-ad16-e715f151bab3"
96+
) # GUID not in DB
97+
assert response.status_code == 404
98+
assert response.json() == {
99+
"detail": "No User with this id: `16303002-876a-4f39-ad16-e715f151bab3` found"
100+
}

0 commit comments

Comments
 (0)