Skip to content

Commit

Permalink
feat: added auth module
Browse files Browse the repository at this point in the history
  • Loading branch information
black-redoc committed Jun 3, 2024
1 parent babd29f commit c1db25b
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 4 deletions.
12 changes: 8 additions & 4 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
from contextlib import asynccontextmanager

from src.tasks.router import router as task_router
from src.tasks import models
from src.tasks import models as tasks_model
from src.projects.router import router as project_router
from src.projects import models
from src.projects import models as projects_model
from src.users.router import router as users_router
from src.users import models as users_model
from src.settings.database import create_database


Expand All @@ -27,8 +29,9 @@ async def lifespan(app: FastAPI):

app = FastAPI(lifespan=lifespan)

models.Base.metadata.create_all(bind=engine)
models.Base.metadata.create_all(bind=engine)
tasks_model.Base.metadata.create_all(bind=engine)
projects_model.Base.metadata.create_all(bind=engine)
users_model.Base.metadata.create_all(bind=engine)


@app.get("/healthcheck")
Expand Down Expand Up @@ -62,3 +65,4 @@ async def db_session_middleware(request: Request, call_next):
)
app.include_router(task_router)
app.include_router(project_router)
app.include_router(users_router)
Empty file added src/users/__init__.py
Empty file.
12 changes: 12 additions & 0 deletions src/users/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from sqlalchemy import Column, Integer, String

from src.settings.database import Base


class User(Base):
__tablename__ = "users"

id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String, nullable=False)
email = Column(String, nullable=False)
password = Column(String, nullable=False)
31 changes: 31 additions & 0 deletions src/users/router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from fastapi import APIRouter, Depends, status
from sqlalchemy.orm import Session

from src.settings.database import SessionLocal
from . import schemas, service


def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()


router = APIRouter()


@router.post("/users/")
async def valid_user(user: schemas.UserSchema, db: Session = Depends(get_db)):
return service.validate_user(db, user.username)


@router.post(
"/login/",
)
async def login(
user: schemas.UserSchema,
db: Session = Depends(get_db),
):
return service.login(db, user)
10 changes: 10 additions & 0 deletions src/users/schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from pydantic import BaseModel, ConfigDict


class UserSchema(BaseModel):
id: int | None = None
username: str | None = None
email: str
password: str

model_config = ConfigDict(from_attributes=True)
27 changes: 27 additions & 0 deletions src/users/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from fastapi.responses import JSONResponse
from fastapi import status
from sqlalchemy.orm import Session

from . import models, schemas


def validate_user(db: Session, username: str):
user = db.query(models.User).first()
if username == user.username:
return JSONResponse(content={"is_user_valid": True})
return JSONResponse(content={"is_user_valid": False})


def login(db: Session, user: schemas.UserSchema):
stored_user = db.query(models.User).first()
if stored_user.email != user.email:
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content={"erorr": "Invalid credentials"},
)
if stored_user.password != user.password:
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content={"erorr": "Invalid credentials"},
)
return JSONResponse(content={"success": True})
81 changes: 81 additions & 0 deletions src/users/tests/test_router_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import os
import json

import pytest
from sqlalchemy.orm import Session
from fastapi.responses import JSONResponse
from fastapi import FastAPI
from fastapi.testclient import TestClient
from unittest.mock import patch
from src.settings.database import create_database, get_db
from src.users.router import router
from src.users.schemas import UserSchema
from src.users.models import User

app = FastAPI()
app.include_router(router)


@pytest.fixture
def client():
with TestClient(router) as client:
yield client


@pytest.fixture
def mock_db_session():
session = Session()
try:
yield session
session.rollback()
finally:
session.close()


app.dependency_overrides[get_db] = mock_db_session


@pytest.fixture(autouse=True)
def run_around_tests():
# Setup: can be used to initialize the database
create_database()

yield # this is where the testing happens

# Teardown
try:
os.remove("test.db")
except:
"""
Do nothing
"""


def test_db():
assert os.getenv("DATABASE_URL") == "sqlite:///./test.db"


def test_validate_user(client):
with patch(
"src.users.service.validate_user",
return_value=JSONResponse(content={"is_valid_user": True}),
):
response = client.post(
"/users/",
json={"email": "[email protected]", "password": "test_password"},
)
expected_status_code = 200
assert response.content == b'{"is_valid_user":true}'
assert response.status_code == expected_status_code


def test_login(client):
with patch(
"src.users.service.login", return_value=JSONResponse(content={"success": True})
):
response = client.post(
"/login/", json={"email": "[email protected]", "password": "test_password"}
)
expected_status_code = 200
assert response.status_code == expected_status_code
assert response.content == b'{"success":true}'
58 changes: 58 additions & 0 deletions src/users/tests/test_service_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import os
import json
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from unittest.mock import Mock
from sqlalchemy.orm import Session
from src.settings.database import create_database, get_db
from src.users import service, schemas
from src.users.router import router


app = FastAPI()
app.include_router(router)


@pytest.fixture
def client():
with TestClient(router) as client:
yield client


@pytest.fixture
def mock_db_session():
return Mock(spec=Session)


app.dependency_overrides[get_db] = mock_db_session


@pytest.fixture(autouse=True)
def run_around_tests():
# Setup: can be used to initialize the database
create_database()

yield # this is where the testing happens

# Teardown
try:
os.remove("test.db")
except:
"""
Do nothing
"""


def test_validate_user(mock_db_session):
username = "test_username"
tasks = service.validate_user(mock_db_session, username)
assert tasks is not None


def test_login(mock_db_session):
user = schemas.UserSchema(
username="test_username", email="[email protected]", password="test_password"
)
tasks = service.login(mock_db_session, user)
assert tasks is not None

0 comments on commit c1db25b

Please sign in to comment.