Skip to content

Commit

Permalink
Merge branch 'feature/test-sqlmodel' into next
Browse files Browse the repository at this point in the history
  • Loading branch information
aprilahijriyan committed Apr 27, 2024
2 parents ce84974 + 4acd563 commit bf1e37b
Show file tree
Hide file tree
Showing 13 changed files with 147 additions and 65 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@ jobs:
uses: supercharge/redis-github-action@1.8.0
- name: Run the automated tests
run: poetry run pytest -v -s
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4.0.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.vscode
.idea
*.sqlite

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
3 changes: 0 additions & 3 deletions .idea/.gitignore

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/inspectionProfiles/profiles_settings.xml

This file was deleted.

4 changes: 0 additions & 4 deletions .idea/misc.xml

This file was deleted.

8 changes: 0 additions & 8 deletions .idea/modules.xml

This file was deleted.

15 changes: 0 additions & 15 deletions .idea/popol.iml

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/vcs.xml

This file was deleted.

9 changes: 0 additions & 9 deletions .vscode/settings.json

This file was deleted.

Empty file added popol/py.typed
Empty file.
16 changes: 16 additions & 0 deletions tests/_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from popol.db.sqlmodel import models
from enum import Enum

class Counter(models.Model, table=True):
value: int

class Hero(models.Model, table=True):
class Roles(str, Enum):
ROAM = "ROAM"
EXPLANE = "EXPLANE"
MIDLANE = "MIDLANE"
GOLDLANE = "GOLDLANE"
JUNGLER = "JUNGLER"

name: str
role: Roles
17 changes: 3 additions & 14 deletions tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,13 @@ def SAQ_QUEUES(self):
"context": {},
}
}

DB_USERNAME: str = "popol_user"
DB_PASSWORD: str = "popol_pass"
DB_HOST: str = "localhost"
DB_PORT: int = 5432
DB_NAME: str = "popol_db"

DB_NAME: str = "popol_test_db.sqlite"
SQLALCHEMY_ASYNC_MODE: bool = False

@property
def SQLALCHEMY_DIALECT(self) -> str:
dialect = "psycopg2"
if self.SQLALCHEMY_ASYNC_MODE:
dialect = "asyncpg"
return dialect

@property
def SQLALCHEMY_DATABASE_URI(self):
return f"postgresql+{self.SQLALCHEMY_DIALECT}://{self.DB_USERNAME}:{self.DB_PASSWORD}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
return f"sqlite:///{self.DB_NAME}"

SQLALCHEMY_OPTIONS: dict = {}
EMAIL_BACKEND: str = "popol.email.backend.EmailBackend"
Expand Down
120 changes: 120 additions & 0 deletions tests/test_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
from fastapi import FastAPI, APIRouter
from fastapi.testclient import TestClient
from popol.db import sqlmodel
from popol.db.sqlmodel.globals import db

import pytest
from sqlmodel import select

from tests._models import Hero
from pydantic import BaseModel
from popol import dantic
from popol.utils import abort

from asgi_lifespan import LifespanManager


class HeroIn(BaseModel):
name: str
role: Hero.Roles

def as_model(self):
return Hero(**dantic.to_dict(self))

@pytest.fixture
def db_app(app: FastAPI):
sqlmodel.setup(app)

hero_router = APIRouter(prefix="/hero")

@hero_router.get("/")
async def list_hero():
with db.open() as session:
heroes = session.exec(select(Hero)).all()
return heroes

@hero_router.post("/")
async def create_hero(hero_data: HeroIn):
with db.open() as session:
hero = hero_data.as_model()
session.add(hero)
session.commit()
session.refresh(hero)
return hero

@hero_router.get("/{id}")
async def get_hero(id: int):
with db.open() as session:
stmt = select(Hero).where(Hero.id == id)
hero = session.exec(stmt).first()
if not hero:
abort(404, "Hero not found")
return hero

@hero_router.put("/{id}")
async def update_hero(id: int, hero_data: HeroIn):
with db.open() as session:
stmt = select(Hero).where(Hero.id == id).with_for_update()
hero = session.exec(stmt).first()
if not hero:
abort(404, "Hero not found")
hero.name = hero_data.name
hero.role = hero_data.role
session.add(hero)
session.commit()
session.refresh(hero)
return hero

@hero_router.delete("/{id}")
async def delete_hero(id: int):
with db.open() as session:
stmt = select(Hero).where(Hero.id == id)
hero = session.exec(stmt).first()
if not hero:
abort(404, "Hero not found")
session.delete(hero)
session.commit()
return {"detail": "Hero deleted"}

app.include_router(hero_router)
return app

@pytest.fixture
async def db_client(db_app: FastAPI):
async with LifespanManager(db_app):
with TestClient(db_app) as client:
yield client

@pytest.mark.anyio
async def test_db_via_api(db_client: TestClient):
# get all heroes
resp = db_client.get("/hero/")
all_heroes = resp.json()
assert resp.status_code == 200 and len(all_heroes) == 0
# create a hero
resp = db_client.post("/hero/", json={"name": "Nana", "role": Hero.Roles.MIDLANE})
created_hero = resp.json()
assert resp.status_code == 200 and created_hero["name"] == "Nana" and created_hero["role"] == Hero.Roles.MIDLANE
# get all heroes
resp = db_client.get("/hero/")
all_heroes = resp.json()
assert resp.status_code == 200 and len(all_heroes) == 1
# get a hero
resp = db_client.get(f"/hero/{created_hero['id']}")
hero = resp.json()
assert resp.status_code == 200 and hero["name"] == "Nana" and hero["role"] == Hero.Roles.MIDLANE
# update a hero
resp = db_client.put(f"/hero/{created_hero['id']}", json={"name": "Nana", "role": Hero.Roles.JUNGLER})
updated_hero = resp.json()
assert resp.status_code == 200 and updated_hero["name"] == "Nana" and updated_hero["role"] == Hero.Roles.JUNGLER
# get a hero
resp = db_client.get(f"/hero/{created_hero['id']}")
hero = resp.json()
assert resp.status_code == 200 and hero["name"] == "Nana" and hero["role"] == Hero.Roles.JUNGLER
# delete a hero
resp = db_client.delete(f"/hero/{created_hero['id']}")
assert resp.status_code == 200
# get all heroes
resp = db_client.get("/hero/")
all_heroes = resp.json()
assert resp.status_code == 200 and len(all_heroes) == 0

0 comments on commit bf1e37b

Please sign in to comment.