Spaces:
Sleeping
Sleeping
Praneeth Yerrapragada
commited on
Commit
•
b6fbea9
1
Parent(s):
9c3303f
test: setup pytest | pytest for users apis
Browse files- app/engine/postgresdb.py +14 -2
- app/model/user.py +60 -11
- config/index.py +0 -2
- main.py +15 -25
- poetry.lock +167 -1
- pyproject.toml +7 -1
- tests/conftest.py +32 -43
- tests/pytest.ini +3 -1
- tests/test_main.py +0 -9
- tests/test_users.py +40 -0
- tests/test_users_crud.py +0 -49
app/engine/postgresdb.py
CHANGED
@@ -5,6 +5,7 @@ from typing import Any, AsyncIterator
|
|
5 |
from sqlalchemy.ext.asyncio import (
|
6 |
AsyncConnection,
|
7 |
AsyncSession,
|
|
|
8 |
async_sessionmaker,
|
9 |
create_async_engine,
|
10 |
)
|
@@ -19,7 +20,11 @@ Base = declarative_base()
|
|
19 |
|
20 |
class PostgresDatabase:
|
21 |
|
22 |
-
def __init__(self
|
|
|
|
|
|
|
|
|
23 |
self._engine = create_async_engine(host, **engine_kwargs)
|
24 |
self._sessionmaker = async_sessionmaker(autocommit=False, bind=self._engine)
|
25 |
|
@@ -60,8 +65,15 @@ class PostgresDatabase:
|
|
60 |
def get_engine(self):
|
61 |
return self._engine
|
62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
|
64 |
-
postgresdb = PostgresDatabase(
|
65 |
|
66 |
|
67 |
async def get_db_session():
|
|
|
5 |
from sqlalchemy.ext.asyncio import (
|
6 |
AsyncConnection,
|
7 |
AsyncSession,
|
8 |
+
AsyncEngine,
|
9 |
async_sessionmaker,
|
10 |
create_async_engine,
|
11 |
)
|
|
|
20 |
|
21 |
class PostgresDatabase:
|
22 |
|
23 |
+
def __init__(self):
|
24 |
+
self._engine: AsyncEngine | None = None
|
25 |
+
self._sessionmaker: async_sessionmaker | None = None
|
26 |
+
|
27 |
+
def init(self, host: str, engine_kwargs: dict[str, Any] = {}):
|
28 |
self._engine = create_async_engine(host, **engine_kwargs)
|
29 |
self._sessionmaker = async_sessionmaker(autocommit=False, bind=self._engine)
|
30 |
|
|
|
65 |
def get_engine(self):
|
66 |
return self._engine
|
67 |
|
68 |
+
# Used for testing
|
69 |
+
async def create_all(self, connection: AsyncConnection):
|
70 |
+
await connection.run_sync(Base.metadata.create_all)
|
71 |
+
|
72 |
+
async def drop_all(self, connection: AsyncConnection):
|
73 |
+
await connection.run_sync(Base.metadata.drop_all)
|
74 |
+
|
75 |
|
76 |
+
postgresdb = PostgresDatabase()
|
77 |
|
78 |
|
79 |
async def get_db_session():
|
app/model/user.py
CHANGED
@@ -5,6 +5,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|
5 |
|
6 |
from app.model.base import BaseModel
|
7 |
from app.engine.postgresdb import Base
|
|
|
8 |
|
9 |
logger = logging.getLogger(__name__)
|
10 |
|
@@ -20,25 +21,62 @@ class User(Base, BaseModel):
|
|
20 |
transactions = relationship("Transaction", back_populates="user")
|
21 |
|
22 |
@classmethod
|
23 |
-
async def create(cls: "type[User]", db: AsyncSession, **kwargs) -> "User":
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
await db.commit()
|
|
|
30 |
return user
|
31 |
|
32 |
@classmethod
|
33 |
-
async def update(cls: "type[User]", db: AsyncSession, id: int, **kwargs) -> "User":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
query = sql.update(cls).where(cls.id == id).values(**kwargs).execution_options(synchronize_session="fetch")
|
35 |
-
|
36 |
-
user =
|
37 |
await db.commit()
|
38 |
return user
|
39 |
|
40 |
@classmethod
|
41 |
async def get(cls: "type[User]", db: AsyncSession, email: str) -> "User":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
logging.info(f"Getting user: {email}")
|
43 |
query = sql.select(cls).where(cls.email == email)
|
44 |
logging.info(f"Query: {query}")
|
@@ -48,13 +86,24 @@ class User(Base, BaseModel):
|
|
48 |
|
49 |
@classmethod
|
50 |
async def delete(cls: "type[User]", db: AsyncSession, email: str) -> "User":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
query = (
|
52 |
sql.update(cls)
|
53 |
.where(cls.email == email)
|
54 |
.values(is_deleted=True)
|
55 |
.execution_options(synchronize_session="fetch")
|
56 |
)
|
57 |
-
|
58 |
-
user =
|
59 |
await db.commit()
|
60 |
return user
|
|
|
5 |
|
6 |
from app.model.base import BaseModel
|
7 |
from app.engine.postgresdb import Base
|
8 |
+
from app.schema.index import UserCreate, UserUpdate
|
9 |
|
10 |
logger = logging.getLogger(__name__)
|
11 |
|
|
|
21 |
transactions = relationship("Transaction", back_populates="user")
|
22 |
|
23 |
@classmethod
|
24 |
+
async def create(cls: "type[User]", db: AsyncSession, **kwargs: UserCreate) -> "User":
|
25 |
+
"""
|
26 |
+
Creates a new user in the database.
|
27 |
+
|
28 |
+
Args:
|
29 |
+
cls (type[User]): The class object representing the User model.
|
30 |
+
db (AsyncSession): The asynchronous session object for interacting with the database.
|
31 |
+
**kwargs (UserCreate): The keyword arguments representing the user's attributes.
|
32 |
+
|
33 |
+
Returns:
|
34 |
+
User
|
35 |
+
"""
|
36 |
+
|
37 |
+
print(f"Creating user: {kwargs}")
|
38 |
+
# query = sql.insert(cls).values(**kwargs)
|
39 |
+
user = cls(name=kwargs["name"], email=kwargs["email"], hashed_password=kwargs["hashed_password"])
|
40 |
+
db.add(user)
|
41 |
await db.commit()
|
42 |
+
await db.refresh(user)
|
43 |
return user
|
44 |
|
45 |
@classmethod
|
46 |
+
async def update(cls: "type[User]", db: AsyncSession, id: int, **kwargs: UserUpdate) -> "User":
|
47 |
+
"""
|
48 |
+
Updates a user in the database with the given ID and keyword arguments.
|
49 |
+
|
50 |
+
Args:
|
51 |
+
cls (type[User]): The class object representing the User model.
|
52 |
+
db (AsyncSession): The asynchronous session object for interacting with the database.
|
53 |
+
id (int): The ID of the user to update.
|
54 |
+
**kwargs (UserUpdate): The keyword arguments representing the user's attributes to update.
|
55 |
+
|
56 |
+
Returns:
|
57 |
+
User
|
58 |
+
"""
|
59 |
+
|
60 |
query = sql.update(cls).where(cls.id == id).values(**kwargs).execution_options(synchronize_session="fetch")
|
61 |
+
results = await db.execute(query)
|
62 |
+
user = results.fetchone()
|
63 |
await db.commit()
|
64 |
return user
|
65 |
|
66 |
@classmethod
|
67 |
async def get(cls: "type[User]", db: AsyncSession, email: str) -> "User":
|
68 |
+
"""
|
69 |
+
Retrieves a user from the database based on their email.
|
70 |
+
|
71 |
+
Args:
|
72 |
+
cls (type[User]): The class object representing the User model.
|
73 |
+
db (AsyncSession): The asynchronous session object for interacting with the database.
|
74 |
+
email (str): The email of the user to retrieve.
|
75 |
+
|
76 |
+
Returns:
|
77 |
+
User
|
78 |
+
"""
|
79 |
+
|
80 |
logging.info(f"Getting user: {email}")
|
81 |
query = sql.select(cls).where(cls.email == email)
|
82 |
logging.info(f"Query: {query}")
|
|
|
86 |
|
87 |
@classmethod
|
88 |
async def delete(cls: "type[User]", db: AsyncSession, email: str) -> "User":
|
89 |
+
"""
|
90 |
+
Deletes a user from the database based on their email.
|
91 |
+
|
92 |
+
Args:
|
93 |
+
cls (type[User]): The class object representing the User model.
|
94 |
+
db (AsyncSession): The asynchronous session object for interacting with the database.
|
95 |
+
email (str): The email of the user to delete.
|
96 |
+
|
97 |
+
Returns:
|
98 |
+
User
|
99 |
+
"""
|
100 |
query = (
|
101 |
sql.update(cls)
|
102 |
.where(cls.email == email)
|
103 |
.values(is_deleted=True)
|
104 |
.execution_options(synchronize_session="fetch")
|
105 |
)
|
106 |
+
result = await db.execute(query)
|
107 |
+
user = result.fetchone()
|
108 |
await db.commit()
|
109 |
return user
|
config/index.py
CHANGED
@@ -26,11 +26,9 @@ class Config:
|
|
26 |
POSTGRES_USER = os.getenv("POSTGRES_USER")
|
27 |
POSTGRES_PASSWORD = os.getenv("POSTGRES_PASSWORD")
|
28 |
POSTGRES_DB_NAME = os.getenv("POSTGRES_DB_NAME")
|
29 |
-
POSTGRES_TEST_DB_NAME = os.getenv("POSTGRES_TEST_DB_NAME")
|
30 |
POSTGRES_DB_HOST = os.getenv("POSTGRES_DB_HOST")
|
31 |
POSTGRES_DB_PORT = os.getenv("POSTGRES_DB_PORT")
|
32 |
SQLALCHEMY_DATABASE_URL = f"postgresql+asyncpg://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_DB_HOST}:{POSTGRES_DB_PORT}/{POSTGRES_DB_NAME}"
|
33 |
-
SQLALCHEMY_TEST_DATABASE_URL = f"postgresql+asyncpg://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_DB_HOST}:{POSTGRES_DB_PORT}/{POSTGRES_TEST_DB_NAME}"
|
34 |
|
35 |
|
36 |
config = Config
|
|
|
26 |
POSTGRES_USER = os.getenv("POSTGRES_USER")
|
27 |
POSTGRES_PASSWORD = os.getenv("POSTGRES_PASSWORD")
|
28 |
POSTGRES_DB_NAME = os.getenv("POSTGRES_DB_NAME")
|
|
|
29 |
POSTGRES_DB_HOST = os.getenv("POSTGRES_DB_HOST")
|
30 |
POSTGRES_DB_PORT = os.getenv("POSTGRES_DB_PORT")
|
31 |
SQLALCHEMY_DATABASE_URL = f"postgresql+asyncpg://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_DB_HOST}:{POSTGRES_DB_PORT}/{POSTGRES_DB_NAME}"
|
|
|
32 |
|
33 |
|
34 |
config = Config
|
main.py
CHANGED
@@ -15,40 +15,30 @@ from alembic import command
|
|
15 |
|
16 |
|
17 |
from app.engine.postgresdb import postgresdb
|
18 |
-
from config.index import config
|
19 |
|
20 |
logger = logging.getLogger("uvicorn")
|
21 |
|
22 |
|
23 |
-
|
24 |
-
|
25 |
-
command.upgrade(cfg, "head")
|
26 |
-
|
27 |
-
|
28 |
-
async def run_async_migrations():
|
29 |
-
logger.info("Running migrations...")
|
30 |
-
async_engine = postgresdb.get_engine()
|
31 |
-
|
32 |
-
async with async_engine.begin() as conn:
|
33 |
-
await conn.run_sync(run_migrations, Config("alembic.ini"))
|
34 |
-
|
35 |
-
logger.info("Finished running migrations")
|
36 |
-
|
37 |
|
38 |
-
@asynccontextmanager
|
39 |
-
async def lifespan(app_: FastAPI):
|
40 |
-
yield
|
41 |
-
if postgresdb.get_engine() is not None:
|
42 |
-
await postgresdb.close()
|
43 |
|
|
|
|
|
44 |
|
45 |
-
|
46 |
-
|
47 |
|
|
|
|
|
|
|
|
|
|
|
48 |
|
49 |
-
def init_app():
|
50 |
app = FastAPI(lifespan=lifespan)
|
51 |
-
|
|
|
52 |
logger.warning("Running in development mode - allowing CORS for all origins")
|
53 |
app.add_middleware(
|
54 |
CORSMiddleware,
|
@@ -75,4 +65,4 @@ if __name__ == "__main__":
|
|
75 |
async def redirect_to_docs():
|
76 |
return RedirectResponse(url="/docs")
|
77 |
|
78 |
-
uvicorn.run(app="main:app", host=
|
|
|
15 |
|
16 |
|
17 |
from app.engine.postgresdb import postgresdb
|
18 |
+
from config.index import config as env
|
19 |
|
20 |
logger = logging.getLogger("uvicorn")
|
21 |
|
22 |
|
23 |
+
init_settings()
|
24 |
+
# init_observability()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
|
|
|
|
|
|
|
|
|
|
|
26 |
|
27 |
+
def init_app(init_db: bool = True) -> FastAPI:
|
28 |
+
lifespan = None
|
29 |
|
30 |
+
if init_db:
|
31 |
+
postgresdb.init(env.SQLALCHEMY_DATABASE_URL, {"echo": True, "future": True})
|
32 |
|
33 |
+
@asynccontextmanager
|
34 |
+
async def lifespan(app: FastAPI):
|
35 |
+
yield
|
36 |
+
if postgresdb.get_engine() is not None:
|
37 |
+
await postgresdb.close()
|
38 |
|
|
|
39 |
app = FastAPI(lifespan=lifespan)
|
40 |
+
|
41 |
+
if env.ENVIRONMENT == "dev":
|
42 |
logger.warning("Running in development mode - allowing CORS for all origins")
|
43 |
app.add_middleware(
|
44 |
CORSMiddleware,
|
|
|
65 |
async def redirect_to_docs():
|
66 |
return RedirectResponse(url="/docs")
|
67 |
|
68 |
+
uvicorn.run(app="main:app", host=env.APP_HOST, port=env.APP_PORT, reload=(env.ENVIRONMENT == "dev"))
|
poetry.lock
CHANGED
@@ -1697,6 +1697,20 @@ dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"]
|
|
1697 |
docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"]
|
1698 |
tests = ["pytest", "pytz", "simplejson"]
|
1699 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1700 |
[[package]]
|
1701 |
name = "monotonic"
|
1702 |
version = "1.6"
|
@@ -2699,6 +2713,17 @@ files = [
|
|
2699 |
dev = ["pre-commit", "tox"]
|
2700 |
testing = ["pytest", "pytest-benchmark"]
|
2701 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2702 |
[[package]]
|
2703 |
name = "posthog"
|
2704 |
version = "3.5.0"
|
@@ -2742,6 +2767,129 @@ files = [
|
|
2742 |
{file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"},
|
2743 |
]
|
2744 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2745 |
[[package]]
|
2746 |
name = "pydantic"
|
2747 |
version = "2.7.2"
|
@@ -2908,6 +3056,24 @@ pytest = ">=7.0.0,<9"
|
|
2908 |
docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
|
2909 |
testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
|
2910 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2911 |
[[package]]
|
2912 |
name = "pytest-xdist"
|
2913 |
version = "3.6.1"
|
@@ -4210,4 +4376,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more
|
|
4210 |
[metadata]
|
4211 |
lock-version = "2.0"
|
4212 |
python-versions = "^3.11,<3.12"
|
4213 |
-
content-hash = "
|
|
|
1697 |
docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"]
|
1698 |
tests = ["pytest", "pytz", "simplejson"]
|
1699 |
|
1700 |
+
[[package]]
|
1701 |
+
name = "mirakuru"
|
1702 |
+
version = "2.5.2"
|
1703 |
+
description = "Process executor (not only) for tests."
|
1704 |
+
optional = false
|
1705 |
+
python-versions = ">=3.8"
|
1706 |
+
files = [
|
1707 |
+
{file = "mirakuru-2.5.2-py3-none-any.whl", hash = "sha256:90c2d90a8cf14349b2f33e6db30a16acd855499811e0312e56cf80ceacf2d3e5"},
|
1708 |
+
{file = "mirakuru-2.5.2.tar.gz", hash = "sha256:41ca583d355eb7a6cfdc21c1aea549979d685c27b57239b88725434f115a7132"},
|
1709 |
+
]
|
1710 |
+
|
1711 |
+
[package.dependencies]
|
1712 |
+
psutil = {version = ">=4.0.0", markers = "sys_platform != \"cygwin\""}
|
1713 |
+
|
1714 |
[[package]]
|
1715 |
name = "monotonic"
|
1716 |
version = "1.6"
|
|
|
2713 |
dev = ["pre-commit", "tox"]
|
2714 |
testing = ["pytest", "pytest-benchmark"]
|
2715 |
|
2716 |
+
[[package]]
|
2717 |
+
name = "port-for"
|
2718 |
+
version = "0.7.2"
|
2719 |
+
description = "Utility that helps with local TCP ports management. It can find an unused TCP localhost port and remember the association."
|
2720 |
+
optional = false
|
2721 |
+
python-versions = ">=3.8"
|
2722 |
+
files = [
|
2723 |
+
{file = "port-for-0.7.2.tar.gz", hash = "sha256:074f29335130578aa42fef3726985e57d01c15189e509633a8a1b0b7f9226349"},
|
2724 |
+
{file = "port_for-0.7.2-py3-none-any.whl", hash = "sha256:16b279ab4f210bad33515c45bd9af0c6e048ab24c3b6bbd9cfc7e451782617df"},
|
2725 |
+
]
|
2726 |
+
|
2727 |
[[package]]
|
2728 |
name = "posthog"
|
2729 |
version = "3.5.0"
|
|
|
2767 |
{file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"},
|
2768 |
]
|
2769 |
|
2770 |
+
[[package]]
|
2771 |
+
name = "psutil"
|
2772 |
+
version = "5.9.8"
|
2773 |
+
description = "Cross-platform lib for process and system monitoring in Python."
|
2774 |
+
optional = false
|
2775 |
+
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
2776 |
+
files = [
|
2777 |
+
{file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"},
|
2778 |
+
{file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"},
|
2779 |
+
{file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"},
|
2780 |
+
{file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"},
|
2781 |
+
{file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"},
|
2782 |
+
{file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"},
|
2783 |
+
{file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"},
|
2784 |
+
{file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"},
|
2785 |
+
{file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"},
|
2786 |
+
{file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"},
|
2787 |
+
{file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"},
|
2788 |
+
{file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"},
|
2789 |
+
{file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"},
|
2790 |
+
{file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"},
|
2791 |
+
{file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"},
|
2792 |
+
{file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"},
|
2793 |
+
]
|
2794 |
+
|
2795 |
+
[package.extras]
|
2796 |
+
test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
|
2797 |
+
|
2798 |
+
[[package]]
|
2799 |
+
name = "psycopg"
|
2800 |
+
version = "3.1.19"
|
2801 |
+
description = "PostgreSQL database adapter for Python"
|
2802 |
+
optional = false
|
2803 |
+
python-versions = ">=3.7"
|
2804 |
+
files = [
|
2805 |
+
{file = "psycopg-3.1.19-py3-none-any.whl", hash = "sha256:dca5e5521c859f6606686432ae1c94e8766d29cc91f2ee595378c510cc5b0731"},
|
2806 |
+
{file = "psycopg-3.1.19.tar.gz", hash = "sha256:92d7b78ad82426cdcf1a0440678209faa890c6e1721361c2f8901f0dccd62961"},
|
2807 |
+
]
|
2808 |
+
|
2809 |
+
[package.dependencies]
|
2810 |
+
typing-extensions = ">=4.1"
|
2811 |
+
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
|
2812 |
+
|
2813 |
+
[package.extras]
|
2814 |
+
binary = ["psycopg-binary (==3.1.19)"]
|
2815 |
+
c = ["psycopg-c (==3.1.19)"]
|
2816 |
+
dev = ["black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.4.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"]
|
2817 |
+
docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"]
|
2818 |
+
pool = ["psycopg-pool"]
|
2819 |
+
test = ["anyio (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"]
|
2820 |
+
|
2821 |
+
[[package]]
|
2822 |
+
name = "psycopg-binary"
|
2823 |
+
version = "3.1.19"
|
2824 |
+
description = "PostgreSQL database adapter for Python -- C optimisation distribution"
|
2825 |
+
optional = false
|
2826 |
+
python-versions = ">=3.7"
|
2827 |
+
files = [
|
2828 |
+
{file = "psycopg_binary-3.1.19-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7204818f05151dd08f8f851defb01972ec9d2cc925608eb0de232563f203f354"},
|
2829 |
+
{file = "psycopg_binary-3.1.19-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d4e67fd86758dbeac85641419a54f84d74495a8683b58ad5dfad08b7fc37a8f"},
|
2830 |
+
{file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12173e34b176e93ad2da913de30f774d5119c2d4d4640c6858d2d77dfa6c9bf"},
|
2831 |
+
{file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:052f5193304066318853b4b2e248f523c8f52b371fc4e95d4ef63baee3f30955"},
|
2832 |
+
{file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29008f3f8977f600b8a7fb07c2e041b01645b08121760609cc45e861a0364dc9"},
|
2833 |
+
{file = "psycopg_binary-3.1.19-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c6a9a651a08d876303ed059c9553df18b3c13c3406584a70a8f37f1a1fe2709"},
|
2834 |
+
{file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:91a645e6468c4f064b7f4f3b81074bdd68fe5aa2b8c5107de15dcd85ba6141be"},
|
2835 |
+
{file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5c6956808fd5cf0576de5a602243af8e04594b25b9a28675feddc71c5526410a"},
|
2836 |
+
{file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:1622ca27d5a7a98f7d8f35e8b146dc7efda4a4b6241d2edf7e076bd6bcecbeb4"},
|
2837 |
+
{file = "psycopg_binary-3.1.19-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a100482950a55228f648bd382bb71bfaff520002f29845274fccbbf02e28bd52"},
|
2838 |
+
{file = "psycopg_binary-3.1.19-cp310-cp310-win_amd64.whl", hash = "sha256:955ca8905c0251fc4af7ce0a20999e824a25652f53a558ab548b60969f1f368e"},
|
2839 |
+
{file = "psycopg_binary-3.1.19-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cf49e91dcf699b8a449944ed898ef1466b39b92720613838791a551bc8f587a"},
|
2840 |
+
{file = "psycopg_binary-3.1.19-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:964c307e400c5f33fa762ba1e19853e048814fcfbd9679cc923431adb7a2ead2"},
|
2841 |
+
{file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3433924e1b14074798331dc2bfae2af452ed7888067f2fc145835704d8981b15"},
|
2842 |
+
{file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00879d4c6be4b3afc510073f48a5e960f797200e261ab3d9bd9b7746a08c669d"},
|
2843 |
+
{file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34a6997c80f86d3dd80a4f078bb3b200079c47eeda4fd409d8899b883c90d2ac"},
|
2844 |
+
{file = "psycopg_binary-3.1.19-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0106e42b481677c41caa69474fe530f786dcef88b11b70000f0e45a03534bc8f"},
|
2845 |
+
{file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81efe09ba27533e35709905c3061db4dc9fb814f637360578d065e2061fbb116"},
|
2846 |
+
{file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d312d6dddc18d9c164e1893706269c293cba1923118349d375962b1188dafb01"},
|
2847 |
+
{file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:bfd2c734da9950f7afaad5f132088e0e1478f32f042881fca6651bb0c8d14206"},
|
2848 |
+
{file = "psycopg_binary-3.1.19-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8a732610a5a6b4f06dadcf9288688a8ff202fd556d971436a123b7adb85596e2"},
|
2849 |
+
{file = "psycopg_binary-3.1.19-cp311-cp311-win_amd64.whl", hash = "sha256:321814a9a3ad785855a821b842aba08ca1b7de7dfb2979a2f0492dca9ec4ae70"},
|
2850 |
+
{file = "psycopg_binary-3.1.19-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4aa0ca13bb8a725bb6d12c13999217fd5bc8b86a12589f28a74b93e076fbb959"},
|
2851 |
+
{file = "psycopg_binary-3.1.19-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:469424e354ebcec949aa6aa30e5a9edc352a899d9a68ad7a48f97df83cc914cf"},
|
2852 |
+
{file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b04f5349313529ae1f1c42fe1aa0443faaf50fdf12d13866c2cc49683bfa53d0"},
|
2853 |
+
{file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959feabddc7fffac89b054d6f23f3b3c62d7d3c90cd414a02e3747495597f150"},
|
2854 |
+
{file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e9da624a6ca4bc5f7fa1f03f8485446b5b81d5787b6beea2b4f8d9dbef878ad7"},
|
2855 |
+
{file = "psycopg_binary-3.1.19-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1823221a6b96e38b15686170d4fc5b36073efcb87cce7d3da660440b50077f6"},
|
2856 |
+
{file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:866db42f986298f0cf15d805225eb8df2228bf19f7997d7f1cb5f388cbfc6a0f"},
|
2857 |
+
{file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:738c34657305b5973af6dbb6711b07b179dfdd21196d60039ca30a74bafe9648"},
|
2858 |
+
{file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb9758473200384a04374d0e0cac6f451218ff6945a024f65a1526802c34e56e"},
|
2859 |
+
{file = "psycopg_binary-3.1.19-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0e991632777e217953ac960726158987da684086dd813ac85038c595e7382c91"},
|
2860 |
+
{file = "psycopg_binary-3.1.19-cp312-cp312-win_amd64.whl", hash = "sha256:1d87484dd42c8783c44a30400949efb3d81ef2487eaa7d64d1c54df90cf8b97a"},
|
2861 |
+
{file = "psycopg_binary-3.1.19-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d1d1723d7449c12bb61aca7eb6e0c6ab2863cd8dc0019273cc4d4a1982f84bdb"},
|
2862 |
+
{file = "psycopg_binary-3.1.19-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e538a8671005641fa195eab962f85cf0504defbd3b548c4c8fc27102a59f687b"},
|
2863 |
+
{file = "psycopg_binary-3.1.19-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c50592bc8517092f40979e4a5d934f96a1737a77724bb1d121eb78b614b30fc8"},
|
2864 |
+
{file = "psycopg_binary-3.1.19-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:95f16ae82bc242b76cd3c3e5156441e2bd85ff9ec3a9869d750aad443e46073c"},
|
2865 |
+
{file = "psycopg_binary-3.1.19-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebd1e98e865e9a28ce0cb2c25b7dfd752f0d1f0a423165b55cd32a431dcc0f4"},
|
2866 |
+
{file = "psycopg_binary-3.1.19-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:49cd7af7d49e438a39593d1dd8cab106a1912536c2b78a4d814ebdff2786094e"},
|
2867 |
+
{file = "psycopg_binary-3.1.19-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:affebd61aa3b7a8880fd4ac3ee94722940125ff83ff485e1a7c76be9adaabb38"},
|
2868 |
+
{file = "psycopg_binary-3.1.19-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d1bac282f140fa092f2bbb6c36ed82270b4a21a6fc55d4b16748ed9f55e50fdb"},
|
2869 |
+
{file = "psycopg_binary-3.1.19-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1285aa54449e362b1d30d92b2dc042ad3ee80f479cc4e323448d0a0a8a1641fa"},
|
2870 |
+
{file = "psycopg_binary-3.1.19-cp37-cp37m-win_amd64.whl", hash = "sha256:6cff31af8155dc9ee364098a328bab688c887c732c66b8d027e5b03818ca0287"},
|
2871 |
+
{file = "psycopg_binary-3.1.19-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d9b689c4a17dd3130791dcbb8c30dbf05602f7c2d56c792e193fb49adc7bf5f8"},
|
2872 |
+
{file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:017518bd2de4851adc826a224fb105411e148ad845e11355edd6786ba3dfedf5"},
|
2873 |
+
{file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c35fd811f339a3cbe7f9b54b2d9a5e592e57426c6cc1051632a62c59c4810208"},
|
2874 |
+
{file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38ed45ec9673709bfa5bc17f140e71dd4cca56d4e58ef7fd50d5a5043a4f55c6"},
|
2875 |
+
{file = "psycopg_binary-3.1.19-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:433f1c256108f9e26f480a8cd6ddb0fb37dbc87d7f5a97e4540a9da9b881f23f"},
|
2876 |
+
{file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ed61e43bf5dc8d0936daf03a19fef3168d64191dbe66483f7ad08c4cea0bc36b"},
|
2877 |
+
{file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ae8109ff9fdf1fa0cb87ab6645298693fdd2666a7f5f85660df88f6965e0bb7"},
|
2878 |
+
{file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a53809ee02e3952fae7977c19b30fd828bd117b8f5edf17a3a94212feb57faaf"},
|
2879 |
+
{file = "psycopg_binary-3.1.19-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9d39d5ffc151fb33bcd55b99b0e8957299c0b1b3e5a1a5f4399c1287ef0051a9"},
|
2880 |
+
{file = "psycopg_binary-3.1.19-cp38-cp38-win_amd64.whl", hash = "sha256:e14bc8250000921fcccd53722f86b3b3d1b57db901e206e49e2ab2afc5919c2d"},
|
2881 |
+
{file = "psycopg_binary-3.1.19-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cd88c5cea4efe614d5004fb5f5dcdea3d7d59422be796689e779e03363102d24"},
|
2882 |
+
{file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:621a814e60825162d38760c66351b4df679fd422c848b7c2f86ad399bff27145"},
|
2883 |
+
{file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46e50c05952b59a214e27d3606f6d510aaa429daed898e16b8a37bfbacc81acc"},
|
2884 |
+
{file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03354a9db667c27946e70162cb0042c3929154167f3678a30d23cebfe0ad55b5"},
|
2885 |
+
{file = "psycopg_binary-3.1.19-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c2f3b79037581afec7baa2bdbcb0a1787f1758744a7662099b0eca2d721cb"},
|
2886 |
+
{file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6469ebd9e93327e9f5f36dcf8692fb1e7aeaf70087c1c15d4f2c020e0be3a891"},
|
2887 |
+
{file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:85bca9765c04b6be90cb46e7566ffe0faa2d7480ff5c8d5e055ac427f039fd24"},
|
2888 |
+
{file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:a836610d5c75e9cff98b9fdb3559c007c785c09eaa84a60d5d10ef6f85f671e8"},
|
2889 |
+
{file = "psycopg_binary-3.1.19-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ef8de7a1d9fb3518cc6b58e3c80b75a824209ad52b90c542686c912db8553dad"},
|
2890 |
+
{file = "psycopg_binary-3.1.19-cp39-cp39-win_amd64.whl", hash = "sha256:76fcd33342f38e35cd6b5408f1bc117d55ab8b16e5019d99b6d3ce0356c51717"},
|
2891 |
+
]
|
2892 |
+
|
2893 |
[[package]]
|
2894 |
name = "pydantic"
|
2895 |
version = "2.7.2"
|
|
|
3056 |
docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
|
3057 |
testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
|
3058 |
|
3059 |
+
[[package]]
|
3060 |
+
name = "pytest-postgresql"
|
3061 |
+
version = "6.0.0"
|
3062 |
+
description = "Postgresql fixtures and fixture factories for Pytest."
|
3063 |
+
optional = false
|
3064 |
+
python-versions = ">=3.8"
|
3065 |
+
files = [
|
3066 |
+
{file = "pytest-postgresql-6.0.0.tar.gz", hash = "sha256:6a4d8e600a2eef273f3c0e846cd0b2ea577282e252de29b4ca854bfb929bb682"},
|
3067 |
+
{file = "pytest_postgresql-6.0.0-py3-none-any.whl", hash = "sha256:f14272bffad16a74d9a63f4cc828f243a12ae92995e236b68fd53154760e6a5a"},
|
3068 |
+
]
|
3069 |
+
|
3070 |
+
[package.dependencies]
|
3071 |
+
mirakuru = "*"
|
3072 |
+
port-for = ">=0.6.0"
|
3073 |
+
psycopg = ">=3.0.0"
|
3074 |
+
pytest = ">=6.2"
|
3075 |
+
setuptools = "*"
|
3076 |
+
|
3077 |
[[package]]
|
3078 |
name = "pytest-xdist"
|
3079 |
version = "3.6.1"
|
|
|
4376 |
[metadata]
|
4377 |
lock-version = "2.0"
|
4378 |
python-versions = "^3.11,<3.12"
|
4379 |
+
content-hash = "83b595a344a5db9263207b122c781f07b9cd8ac777e04fb1b743e3e3ce5016e1"
|
pyproject.toml
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
name = "app"
|
4 |
version = "0.1.0"
|
5 |
description = ""
|
6 |
-
authors = [ "
|
7 |
readme = "README.md"
|
8 |
package-mode = false
|
9 |
|
@@ -34,11 +34,17 @@ httpx = "^0.27.0"
|
|
34 |
alembic = "^1.13.1"
|
35 |
asyncpg = "^0.29.0"
|
36 |
asyncio = "^3.4.3"
|
|
|
|
|
37 |
|
38 |
[tool.poetry.dependencies.uvicorn]
|
39 |
extras = [ "standard" ]
|
40 |
version = "^0.23.2"
|
41 |
|
|
|
|
|
|
|
|
|
42 |
[tool.poetry.dependencies.llama-index-agent-openai]
|
43 |
version = "0.2.2"
|
44 |
|
|
|
3 |
name = "app"
|
4 |
version = "0.1.0"
|
5 |
description = ""
|
6 |
+
authors = [ "Praneeth Yerrapragada, Patrick Alexis" ]
|
7 |
readme = "README.md"
|
8 |
package-mode = false
|
9 |
|
|
|
34 |
alembic = "^1.13.1"
|
35 |
asyncpg = "^0.29.0"
|
36 |
asyncio = "^3.4.3"
|
37 |
+
pytest-postgresql = "^6.0.0"
|
38 |
+
psycopg-binary = "^3.1.19"
|
39 |
|
40 |
[tool.poetry.dependencies.uvicorn]
|
41 |
extras = [ "standard" ]
|
42 |
version = "^0.23.2"
|
43 |
|
44 |
+
[tool.poetry.dependencies.sqlalchemy]
|
45 |
+
extras = [ "asyncio" ]
|
46 |
+
version = "^2.0.30"
|
47 |
+
|
48 |
[tool.poetry.dependencies.llama-index-agent-openai]
|
49 |
version = "0.2.2"
|
50 |
|
tests/conftest.py
CHANGED
@@ -10,15 +10,25 @@ from alembic.operations import Operations
|
|
10 |
from alembic.script import ScriptDirectory
|
11 |
from config.index import config as settings
|
12 |
from app.engine.postgresdb import Base, get_db_session, postgresdb as sessionmanager
|
13 |
-
from main import
|
14 |
from asyncpg import Connection
|
15 |
from fastapi.testclient import TestClient
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
|
18 |
@pytest.fixture(autouse=True)
|
19 |
def app():
|
20 |
with ExitStack():
|
21 |
-
|
|
|
|
|
22 |
|
23 |
|
24 |
@pytest.fixture
|
@@ -27,60 +37,39 @@ def client(app):
|
|
27 |
yield c
|
28 |
|
29 |
|
30 |
-
@pytest.
|
31 |
def event_loop(request):
|
32 |
loop = asyncio.get_event_loop_policy().new_event_loop()
|
33 |
yield loop
|
34 |
loop.close()
|
35 |
|
36 |
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
return script._upgrade_revs("head", rev)
|
45 |
-
|
46 |
-
context = MigrationContext.configure(connection, opts={"target_metadata": Base.metadata, "fn": upgrade})
|
47 |
-
|
48 |
-
with context.begin_transaction():
|
49 |
-
with Operations.context(context):
|
50 |
-
context.run_migrations()
|
51 |
-
|
52 |
-
|
53 |
-
@pytest.fixture(scope="session", autouse=True)
|
54 |
-
async def setup_database():
|
55 |
-
# Run alembic migrations on test DB
|
56 |
-
async with sessionmanager.connect() as connection:
|
57 |
-
await connection.run_sync(run_migrations)
|
58 |
-
|
59 |
-
yield
|
60 |
|
61 |
-
|
62 |
-
|
|
|
|
|
|
|
63 |
|
64 |
|
65 |
-
# Each test function is a clean slate
|
66 |
@pytest.fixture(scope="function", autouse=True)
|
67 |
-
async def
|
68 |
-
async with sessionmanager.
|
69 |
-
|
70 |
-
|
71 |
-
yield session
|
72 |
-
finally:
|
73 |
-
await session.rollback() # Rolls back the outer transaction
|
74 |
-
|
75 |
-
|
76 |
-
@pytest.fixture(scope="function")
|
77 |
-
async def db_session(transactional_session):
|
78 |
-
yield transactional_session
|
79 |
|
80 |
|
81 |
@pytest.fixture(scope="function", autouse=True)
|
82 |
-
async def session_override(app,
|
83 |
async def get_db_session_override():
|
84 |
-
|
|
|
85 |
|
86 |
app.dependency_overrides[get_db_session] = get_db_session_override
|
|
|
10 |
from alembic.script import ScriptDirectory
|
11 |
from config.index import config as settings
|
12 |
from app.engine.postgresdb import Base, get_db_session, postgresdb as sessionmanager
|
13 |
+
from main import init_app
|
14 |
from asyncpg import Connection
|
15 |
from fastapi.testclient import TestClient
|
16 |
+
from pytest_postgresql import factories
|
17 |
+
from pytest_postgresql.factories.noprocess import postgresql_noproc
|
18 |
+
from pytest_postgresql.janitor import DatabaseJanitor
|
19 |
+
from sqlalchemy.testing.entities import ComparableEntity
|
20 |
+
|
21 |
+
from config.index import config as env
|
22 |
+
|
23 |
+
test_db = factories.postgresql_proc(dbname="test_db", port=5433)
|
24 |
|
25 |
|
26 |
@pytest.fixture(autouse=True)
|
27 |
def app():
|
28 |
with ExitStack():
|
29 |
+
# Don't initialize database connection.
|
30 |
+
# This is because we want to initialize the database connection manually, so that we can create the test database.
|
31 |
+
yield init_app(init_db=False)
|
32 |
|
33 |
|
34 |
@pytest.fixture
|
|
|
37 |
yield c
|
38 |
|
39 |
|
40 |
+
@pytest.mark.asyncio(scope="session")
|
41 |
def event_loop(request):
|
42 |
loop = asyncio.get_event_loop_policy().new_event_loop()
|
43 |
yield loop
|
44 |
loop.close()
|
45 |
|
46 |
|
47 |
+
@pytest.fixture(scope="function", autouse=True)
|
48 |
+
async def connection_test(test_db, event_loop):
|
49 |
+
pg_host = test_db.host
|
50 |
+
pg_port = test_db.port
|
51 |
+
pg_user = test_db.user
|
52 |
+
pg_db = test_db.dbname
|
53 |
+
pg_password = test_db.password
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
|
55 |
+
with DatabaseJanitor(pg_user, pg_host, pg_port, pg_db, test_db.version, pg_password):
|
56 |
+
connection_str = f"postgresql+asyncpg://{pg_user}:@{pg_host}:{pg_port}/{pg_db}"
|
57 |
+
sessionmanager.init(connection_str)
|
58 |
+
yield
|
59 |
+
await sessionmanager.close()
|
60 |
|
61 |
|
|
|
62 |
@pytest.fixture(scope="function", autouse=True)
|
63 |
+
async def create_tables(connection_test):
|
64 |
+
async with sessionmanager.connect() as connection:
|
65 |
+
await sessionmanager.drop_all(connection)
|
66 |
+
await sessionmanager.create_all(connection)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
|
68 |
|
69 |
@pytest.fixture(scope="function", autouse=True)
|
70 |
+
async def session_override(app, connection_test):
|
71 |
async def get_db_session_override():
|
72 |
+
async with sessionmanager.session() as session:
|
73 |
+
yield session
|
74 |
|
75 |
app.dependency_overrides[get_db_session] = get_db_session_override
|
tests/pytest.ini
CHANGED
@@ -1,2 +1,4 @@
|
|
1 |
[pytest]
|
2 |
-
asyncio_mode = auto
|
|
|
|
|
|
1 |
[pytest]
|
2 |
+
asyncio_mode = auto
|
3 |
+
timeout = 60
|
4 |
+
addopts = -vv --disable-warnings --durations=10 --durations-min=1.0
|
tests/test_main.py
DELETED
@@ -1,9 +0,0 @@
|
|
1 |
-
from main import app
|
2 |
-
from fastapi.testclient import TestClient
|
3 |
-
|
4 |
-
client = TestClient(app)
|
5 |
-
|
6 |
-
|
7 |
-
def test_read_main():
|
8 |
-
response = client.get("/")
|
9 |
-
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_users.py
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from random import randint
|
2 |
+
|
3 |
+
from fastapi.testclient import TestClient
|
4 |
+
|
5 |
+
|
6 |
+
def test_users(client: TestClient) -> None:
|
7 |
+
rand_int = randint(0, 1000)
|
8 |
+
email = f"test{rand_int}@example.com"
|
9 |
+
response = client.get(f"/api/v1/users/{email}")
|
10 |
+
assert response.status_code == 404
|
11 |
+
|
12 |
+
response = client.post(
|
13 |
+
"/api/v1/users",
|
14 |
+
json={"email": email, "name": "Full Name Test", "hashed_password": "test"},
|
15 |
+
)
|
16 |
+
assert response.status_code == 200
|
17 |
+
assert response.json().get("email") == email
|
18 |
+
assert response.json().get("name") == "Full Name Test"
|
19 |
+
assert response.json().get("is_deleted") == False
|
20 |
+
|
21 |
+
response = client.get(f"/api/v1/users/{email}")
|
22 |
+
assert response.status_code == 200
|
23 |
+
assert response.json().get("email") == email
|
24 |
+
assert response.json().get("name") == "Full Name Test"
|
25 |
+
assert response.json().get("is_deleted") == False
|
26 |
+
|
27 |
+
response = client.put(
|
28 |
+
f"/api/v1/users/{email}",
|
29 |
+
json={"email": email, "name": "Full Name Test 2", "hashed_password": "test"},
|
30 |
+
)
|
31 |
+
assert response.status_code == 200
|
32 |
+
assert response.json().get("email") == email
|
33 |
+
assert response.json().get("name") == "Full Name Test 2"
|
34 |
+
assert response.json().get("is_deleted") == False
|
35 |
+
|
36 |
+
response = client.delete(f"/api/v1/users/{email}")
|
37 |
+
assert response.status_code == 200
|
38 |
+
assert response.json().get("email") == email
|
39 |
+
assert response.json().get("name") == "Full Name Test 2"
|
40 |
+
assert response.json().get("is_deleted") == True
|
tests/test_users_crud.py
DELETED
@@ -1,49 +0,0 @@
|
|
1 |
-
import json
|
2 |
-
from httpx import AsyncClient
|
3 |
-
import pytest
|
4 |
-
|
5 |
-
from app.core.config import settings
|
6 |
-
|
7 |
-
|
8 |
-
@pytest.mark.asyncio
|
9 |
-
async def test_users_crud(httpx_async_client: AsyncClient):
|
10 |
-
# Test CREATE
|
11 |
-
user_data = {
|
12 |
-
"name": "John Doe",
|
13 |
-
"email": "john.doe@example.com",
|
14 |
-
"password": "password123",
|
15 |
-
}
|
16 |
-
response = await httpx_async_client.post(
|
17 |
-
f"{settings.API_V1_STR}/users/",
|
18 |
-
data=json.dumps(user_data),
|
19 |
-
headers={"Content-Type": "application/json"},
|
20 |
-
)
|
21 |
-
assert response.status_code == 200
|
22 |
-
user_id = response.json()["user_id"]
|
23 |
-
|
24 |
-
# Test READ
|
25 |
-
response = await httpx_async_client.get(f"{settings.API_V1_STR}/users/{user_id}")
|
26 |
-
assert response.status_code == 200
|
27 |
-
user = response.json()
|
28 |
-
assert user["name"] == user_data["name"]
|
29 |
-
assert user["email"] == user_data["email"]
|
30 |
-
assert "password" not in user
|
31 |
-
|
32 |
-
# Test UPDATE
|
33 |
-
updated_user_data = {
|
34 |
-
"name": "Jane Doe",
|
35 |
-
}
|
36 |
-
response = await httpx_async_client.put(
|
37 |
-
f"{settings.API_V1_STR}/users/{user_id}",
|
38 |
-
data=json.dumps(updated_user_data),
|
39 |
-
headers={"Content-Type": "application/json"},
|
40 |
-
)
|
41 |
-
assert response.status_code == 200
|
42 |
-
updated_user = response.json()
|
43 |
-
assert updated_user["name"] == updated_user_data["name"]
|
44 |
-
assert updated_user["email"] == user_data["email"]
|
45 |
-
assert "password" not in updated_user
|
46 |
-
|
47 |
-
# Test DELETE
|
48 |
-
response = await httpx_async_client.delete(f"{settings.API_V1_STR}/users/{user_id}")
|
49 |
-
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|