Skip to content

Commit

Permalink
Merge pull request #81 from fedecarboni7/tests
Browse files Browse the repository at this point in the history
Refactor Unit Tests
  • Loading branch information
fedecarboni7 authored Sep 17, 2024
2 parents 678db34 + 2203625 commit 9213d48
Show file tree
Hide file tree
Showing 13 changed files with 446 additions and 211 deletions.
1 change: 1 addition & 0 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
env:
TURSO_DATABASE_URL: ${{ secrets.TURSO_DATABASE_URL }}
TURSO_AUTH_TOKEN: ${{ secrets.TURSO_AUTH_TOKEN }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}

steps:
- uses: actions/checkout@v4
Expand Down
6 changes: 2 additions & 4 deletions app/db/database.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import os

from sqlalchemy import create_engine
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import sessionmaker

from app.config.logging_config import logger
from app.db.models import Base

LOCAL_DB = os.getenv("LOCAL_DB", "").lower() == "true"

class Base(DeclarativeBase):
pass
LOCAL_DB = os.getenv("LOCAL_DB", "").lower() == "true"

if LOCAL_DB:
logger.info("Using local database")
Expand Down
7 changes: 4 additions & 3 deletions app/db/models.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from passlib.hash import pbkdf2_sha256
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.orm import DeclarativeBase, relationship

from app.db.database import Base

class Base(DeclarativeBase):
pass

class User(Base):
__tablename__ = "users"
Expand Down
13 changes: 13 additions & 0 deletions app/db/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,17 @@ class PlayerCreate(BaseModel):
habilidad_arquero: int
fuerza_cuerpo: int
vision: int

class PlayerResponse(BaseModel):
id: int
name: str
velocidad: int
resistencia: int
control: int
pases: int
tiro: int
defensa: int
habilidad_arquero: int
fuerza_cuerpo: int
vision: int
user_id: int
4 changes: 2 additions & 2 deletions app/routes/auth_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ async def signup(

user = execute_with_retries(query_user, db, username)
if user:
raise ValueError("Usuario ya registrado")
return templates.TemplateResponse(request=request, name="signup.html", context={"error": "Usuario ya registrado"}, status_code=409)

validate_password(password)
except ValueError as e:
Expand Down Expand Up @@ -82,7 +82,7 @@ async def login(
return HTMLResponse("Error al acceder a la base de datos. Inténtalo de nuevo más tarde.", status_code=500)

if not user or not user.verify_password(password):
return templates.TemplateResponse(request=request, name="login.html", context={"error": "Usuario o contraseña incorrectos"})
return templates.TemplateResponse(request=request, name="login.html", context={"error": "Usuario o contraseña incorrectos"}, status_code=401)

request.session["user_id"] = user.id
return RedirectResponse(url="/", status_code=302)
Expand Down
5 changes: 2 additions & 3 deletions app/routes/main_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ async def submit_form(
defensa=int(list_players[i+7][1]),
habilidad_arquero=int(list_players[i+8][1]),
fuerza_cuerpo=int(list_players[i+9][1]),
vision=int(list_players[i+10][1]),
user_id=current_user_id
vision=int(list_players[i+10][1])
)
player_data.append(player)

Expand All @@ -103,7 +102,7 @@ async def submit_form(
for key, value in player.model_dump().items():
setattr(db_player, key, value)
else:
players_to_add.append(Player(**player.model_dump()))
players_to_add.append(Player(**player.model_dump(), user_id=current_user_id))

if players_to_add:
db.add_all(players_to_add)
Expand Down
20 changes: 17 additions & 3 deletions app/routes/player_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from app.db.database import get_db
from app.db.database_utils import execute_with_retries, execute_write_with_retries, query_player, query_players
from app.db.models import Player, User
from app.db.schemas import PlayerCreate
from app.db.schemas import PlayerCreate, PlayerResponse
from app.utils.auth import get_current_user

router = APIRouter()
Expand Down Expand Up @@ -47,7 +47,7 @@ def get_player(
return HTMLResponse("Error al acceder a la base de datos. Inténtalo de nuevo más tarde.", status_code=500)

if player is None:
raise HTTPException(status_code=404, detail="Player not found")
return HTMLResponse("Player not found", status_code=404)
return player


Expand Down Expand Up @@ -122,4 +122,18 @@ def get_players(
except OperationalError:
return HTMLResponse("Error al acceder a la base de datos. Inténtalo de nuevo más tarde.", status_code=500)

return players
return players

@router.post("/player", response_model=PlayerResponse)
def create_player(
player: PlayerCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
) -> PlayerResponse:
if not current_user:
return HTMLResponse("No hay un usuario autenticado", status_code=401)
db_player = Player(**player.model_dump(), user_id=current_user.id)
db.add(db_player)
db.commit()
db.refresh(db_player)
return db_player
74 changes: 74 additions & 0 deletions tests/config/test_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import pytest

from fastapi.testclient import TestClient

from app.main import app
from app.db.database import get_db

@pytest.fixture(scope="module")
def client(db):
def override_get_db():
try:
yield db
finally:
db.rollback()

app.dependency_overrides[get_db] = override_get_db
with TestClient(app) as client:
yield client
app.dependency_overrides.clear()

@pytest.fixture
def unauthorized_client(client):
# Registra e inicia sesión un usuario para la prueba
response = client.post("/signup", data={"username": "loginuser", "password": "Loginpassword123"}, follow_redirects=False)
if response.status_code == 409:
response = client.post("/login", data={"username": "loginuser", "password": "Loginpassword123"}, follow_redirects=False)
assert response.status_code == 302
assert response.headers["location"] == "/"
return client

@pytest.fixture
def authorized_client(client):
# Registra e inicia sesión un usuario para la prueba
response = client.post("/signup", data={"username": "admin", "password": "Loginpassword123"}, follow_redirects=False)
if response.status_code == 409:
response = client.post("/login", data={"username": "admin", "password": "Loginpassword123"}, follow_redirects=False)
assert response.status_code == 302
assert response.headers["location"] == "/"
return client


def test_get_docs_unauthenticated_user(client):
response = client.get("/docs")
assert response.status_code == 401
assert response.json() == {"detail": "Usuario no autenticado", "error": 401}

def test_get_docs_unauthorized_user(unauthorized_client):
response = unauthorized_client.get("/docs")
assert response.status_code == 401
assert response.json() == {"detail": "Unauthorized: /docs", "error": 401}

def test_get_docs_authorized(authorized_client):
response = authorized_client.get("/docs")
assert response.status_code == 200
assert "swagger-ui" in response.text


# Test the /openapi.json endpoint

def test_get_openapi_unauthenticated_user(client):
client.get("/logout")
response = client.get("/openapi.json")
assert response.status_code == 401
assert response.json() == {"detail": "Usuario no autenticado", "error": 401}

def test_get_openapi_unauthorized_user(unauthorized_client):
response = unauthorized_client.get("/openapi.json")
assert response.status_code == 401
assert response.json() == {"detail": "Unauthorized: /openapi.json", "error": 401}

def test_get_openapi_authorized(authorized_client):
response = authorized_client.get("/openapi.json")
assert response.status_code == 200
assert "openapi" in response.json()
25 changes: 25 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import pytest

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from app.db.database import Base

@pytest.fixture(scope="session")
def engine():
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
return create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})

@pytest.fixture(scope="session")
def TestingSessionLocal(engine):
return sessionmaker(autocommit=False, autoflush=False, bind=engine)

@pytest.fixture(scope="session")
def db(engine, TestingSessionLocal):
Base.metadata.create_all(bind=engine)
db = TestingSessionLocal()
try:
yield db
finally:
db.close()
Base.metadata.drop_all(bind=engine)
134 changes: 134 additions & 0 deletions tests/routes/test_auth_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import pytest

from fastapi.testclient import TestClient

from app.db.database import get_db
from app.db.models import User
from app.main import app

@pytest.fixture(scope="module")
def client(db):
def override_get_db():
try:
yield db
finally:
db.rollback()

app.dependency_overrides[get_db] = override_get_db
with TestClient(app) as client:
yield client
app.dependency_overrides.clear()

@pytest.fixture
def authenticated_client(client):
# Registra e inicia sesión un usuario para la prueba
response = client.post("/login", data={"username": "loginuser", "password": "loginpassword"}, follow_redirects=False)
if response.status_code == 401:
response = client.post("/signup", data={"username": "loginuser", "password": "loginpassword"}, follow_redirects=False)
assert response.status_code == 302
assert response.headers["location"] == "/"
return client


# Test the signup endpoints

def test_get_signup(client):
response = client.get("/signup")
assert response.status_code == 200
assert response.template.name == "signup.html"

def test_post_signup(client, db):
username = "newuser1"
password = "Newpassword1*"
response = client.post("/signup", data={"username": username, "password": password}, follow_redirects=False)
assert response.status_code == 302
assert response.headers["location"] == "/"

db_user = db.query(User).filter(User.username == username).first()
assert db_user is not None
assert db_user.verify_password(password)

# Test that the user cannot be created again
response = client.post("/signup", data={"username": username, "password": password}, follow_redirects=False)
assert response.status_code == 409
assert response.context["error"] == "Usuario ya registrado"


# Test the login endpoints

def test_get_login(client):
response = client.get("/logout", follow_redirects=False)

response = client.get("/login")
assert response.status_code == 200
assert response.template.name == "login.html"

def test_post_login(client, db):
# Empty the database
db.query(User).delete()
db.commit()

# Create user
user = User(username="loginuser")
user.set_password("loginpassword")
db.add(user)
db.commit()

response = client.post("/login", data={"username": "loginuser", "password": "loginpassword"}, follow_redirects=False)
assert response.status_code == 302
assert response.headers["location"] == "/"

# Test that the user cannot login with wrong password
response = client.post("/login", data={"username": "loginuser", "password": "wrongpassword"}, follow_redirects=False)
assert response.status_code == 401
assert response.template.name == "login.html"
assert "Usuario o contraseña incorrectos" in response.text

# Test that the user cannot login with wrong username
response = client.post("/login", data={"username": "wronguser", "password": "loginpassword"}, follow_redirects=False)
assert response.status_code == 401
assert response.template.name == "login.html"
assert "Usuario o contraseña incorrectos" in response.text


# Test the logout endpoint

def test_logout(client):
response = client.get("/logout", follow_redirects=False)
assert response.status_code == 307
assert response.headers["location"] == "/login"


# Test the token endpoint

def test_create_user_and_generate_token(authenticated_client):
response = authenticated_client.post("/token", data={"username": "loginuser", "password": "loginpassword"}, follow_redirects=False)
assert response.status_code == 200
assert response.json()["access_token"]
assert response.json()["token_type"] == "bearer"

def test_token_with_wrong_password(client):
response = client.post("/token", data={"username": "tokenuser", "password": "wrongpassword"}, follow_redirects=False)
assert response.status_code == 401
assert response.json()["detail"] == "Incorrect username or password"

def test_token_with_wrong_username(client):
response = client.post("/token", data={"username": "wronguser", "password": "tokenpassword"}, follow_redirects=False)
assert response.status_code == 401
assert response.json()["detail"] == "Incorrect username or password"

def test_token_with_empty_credentials(client):
response = client.post("/token", follow_redirects=False)
assert response.status_code == 422
assert response.json()["detail"][0]["msg"] == "Field required"
assert response.json()["detail"][1]["msg"] == "Field required"

def test_token_with_empty_username(client):
response = client.post("/token", data={"password": "tokenpassword"}, follow_redirects=False)
assert response.status_code == 422
assert response.json()["detail"][0]["msg"] == "Field required"

def test_token_with_empty_password(client):
response = client.post("/token", data={"username": "tokenuser"}, follow_redirects=False)
assert response.status_code == 422
assert response.json()["detail"][0]["msg"] == "Field required"
Loading

0 comments on commit 9213d48

Please sign in to comment.