diff --git a/fastapi_ecom/app.py b/fastapi_ecom/app.py
index 9bf3591..6b35a07 100644
--- a/fastapi_ecom/app.py
+++ b/fastapi_ecom/app.py
@@ -11,25 +11,26 @@
{"name": "business", "description": "Operations on businesses"},
{"name": "product", "description": "Operations on products"},
{"name": "customer", "description": "Operations on customers"},
- {"name": "order", "description": "Operations on orders"}
+ {"name": "order", "description": "Operations on orders"},
]
# Initialize the FastAPI application
app = FastAPI(
- title="FastAPI ECOM",
- description="E-Commerce API for businesses and end users using FastAPI.",
- version="0.1.0",
- openapi_tags=tags_metadata,
- swagger_ui_init_oauth={
- "clientId": config.GOOGLE_CLIENT_ID,
- "clientSecret": config.GOOGLE_CLIENT_SECRET,
- }
- )
+ title="FastAPI ECOM",
+ description="E-Commerce API for businesses and end users using FastAPI.",
+ version="0.1.0",
+ openapi_tags=tags_metadata,
+ swagger_ui_init_oauth={
+ "clientId": config.GOOGLE_CLIENT_ID,
+ "clientSecret": config.GOOGLE_CLIENT_SECRET,
+ },
+)
app.add_middleware(SessionMiddleware, secret_key=config.GOOGLE_CLIENT_SECRET)
PREFIX = "/api/v1"
+
@app.get("/")
def root() -> dict[str, str]:
"""
@@ -38,11 +39,8 @@ def root() -> dict[str, str]:
:return: Metadata about the API, including title, description, and version.
"""
general("Root endpoint accessed")
- return{
- "title": "FastAPI ECOM",
- "description": "E-Commerce API for businesses and end users using FastAPI.",
- "version": "0.1.0"
- }
+ return {"title": "FastAPI ECOM", "description": "E-Commerce API for businesses and end users using FastAPI.", "version": "0.1.0"}
+
# Include routers for different modules
app.include_router(business.router, prefix=PREFIX)
diff --git a/fastapi_ecom/database/__init__.py b/fastapi_ecom/database/__init__.py
index 81693e3..ef45dd3 100644
--- a/fastapi_ecom/database/__init__.py
+++ b/fastapi_ecom/database/__init__.py
@@ -1,5 +1,4 @@
from pathlib import Path
-from typing import Union
from sqlalchemy import URL, Engine, create_engine
from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker, create_async_engine
@@ -11,10 +10,10 @@
baseobjc = declarative_base()
# Path of alembic configuration file
-alempath = str(Path(str(Path(str(Path(__file__).parent.resolve().parent.resolve()),"migrations").resolve()),"alembic.ini").resolve())
+alempath = str(Path(str(Path(str(Path(__file__).parent.resolve().parent.resolve()), "migrations").resolve()), "alembic.ini").resolve())
# Migration path for alembic configuration
-migrpath = str(Path(str(Path(__file__).parent.resolve().parent.resolve()),"migrations").resolve())
+migrpath = str(Path(str(Path(__file__).parent.resolve().parent.resolve()), "migrations").resolve())
def get_database_url(engine: str = "async") -> URL:
@@ -27,26 +26,27 @@ def get_database_url(engine: str = "async") -> URL:
"""
if engine == "sync":
SQLALCHEMY_DATABASE_URL = URL.create(
- drivername = "postgresql+psycopg2",
- username = config.username,
- password = config.password,
- host = config.dtbsbhost,
- port = config.dtbsbport,
- database = config.database,
+ drivername="postgresql+psycopg2",
+ username=config.username,
+ password=config.password,
+ host=config.dtbsbhost,
+ port=config.dtbsbport,
+ database=config.database,
)
return SQLALCHEMY_DATABASE_URL
SQLALCHEMY_DATABASE_URL = URL.create(
- drivername = config.dtbsdriver,
- username = config.username,
- password = config.password,
- host = config.dtbsbhost,
- port = config.dtbsbport,
- database = config.database,
+ drivername=config.dtbsdriver,
+ username=config.username,
+ password=config.password,
+ host=config.dtbsbhost,
+ port=config.dtbsbport,
+ database=config.database,
)
return SQLALCHEMY_DATABASE_URL
-def get_engine(engine: str = "async") -> Union[Engine, AsyncEngine]:
+
+def get_engine(engine: str = "async") -> Engine | AsyncEngine:
"""
Create a session engine based on the specified engine type.
@@ -55,7 +55,7 @@ def get_engine(engine: str = "async") -> Union[Engine, AsyncEngine]:
:return: An SQLAlchemy engine instance, either synchronous or asynchronous.
"""
if engine == "sync":
- SQLALCHEMY_DATABASE_URL = get_database_url(engine = "sync")
+ SQLALCHEMY_DATABASE_URL = get_database_url(engine="sync")
sync_engine = create_engine(url=SQLALCHEMY_DATABASE_URL, echo=config.confecho)
return sync_engine
@@ -63,6 +63,7 @@ def get_engine(engine: str = "async") -> Union[Engine, AsyncEngine]:
async_engine = create_async_engine(url=SQLALCHEMY_DATABASE_URL, echo=config.confecho)
return async_engine
+
def get_async_session() -> async_sessionmaker:
"""
Create an asynchronous session factory for handling database sessions.
diff --git a/fastapi_ecom/database/db_setup.py b/fastapi_ecom/database/db_setup.py
index d3a38c0..477b191 100644
--- a/fastapi_ecom/database/db_setup.py
+++ b/fastapi_ecom/database/db_setup.py
@@ -41,6 +41,7 @@ def make_database() -> None:
command.stamp(alembic_config, "head")
success("Database marked at migration head successfully")
+
async def get_db() -> AsyncGenerator[AsyncSession, None]:
"""
Dependency function to provide a database session for FastAPI routes.
diff --git a/fastapi_ecom/database/models/business.py b/fastapi_ecom/database/models/business.py
index 14ec35a..cf5b4dd 100644
--- a/fastapi_ecom/database/models/business.py
+++ b/fastapi_ecom/database/models/business.py
@@ -30,6 +30,7 @@ class Business(baseobjc, UUIDCreatableMixin, DateCreatableMixin, DateUpdateableM
:cvar products: Relationship to the `Product` model, representing the products offered by the
business.
"""
+
__tablename__ = "businesses"
id = Column("id", Integer, primary_key=True, index=True, autoincrement=True)
diff --git a/fastapi_ecom/database/models/customer.py b/fastapi_ecom/database/models/customer.py
index 92bc1a3..10fd2e1 100644
--- a/fastapi_ecom/database/models/customer.py
+++ b/fastapi_ecom/database/models/customer.py
@@ -29,6 +29,7 @@ class Customer(baseobjc, UUIDCreatableMixin, DateCreatableMixin, DateUpdateableM
:cvar created_via_oauth: Flag indicating if the customer account is created using OAuth.
:cvar orders: Relationship to the `Order` model, representing orders placed by the customer.
"""
+
__tablename__ = "customers"
id = Column("id", Integer, primary_key=True, index=True, autoincrement=True)
diff --git a/fastapi_ecom/database/models/order.py b/fastapi_ecom/database/models/order.py
index 3d57e6a..70bb1a9 100644
--- a/fastapi_ecom/database/models/order.py
+++ b/fastapi_ecom/database/models/order.py
@@ -24,10 +24,11 @@ class Order(baseobjc, UUIDCreatableMixin, DateCreatableMixin, DateUpdateableMixi
:cvar customers: Relationship to the `Customer` model, representing the customer who placed the
order. The record will reflect customer deletion (passive deletes).
"""
+
__tablename__ = "orders"
id = Column("id", Integer, primary_key=True, index=True, autoincrement=True)
- user_id = Column("user_id", Text, ForeignKey('customers.uuid', ondelete="CASCADE"), nullable=False)
+ user_id = Column("user_id", Text, ForeignKey("customers.uuid", ondelete="CASCADE"), nullable=False)
order_date = Column("order_date", Date, nullable=False)
total_price = Column("total_price", Float, nullable=False)
diff --git a/fastapi_ecom/database/models/order_details.py b/fastapi_ecom/database/models/order_details.py
index 1c010ec..9c62804 100644
--- a/fastapi_ecom/database/models/order_details.py
+++ b/fastapi_ecom/database/models/order_details.py
@@ -25,13 +25,14 @@ class OrderDetail(baseobjc, UUIDCreatableMixin, DateCreatableMixin, DateUpdateab
:cvar products: Relationship to the `Product` model, representing the associated product in
the order detail.
"""
+
__tablename__ = "order_details"
id = Column("id", Integer, primary_key=True, index=True, autoincrement=True)
- product_id = Column("product_id", Text, ForeignKey('products.uuid'), nullable=False)
+ product_id = Column("product_id", Text, ForeignKey("products.uuid"), nullable=False)
quantity = Column("quantity", Integer, nullable=False)
price = Column("product_price", Float, nullable=False)
- order_id = Column("order_id", Text, ForeignKey('orders.uuid', ondelete="CASCADE"), nullable=False)
+ order_id = Column("order_id", Text, ForeignKey("orders.uuid", ondelete="CASCADE"), nullable=False)
orders = relationship("Order", back_populates="order_details", passive_deletes=True)
products = relationship("Product", back_populates="order_details")
diff --git a/fastapi_ecom/database/models/product.py b/fastapi_ecom/database/models/product.py
index 1abf0c8..46147b7 100644
--- a/fastapi_ecom/database/models/product.py
+++ b/fastapi_ecom/database/models/product.py
@@ -28,6 +28,7 @@ class Product(baseobjc, UUIDCreatableMixin, DateCreatableMixin, DateUpdateableMi
:cvar order_details: Relationship to the `OrderDetail` model, representing the details of
orders containing this product.
"""
+
__tablename__ = "products"
id = Column("id", Integer, primary_key=True, index=True, autoincrement=True)
@@ -37,7 +38,7 @@ class Product(baseobjc, UUIDCreatableMixin, DateCreatableMixin, DateUpdateableMi
mfg_date = Column("manufacturing_date", Date, nullable=False)
exp_date = Column("expiry_date", Date, nullable=False)
price = Column("product_price", Float, nullable=False)
- business_id = Column("business_id", Text, ForeignKey('businesses.uuid', ondelete="CASCADE"), nullable=False)
+ business_id = Column("business_id", Text, ForeignKey("businesses.uuid", ondelete="CASCADE"), nullable=False)
businesses = relationship("Business", back_populates="products", passive_deletes=True)
order_details = relationship("OrderDetail", back_populates="products")
diff --git a/fastapi_ecom/database/models/util.py b/fastapi_ecom/database/models/util.py
index 9be6053..856bc71 100644
--- a/fastapi_ecom/database/models/util.py
+++ b/fastapi_ecom/database/models/util.py
@@ -13,6 +13,7 @@ class UUIDCreatableMixin:
:cvar uuid: An 8-character hexadecimal string serving as a unique identifier for the record.
This UUID is generated upon creation and remains constant.
"""
+
uuid = Column("uuid", Text, unique=True, nullable=False, default=uuid4().hex[0:8])
@@ -23,6 +24,7 @@ class DateCreatableMixin:
:cvar creation_date: Timestamp marking when the record was created.
Automatically set to the current UTC datetime at creation.
"""
+
creation_date = Column("creation_date", TIMESTAMP(timezone=True), nullable=False, default=partial(datetime.now, tz=UTC))
@@ -34,4 +36,5 @@ class DateUpdateableMixin:
Automatically set to the current UTC datetime at creation and should be
updated on each modification.
"""
+
update_date = Column("update_date", TIMESTAMP(timezone=True), nullable=False, default=partial(datetime.now, tz=UTC))
diff --git a/fastapi_ecom/database/pydantic_schemas/business.py b/fastapi_ecom/database/pydantic_schemas/business.py
index 0048e60..46ce029 100644
--- a/fastapi_ecom/database/pydantic_schemas/business.py
+++ b/fastapi_ecom/database/pydantic_schemas/business.py
@@ -1,5 +1,4 @@
from datetime import datetime
-from typing import Optional
from pydantic import BaseModel, EmailStr
@@ -10,6 +9,7 @@ class BusinessBase(BaseModel):
"""
Base model for business-related schemas, providing shared configurations.
"""
+
class Config:
"""
This class enables attribute mapping from database model instances to Pydantic models
@@ -19,6 +19,7 @@ class Config:
input data directly (i.e., allow assigning to the model attributes
directly without validation).
"""
+
from_attributes = True
@@ -33,12 +34,13 @@ class BusinessView(BusinessBase):
:ivar city: City in which the business is located.
:ivar state: State in which the business is located.
"""
+
email: EmailStr
name: str
- addr_line_1: Optional[str]
- addr_line_2: Optional[str]
- city: Optional[str]
- state: Optional[str]
+ addr_line_1: str | None
+ addr_line_2: str | None
+ city: str | None
+ state: str | None
class BusinessCreate(BusinessView):
@@ -48,6 +50,7 @@ class BusinessCreate(BusinessView):
:ivar password: Password for the business account.
"""
+
password: str
@@ -63,13 +66,14 @@ class BusinessUpdate(BaseModel):
:ivar state: Updated state of the business, optional.
:ivar password: Updated password for the business account, optional.
"""
- email: Optional[EmailStr] = ""
- name: Optional[str] = ""
- addr_line_1: Optional[str] = ""
- addr_line_2: Optional[str] = ""
- city: Optional[str] = ""
- state: Optional[str] = ""
- password: Optional[str] = ""
+
+ email: EmailStr | None = ""
+ name: str | None = ""
+ addr_line_1: str | None = ""
+ addr_line_2: str | None = ""
+ city: str | None = ""
+ state: str | None = ""
+ password: str | None = ""
class BusinessInternal(BusinessCreate):
@@ -82,6 +86,7 @@ class BusinessInternal(BusinessCreate):
:ivar is_verified: Indicates whether the business has been verified, defaults to False.
:ivar creation_date: Timestamp of when the business was created.
"""
+
id: int
uuid: str
is_verified: bool = False
@@ -94,6 +99,7 @@ class BusinessResult(APIResult):
:ivar business: Contains details of a single business.
"""
+
business: BusinessView
@@ -103,4 +109,5 @@ class BusinessManyResult(APIResult):
:ivar businesses: List of businesses with their details.
"""
+
businesses: list[BusinessView] = []
diff --git a/fastapi_ecom/database/pydantic_schemas/customer.py b/fastapi_ecom/database/pydantic_schemas/customer.py
index d2946c3..50a0c34 100644
--- a/fastapi_ecom/database/pydantic_schemas/customer.py
+++ b/fastapi_ecom/database/pydantic_schemas/customer.py
@@ -1,5 +1,4 @@
from datetime import datetime
-from typing import Optional
from pydantic import BaseModel, EmailStr
@@ -10,6 +9,7 @@ class CustomerBase(BaseModel):
"""
Base model for customer-related schemas, providing shared configurations.
"""
+
class Config:
"""
This class enables attribute mapping from database model instances to Pydantic models
@@ -19,6 +19,7 @@ class Config:
input data directly (i.e., allow assigning to the model attributes
directly without validation).
"""
+
from_attributes = True
@@ -33,12 +34,13 @@ class CustomerView(CustomerBase):
:ivar city: City in which the customer resides.
:ivar state: State in which the customer resides.
"""
+
email: EmailStr
name: str
- addr_line_1: Optional[str]
- addr_line_2: Optional[str]
- city: Optional[str]
- state: Optional[str]
+ addr_line_1: str | None
+ addr_line_2: str | None
+ city: str | None
+ state: str | None
class CustomerCreate(CustomerView):
@@ -48,6 +50,7 @@ class CustomerCreate(CustomerView):
:ivar password: Password for the customer account.
"""
+
password: str
@@ -63,13 +66,14 @@ class CustomerUpdate(BaseModel):
:ivar state: Updated state of the customer, optional.
:ivar password: Updated password for the customer account, optional.
"""
- email: Optional[EmailStr] = ""
- name: Optional[str] = ""
- addr_line_1: Optional[str] = ""
- addr_line_2: Optional[str] = ""
- city: Optional[str] = ""
- state: Optional[str] = ""
- password: Optional[str] = ""
+
+ email: EmailStr | None = ""
+ name: str | None = ""
+ addr_line_1: str | None = ""
+ addr_line_2: str | None = ""
+ city: str | None = ""
+ state: str | None = ""
+ password: str | None = ""
class CustomerInternal(CustomerCreate):
@@ -82,6 +86,7 @@ class CustomerInternal(CustomerCreate):
:ivar is_verified: Indicates whether the customer has been verified, defaults to False.
:ivar creation_datetime: Timestamp of when the customer was created.
"""
+
id: int
uuid: str
is_verified: bool = False
@@ -94,6 +99,7 @@ class CustomerResult(APIResult):
:ivar customer: Contains details of a single customer.
"""
+
customer: CustomerView
@@ -103,4 +109,5 @@ class CustomerManyResult(APIResult):
:ivar customers: List of customer details.
"""
+
customers: list[CustomerView] = []
diff --git a/fastapi_ecom/database/pydantic_schemas/order.py b/fastapi_ecom/database/pydantic_schemas/order.py
index 42468fa..083986a 100644
--- a/fastapi_ecom/database/pydantic_schemas/order.py
+++ b/fastapi_ecom/database/pydantic_schemas/order.py
@@ -14,6 +14,7 @@ class OrderBase(BaseModel):
"""
Base model for order-related schemas, providing shared configurations.
"""
+
class Config:
"""
This class enables attribute mapping from database model instances to Pydantic models
@@ -23,6 +24,7 @@ class Config:
input data directly (i.e., allow assigning to the model attributes
directly without validation).
"""
+
from_attributes = True
@@ -33,6 +35,7 @@ class OrderCreate(OrderBase):
:ivar order_date: Date and time when the order was placed.
:ivar order_items: List of order details, including product information and quantity.
"""
+
order_date: datetime
order_items: list[OrderDetailsCreate]
@@ -46,6 +49,7 @@ class OrderView(OrderCreate):
:ivar total_price: Total price of all items in the order.
:ivar order_items: Detailed information about the items in the order.
"""
+
uuid: str
total_price: float
order_items: list[OrderDetailsView]
@@ -60,6 +64,7 @@ class OrderViewInternal(OrderView):
:ivar order_items: Detailed information about the items in the order, including internal
identifiers.
"""
+
user_id: str
order_items: list[OrderDetailsViewInternal]
@@ -74,6 +79,7 @@ class OrderResult(APIResult):
:ivar order: Contains detailed information about a single order.
"""
+
order: OrderView
@@ -83,6 +89,7 @@ class OrderResultInternal(APIResult):
:ivar order: Contains detailed information about a single order, including internal details.
"""
+
order: OrderViewInternal
@@ -92,6 +99,7 @@ class OrderManyResult(APIResult):
:ivar orders: List of orders with detailed information.
"""
+
orders: list[OrderView] = []
@@ -101,4 +109,5 @@ class OrderManyResultInternal(APIResult):
:ivar orders: List of orders with detailed internal information.
"""
+
orders: list[OrderViewInternal] = []
diff --git a/fastapi_ecom/database/pydantic_schemas/order_details.py b/fastapi_ecom/database/pydantic_schemas/order_details.py
index c54c879..af9c486 100644
--- a/fastapi_ecom/database/pydantic_schemas/order_details.py
+++ b/fastapi_ecom/database/pydantic_schemas/order_details.py
@@ -5,6 +5,7 @@ class OrderDetailsBase(BaseModel):
"""
Base model for order detail-related schemas, providing shared configurations.
"""
+
class Config:
"""
This class enables attribute mapping from database model instances to Pydantic models
@@ -14,6 +15,7 @@ class Config:
input data directly (i.e., allow assigning to the model attributes
directly without validation).
"""
+
from_attributes = True
@@ -24,6 +26,7 @@ class OrderDetailsCreate(OrderDetailsBase):
:ivar product_id: UUID of the product associated with the order detail.
:ivar quantity: Number of units of the product in the order.
"""
+
product_id: str
quantity: int
@@ -36,6 +39,7 @@ class OrderDetailsView(OrderDetailsCreate):
:ivar price (float): Price of the product at the time of the order.
"""
+
price: float
@@ -46,6 +50,7 @@ class OrderDetailsViewInternal(OrderDetailsView):
:ivar uuid: Unique identifier for the order detail.
"""
+
uuid: str
diff --git a/fastapi_ecom/database/pydantic_schemas/product.py b/fastapi_ecom/database/pydantic_schemas/product.py
index 944a5e0..561933c 100644
--- a/fastapi_ecom/database/pydantic_schemas/product.py
+++ b/fastapi_ecom/database/pydantic_schemas/product.py
@@ -1,5 +1,4 @@
from datetime import datetime
-from typing import Optional
from pydantic import BaseModel
@@ -10,6 +9,7 @@ class ProductBase(BaseModel):
"""
Base model for product-related schemas, providing shared configurations.
"""
+
class Config:
"""
This class enables attribute mapping from database model instances to Pydantic models
@@ -19,6 +19,7 @@ class Config:
input data directly (i.e., allow assigning to the model attributes
directly without validation).
"""
+
from_attributes = True
@@ -33,6 +34,7 @@ class ProductView(ProductBase):
:ivar exp_date: Expiration date of the product.
:ivar price: Price of the product.
"""
+
name: str
description: str
category: str
@@ -49,6 +51,7 @@ class ProductViewInternal(ProductView):
:ivar uuid: Unique identifier for the product.
:ivar business_id: Identifier for the business associated with the product.
"""
+
uuid: str
business_id: str
@@ -57,6 +60,7 @@ class ProductCreate(ProductView):
"""
Schema for creating a new product, inheriting from ProductView to require standard attributes.
"""
+
pass
@@ -71,12 +75,13 @@ class ProductUpdate(BaseModel):
:ivar exp_date: Updated expiration date, defaults to "1900-01-01".
:ivar price: Updated price of the product, defaults to 0.0.
"""
- name: Optional[str] = ""
- description: Optional[str] = ""
- category: Optional[str] = ""
- mfg_date: Optional[datetime] = "1900-01-01T00:00:00.000Z"
- exp_date: Optional[datetime] = "1900-01-01T00:00:00.000Z"
- price: Optional[float] = 0.0
+
+ name: str | None = ""
+ description: str | None = ""
+ category: str | None = ""
+ mfg_date: datetime | None = "1900-01-01T00:00:00.000Z"
+ exp_date: datetime | None = "1900-01-01T00:00:00.000Z"
+ price: float | None = 0.0
class ProductInternal(ProductCreate):
@@ -88,6 +93,7 @@ class ProductInternal(ProductCreate):
:ivar uuid: Short UUID for uniquely identifying the product.
:ivar business_id: ID of the business which is associated with this product.
"""
+
id: int
uuid: str
business_id: str
@@ -99,6 +105,7 @@ class ProductResult(APIResult):
:ivar product: Contains detailed information about a single product.
"""
+
product: ProductView
@@ -108,6 +115,7 @@ class ProductResultInternal(APIResult):
:ivar product: Contains detailed information about a single product, including internal details.
"""
+
product: ProductViewInternal
@@ -117,6 +125,7 @@ class ProductManyResult(APIResult):
:ivar products: List of products with detailed information.
"""
+
products: list[ProductView] = []
@@ -126,4 +135,5 @@ class ProductManyResultInternal(APIResult):
:ivar products: List of products with detailed internal information.
"""
+
products: list[ProductViewInternal] = []
diff --git a/fastapi_ecom/database/pydantic_schemas/util.py b/fastapi_ecom/database/pydantic_schemas/util.py
index b00d64f..85bc3cb 100644
--- a/fastapi_ecom/database/pydantic_schemas/util.py
+++ b/fastapi_ecom/database/pydantic_schemas/util.py
@@ -1,5 +1,4 @@
from enum import Enum
-from typing import Optional
from pydantic import BaseModel
@@ -13,6 +12,7 @@ class APIResultAction(str, Enum):
:cvar put: Represents an HTTP PUT action.
:cvar delete: Represents an HTTP DELETE action.
"""
+
get = "get"
post = "post"
put = "put"
@@ -25,4 +25,5 @@ class APIResult(BaseModel):
:ivar action: The HTTP method associated with the API response, if applicable.
"""
- action: Optional[APIResultAction]
+
+ action: APIResultAction | None
diff --git a/fastapi_ecom/main.py b/fastapi_ecom/main.py
index 19f09be..6182b02 100644
--- a/fastapi_ecom/main.py
+++ b/fastapi_ecom/main.py
@@ -18,6 +18,7 @@ def main() -> None:
"""
pass
+
@main.command(name="setup", help="Setup the database schema")
def setup() -> None:
"""
@@ -31,6 +32,7 @@ def setup() -> None:
make_database()
success("Database schema setup completed")
+
@main.command(name="start", help="Start the FastAPI eComm application")
def start() -> None:
"""
@@ -43,6 +45,7 @@ def start() -> None:
general("Starting FastAPI eComm application")
start_service()
+
@main.command(name="create-migration", help="Create a new migration script")
@click.argument("comment", type=str)
@click.option("--autogenerate", is_flag=True, help="Automatically generate the migration")
@@ -59,6 +62,7 @@ def create_migration(comment: str, autogenerate: bool) -> None:
alembic_migration.create(comment, autogenerate)
success(f"Migration created successfully: {comment}")
+
@main.command(name="db-version", help="Show the current database version")
def db_version() -> None:
"""
@@ -71,6 +75,7 @@ def db_version() -> None:
general("Checking database version")
alembic_migration.db_version()
+
@main.command(name="upgrade-db", help="Upgrade the database to a specific version")
@click.argument("version", type=str, default="head")
def upgrade_db(version: str) -> None:
@@ -86,6 +91,7 @@ def upgrade_db(version: str) -> None:
alembic_migration.upgrade(version)
success(f"Database upgraded to version: {version}")
+
@main.command(name="downgrade-db", help="Downgrade the database to a specific version")
@click.argument("version", type=str)
def downgrade_db(version: str) -> None:
diff --git a/fastapi_ecom/migrations/env.py b/fastapi_ecom/migrations/env.py
index bb2b581..0878d8e 100644
--- a/fastapi_ecom/migrations/env.py
+++ b/fastapi_ecom/migrations/env.py
@@ -8,7 +8,7 @@
from sqlalchemy.ext.asyncio import async_engine_from_config
from fastapi_ecom.database import baseobjc
-from fastapi_ecom.database.models import ( #noqa: F401
+from fastapi_ecom.database.models import ( # noqa: F401
business,
customer,
order,
@@ -84,6 +84,7 @@ def run_migrations_offline() -> None:
# Single database connection with async db. Ref. https://github.com/sqlalchemy/alembic/blob/main/alembic/templates/async/env.py
+
def do_run_migrations(connection):
context.configure(connection=connection, target_metadata=target_metadata)
diff --git a/fastapi_ecom/migrations/main.py b/fastapi_ecom/migrations/main.py
index f46362f..833aca2 100644
--- a/fastapi_ecom/migrations/main.py
+++ b/fastapi_ecom/migrations/main.py
@@ -1,4 +1,3 @@
-
from alembic import command, config, runtime, script
from fastapi_ecom.database import get_database_url, migrpath
@@ -59,14 +58,10 @@ def _get_rev_current(rev: str, context: object) -> list:
:return: An empty list as required by the Alembic environment context callback.
"""
- curtrevs.update(
- _rev.cmd_format(verbose=False) for _rev in scrtobjc.get_all_current(rev)
- )
+ curtrevs.update(_rev.cmd_format(verbose=False) for _rev in scrtobjc.get_all_current(rev))
return []
- with runtime.environment.EnvironmentContext(
- self.config, scrtobjc, fn=_get_rev_current, dont_mutate=True
- ):
+ with runtime.environment.EnvironmentContext(self.config, scrtobjc, fn=_get_rev_current, dont_mutate=True):
scrtobjc.run_env()
return curtrevs
@@ -94,7 +89,7 @@ def upgrade(self, version: str) -> None:
post_revs = self._get_current()
if pre_revs == post_revs:
print("There is nothing to upgrade.")
- else: #pragma: no cover
+ else: # pragma: no cover
"""
This part of the cover cannot be tested as `sqlite` database does not support all the `ALTER TABLE` DDLs.
"""
@@ -114,7 +109,7 @@ def downgrade(self, version: str) -> None:
if pre_revs == post_revs:
print("There is nothing to downgrade.")
else:
- print("Downgraded to:", post_revs if post_revs else '')
+ print("Downgraded to:", post_revs if post_revs else "")
# Instantiate the AlembicMigration class for use in managing migrations.
diff --git a/fastapi_ecom/migrations/versions/02d907ef8e5b_add_oauth_related_fields.py b/fastapi_ecom/migrations/versions/02d907ef8e5b_add_oauth_related_fields.py
index 1681988..382e5c8 100644
--- a/fastapi_ecom/migrations/versions/02d907ef8e5b_add_oauth_related_fields.py
+++ b/fastapi_ecom/migrations/versions/02d907ef8e5b_add_oauth_related_fields.py
@@ -5,88 +5,56 @@
Create Date: 2025-07-27 19:45:21.800069
"""
+
from collections.abc import Sequence
-from typing import Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
-revision: str = '02d907ef8e5b'
-down_revision: Union[str, None] = '60c8ccec25ac'
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
+revision: str = "02d907ef8e5b"
+down_revision: str | None = "60c8ccec25ac"
+branch_labels: str | Sequence[str] | None = None
+depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
- op.add_column('businesses', sa.Column('oauth_provider', sa.String(length=50), nullable=True))
- op.add_column('businesses', sa.Column('oauth_id', sa.String(length=100), nullable=True))
- op.add_column('businesses', sa.Column('oauth_email', sa.String(length=100), nullable=True))
- op.add_column('businesses', sa.Column('created_via_oauth', sa.Boolean(), nullable=True))
- op.alter_column('businesses', 'password',
- existing_type=sa.TEXT(),
- nullable=True)
- op.alter_column('businesses', 'address_line_1',
- existing_type=sa.TEXT(),
- nullable=True)
- op.alter_column('businesses', 'city',
- existing_type=sa.TEXT(),
- nullable=True)
- op.alter_column('businesses', 'state',
- existing_type=sa.TEXT(),
- nullable=True)
- op.add_column('customers', sa.Column('oauth_provider', sa.String(length=50), nullable=True))
- op.add_column('customers', sa.Column('oauth_id', sa.String(length=100), nullable=True))
- op.add_column('customers', sa.Column('oauth_email', sa.String(length=100), nullable=True))
- op.add_column('customers', sa.Column('created_via_oauth', sa.Boolean(), nullable=True))
- op.alter_column('customers', 'password',
- existing_type=sa.TEXT(),
- nullable=True)
- op.alter_column('customers', 'address_line_1',
- existing_type=sa.TEXT(),
- nullable=True)
- op.alter_column('customers', 'city',
- existing_type=sa.TEXT(),
- nullable=True)
- op.alter_column('customers', 'state',
- existing_type=sa.TEXT(),
- nullable=True)
+ op.add_column("businesses", sa.Column("oauth_provider", sa.String(length=50), nullable=True))
+ op.add_column("businesses", sa.Column("oauth_id", sa.String(length=100), nullable=True))
+ op.add_column("businesses", sa.Column("oauth_email", sa.String(length=100), nullable=True))
+ op.add_column("businesses", sa.Column("created_via_oauth", sa.Boolean(), nullable=True))
+ op.alter_column("businesses", "password", existing_type=sa.TEXT(), nullable=True)
+ op.alter_column("businesses", "address_line_1", existing_type=sa.TEXT(), nullable=True)
+ op.alter_column("businesses", "city", existing_type=sa.TEXT(), nullable=True)
+ op.alter_column("businesses", "state", existing_type=sa.TEXT(), nullable=True)
+ op.add_column("customers", sa.Column("oauth_provider", sa.String(length=50), nullable=True))
+ op.add_column("customers", sa.Column("oauth_id", sa.String(length=100), nullable=True))
+ op.add_column("customers", sa.Column("oauth_email", sa.String(length=100), nullable=True))
+ op.add_column("customers", sa.Column("created_via_oauth", sa.Boolean(), nullable=True))
+ op.alter_column("customers", "password", existing_type=sa.TEXT(), nullable=True)
+ op.alter_column("customers", "address_line_1", existing_type=sa.TEXT(), nullable=True)
+ op.alter_column("customers", "city", existing_type=sa.TEXT(), nullable=True)
+ op.alter_column("customers", "state", existing_type=sa.TEXT(), nullable=True)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
- op.alter_column('customers', 'state',
- existing_type=sa.TEXT(),
- nullable=False)
- op.alter_column('customers', 'city',
- existing_type=sa.TEXT(),
- nullable=False)
- op.alter_column('customers', 'address_line_1',
- existing_type=sa.TEXT(),
- nullable=False)
- op.alter_column('customers', 'password',
- existing_type=sa.TEXT(),
- nullable=False)
- op.drop_column('customers', 'created_via_oauth')
- op.drop_column('customers', 'oauth_email')
- op.drop_column('customers', 'oauth_id')
- op.drop_column('customers', 'oauth_provider')
- op.alter_column('businesses', 'state',
- existing_type=sa.TEXT(),
- nullable=False)
- op.alter_column('businesses', 'city',
- existing_type=sa.TEXT(),
- nullable=False)
- op.alter_column('businesses', 'address_line_1',
- existing_type=sa.TEXT(),
- nullable=False)
- op.alter_column('businesses', 'password',
- existing_type=sa.TEXT(),
- nullable=False)
- op.drop_column('businesses', 'created_via_oauth')
- op.drop_column('businesses', 'oauth_email')
- op.drop_column('businesses', 'oauth_id')
- op.drop_column('businesses', 'oauth_provider')
+ op.alter_column("customers", "state", existing_type=sa.TEXT(), nullable=False)
+ op.alter_column("customers", "city", existing_type=sa.TEXT(), nullable=False)
+ op.alter_column("customers", "address_line_1", existing_type=sa.TEXT(), nullable=False)
+ op.alter_column("customers", "password", existing_type=sa.TEXT(), nullable=False)
+ op.drop_column("customers", "created_via_oauth")
+ op.drop_column("customers", "oauth_email")
+ op.drop_column("customers", "oauth_id")
+ op.drop_column("customers", "oauth_provider")
+ op.alter_column("businesses", "state", existing_type=sa.TEXT(), nullable=False)
+ op.alter_column("businesses", "city", existing_type=sa.TEXT(), nullable=False)
+ op.alter_column("businesses", "address_line_1", existing_type=sa.TEXT(), nullable=False)
+ op.alter_column("businesses", "password", existing_type=sa.TEXT(), nullable=False)
+ op.drop_column("businesses", "created_via_oauth")
+ op.drop_column("businesses", "oauth_email")
+ op.drop_column("businesses", "oauth_id")
+ op.drop_column("businesses", "oauth_provider")
# ### end Alembic commands ###
diff --git a/fastapi_ecom/migrations/versions/29f5c5a0304d_modify_date_field_mixin.py b/fastapi_ecom/migrations/versions/29f5c5a0304d_modify_date_field_mixin.py
index 3d23657..e24c4d8 100644
--- a/fastapi_ecom/migrations/versions/29f5c5a0304d_modify_date_field_mixin.py
+++ b/fastapi_ecom/migrations/versions/29f5c5a0304d_modify_date_field_mixin.py
@@ -5,49 +5,31 @@
Create Date: 2024-08-11 13:25:16.313148
"""
+
from collections.abc import Sequence
-from typing import Union
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
-revision: str = '29f5c5a0304d'
-down_revision: Union[str, None] = '97e78a849e66'
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
+revision: str = "29f5c5a0304d"
+down_revision: str | None = "97e78a849e66"
+branch_labels: str | Sequence[str] | None = None
+depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
- op.alter_column('businesses', 'creation_date',
- existing_type=postgresql.TIMESTAMP(),
- type_=sa.TIMESTAMP(timezone=True),
- existing_nullable=False)
- op.alter_column('customers', 'creation_date',
- existing_type=postgresql.TIMESTAMP(),
- type_=sa.TIMESTAMP(timezone=True),
- existing_nullable=False)
- op.alter_column('orders', 'creation_date',
- existing_type=postgresql.TIMESTAMP(),
- type_=sa.TIMESTAMP(timezone=True),
- existing_nullable=False)
+ op.alter_column("businesses", "creation_date", existing_type=postgresql.TIMESTAMP(), type_=sa.TIMESTAMP(timezone=True), existing_nullable=False)
+ op.alter_column("customers", "creation_date", existing_type=postgresql.TIMESTAMP(), type_=sa.TIMESTAMP(timezone=True), existing_nullable=False)
+ op.alter_column("orders", "creation_date", existing_type=postgresql.TIMESTAMP(), type_=sa.TIMESTAMP(timezone=True), existing_nullable=False)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
- op.alter_column('orders', 'creation_date',
- existing_type=sa.TIMESTAMP(timezone=True),
- type_=postgresql.TIMESTAMP(),
- existing_nullable=False)
- op.alter_column('customers', 'creation_date',
- existing_type=sa.TIMESTAMP(timezone=True),
- type_=postgresql.TIMESTAMP(),
- existing_nullable=False)
- op.alter_column('businesses', 'creation_date',
- existing_type=sa.TIMESTAMP(timezone=True),
- type_=postgresql.TIMESTAMP(),
- existing_nullable=False)
+ op.alter_column("orders", "creation_date", existing_type=sa.TIMESTAMP(timezone=True), type_=postgresql.TIMESTAMP(), existing_nullable=False)
+ op.alter_column("customers", "creation_date", existing_type=sa.TIMESTAMP(timezone=True), type_=postgresql.TIMESTAMP(), existing_nullable=False)
+ op.alter_column("businesses", "creation_date", existing_type=sa.TIMESTAMP(timezone=True), type_=postgresql.TIMESTAMP(), existing_nullable=False)
# ### end Alembic commands ###
diff --git a/fastapi_ecom/migrations/versions/306d801996a6_initial_model_setup.py b/fastapi_ecom/migrations/versions/306d801996a6_initial_model_setup.py
index dd932ae..7bf0c7e 100644
--- a/fastapi_ecom/migrations/versions/306d801996a6_initial_model_setup.py
+++ b/fastapi_ecom/migrations/versions/306d801996a6_initial_model_setup.py
@@ -5,109 +5,117 @@
Create Date: 2024-07-26 22:01:52.223573
"""
+
from collections.abc import Sequence
-from typing import Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
-revision: str = '306d801996a6'
-down_revision: Union[str, None] = None
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
+revision: str = "306d801996a6"
+down_revision: str | None = None
+branch_labels: str | Sequence[str] | None = None
+depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
- op.create_table('businesses',
- sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
- sa.Column('email_address', sa.String(length=100), nullable=False),
- sa.Column('password', sa.String(length=100), nullable=False),
- sa.Column('business_name', sa.String(length=100), nullable=False),
- sa.Column('address_line_1', sa.Text(), nullable=False),
- sa.Column('address_line_2', sa.Text(), nullable=True),
- sa.Column('city', sa.Text(), nullable=False),
- sa.Column('state', sa.Text(), nullable=False),
- sa.Column('is_verified', sa.Boolean(), nullable=True),
- sa.Column('creation_date', sa.Date(), nullable=False),
- sa.Column('uuid', sa.Text(), nullable=False),
- sa.PrimaryKeyConstraint('id'),
- sa.UniqueConstraint('uuid')
+ op.create_table(
+ "businesses",
+ sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
+ sa.Column("email_address", sa.String(length=100), nullable=False),
+ sa.Column("password", sa.String(length=100), nullable=False),
+ sa.Column("business_name", sa.String(length=100), nullable=False),
+ sa.Column("address_line_1", sa.Text(), nullable=False),
+ sa.Column("address_line_2", sa.Text(), nullable=True),
+ sa.Column("city", sa.Text(), nullable=False),
+ sa.Column("state", sa.Text(), nullable=False),
+ sa.Column("is_verified", sa.Boolean(), nullable=True),
+ sa.Column("creation_date", sa.Date(), nullable=False),
+ sa.Column("uuid", sa.Text(), nullable=False),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("uuid"),
)
- op.create_index(op.f('ix_businesses_email_address'), 'businesses', ['email_address'], unique=True)
- op.create_index(op.f('ix_businesses_id'), 'businesses', ['id'], unique=False)
- op.create_table('customers',
- sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
- sa.Column('email_address', sa.String(length=100), nullable=False),
- sa.Column('password', sa.String(length=100), nullable=False),
- sa.Column('full_name', sa.String(length=100), nullable=False),
- sa.Column('address_line_1', sa.Text(), nullable=False),
- sa.Column('address_line_2', sa.Text(), nullable=True),
- sa.Column('city', sa.Text(), nullable=False),
- sa.Column('state', sa.Text(), nullable=False),
- sa.Column('is_verified', sa.Boolean(), nullable=True),
- sa.Column('creation_date', sa.Date(), nullable=False),
- sa.Column('uuid', sa.Text(), nullable=False),
- sa.PrimaryKeyConstraint('id'),
- sa.UniqueConstraint('uuid')
+ op.create_index(op.f("ix_businesses_email_address"), "businesses", ["email_address"], unique=True)
+ op.create_index(op.f("ix_businesses_id"), "businesses", ["id"], unique=False)
+ op.create_table(
+ "customers",
+ sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
+ sa.Column("email_address", sa.String(length=100), nullable=False),
+ sa.Column("password", sa.String(length=100), nullable=False),
+ sa.Column("full_name", sa.String(length=100), nullable=False),
+ sa.Column("address_line_1", sa.Text(), nullable=False),
+ sa.Column("address_line_2", sa.Text(), nullable=True),
+ sa.Column("city", sa.Text(), nullable=False),
+ sa.Column("state", sa.Text(), nullable=False),
+ sa.Column("is_verified", sa.Boolean(), nullable=True),
+ sa.Column("creation_date", sa.Date(), nullable=False),
+ sa.Column("uuid", sa.Text(), nullable=False),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("uuid"),
)
- op.create_index(op.f('ix_customers_email_address'), 'customers', ['email_address'], unique=True)
- op.create_index(op.f('ix_customers_id'), 'customers', ['id'], unique=False)
- op.create_table('orders',
- sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
- sa.Column('user_id', sa.Text(), nullable=False),
- sa.Column('order_date', sa.Date(), nullable=False),
- sa.Column('total_price', sa.Float(), nullable=False),
- sa.Column('uuid', sa.Text(), nullable=False),
- sa.ForeignKeyConstraint(['user_id'], ['customers.uuid'], ondelete='CASCADE'),
- sa.PrimaryKeyConstraint('id'),
- sa.UniqueConstraint('uuid')
+ op.create_index(op.f("ix_customers_email_address"), "customers", ["email_address"], unique=True)
+ op.create_index(op.f("ix_customers_id"), "customers", ["id"], unique=False)
+ op.create_table(
+ "orders",
+ sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
+ sa.Column("user_id", sa.Text(), nullable=False),
+ sa.Column("order_date", sa.Date(), nullable=False),
+ sa.Column("total_price", sa.Float(), nullable=False),
+ sa.Column("uuid", sa.Text(), nullable=False),
+ sa.ForeignKeyConstraint(["user_id"], ["customers.uuid"], ondelete="CASCADE"),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("uuid"),
)
- op.create_index(op.f('ix_orders_id'), 'orders', ['id'], unique=False)
- op.create_table('products',
- sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
- sa.Column('product_name', sa.String(length=100), nullable=False),
- sa.Column('description', sa.Text(), nullable=True),
- sa.Column('category', sa.String(length=50), nullable=False),
- sa.Column('manufacturing_date', sa.Date(), nullable=False),
- sa.Column('expiry_date', sa.Date(), nullable=False),
- sa.Column('product_price', sa.Float(), nullable=False),
- sa.Column('business_id', sa.Text(), nullable=False),
- sa.Column('uuid', sa.Text(), nullable=False),
- sa.ForeignKeyConstraint(['business_id'], ['businesses.uuid'], ondelete='CASCADE'),
- sa.PrimaryKeyConstraint('id'),
- sa.UniqueConstraint('uuid')
+ op.create_index(op.f("ix_orders_id"), "orders", ["id"], unique=False)
+ op.create_table(
+ "products",
+ sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
+ sa.Column("product_name", sa.String(length=100), nullable=False),
+ sa.Column("description", sa.Text(), nullable=True),
+ sa.Column("category", sa.String(length=50), nullable=False),
+ sa.Column("manufacturing_date", sa.Date(), nullable=False),
+ sa.Column("expiry_date", sa.Date(), nullable=False),
+ sa.Column("product_price", sa.Float(), nullable=False),
+ sa.Column("business_id", sa.Text(), nullable=False),
+ sa.Column("uuid", sa.Text(), nullable=False),
+ sa.ForeignKeyConstraint(["business_id"], ["businesses.uuid"], ondelete="CASCADE"),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("uuid"),
)
- op.create_index(op.f('ix_products_id'), 'products', ['id'], unique=False)
- op.create_table('order_details',
- sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
- sa.Column('product_id', sa.Text(), nullable=False),
- sa.Column('quantity', sa.Integer(), nullable=False),
- sa.Column('product_price', sa.Float(), nullable=False),
- sa.Column('order_id', sa.Text(), nullable=False),
- sa.Column('uuid', sa.Text(), nullable=False),
- sa.ForeignKeyConstraint(['order_id'], ['orders.uuid'], ondelete='CASCADE'),
- sa.ForeignKeyConstraint(['product_id'], ['products.uuid'], ),
- sa.PrimaryKeyConstraint('id'),
- sa.UniqueConstraint('uuid')
+ op.create_index(op.f("ix_products_id"), "products", ["id"], unique=False)
+ op.create_table(
+ "order_details",
+ sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
+ sa.Column("product_id", sa.Text(), nullable=False),
+ sa.Column("quantity", sa.Integer(), nullable=False),
+ sa.Column("product_price", sa.Float(), nullable=False),
+ sa.Column("order_id", sa.Text(), nullable=False),
+ sa.Column("uuid", sa.Text(), nullable=False),
+ sa.ForeignKeyConstraint(["order_id"], ["orders.uuid"], ondelete="CASCADE"),
+ sa.ForeignKeyConstraint(
+ ["product_id"],
+ ["products.uuid"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("uuid"),
)
- op.create_index(op.f('ix_order_details_id'), 'order_details', ['id'], unique=False)
+ op.create_index(op.f("ix_order_details_id"), "order_details", ["id"], unique=False)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
- op.drop_index(op.f('ix_order_details_id'), table_name='order_details')
- op.drop_table('order_details')
- op.drop_index(op.f('ix_products_id'), table_name='products')
- op.drop_table('products')
- op.drop_index(op.f('ix_orders_id'), table_name='orders')
- op.drop_table('orders')
- op.drop_index(op.f('ix_customers_id'), table_name='customers')
- op.drop_index(op.f('ix_customers_email_address'), table_name='customers')
- op.drop_table('customers')
- op.drop_index(op.f('ix_businesses_id'), table_name='businesses')
- op.drop_index(op.f('ix_businesses_email_address'), table_name='businesses')
- op.drop_table('businesses')
+ op.drop_index(op.f("ix_order_details_id"), table_name="order_details")
+ op.drop_table("order_details")
+ op.drop_index(op.f("ix_products_id"), table_name="products")
+ op.drop_table("products")
+ op.drop_index(op.f("ix_orders_id"), table_name="orders")
+ op.drop_table("orders")
+ op.drop_index(op.f("ix_customers_id"), table_name="customers")
+ op.drop_index(op.f("ix_customers_email_address"), table_name="customers")
+ op.drop_table("customers")
+ op.drop_index(op.f("ix_businesses_id"), table_name="businesses")
+ op.drop_index(op.f("ix_businesses_email_address"), table_name="businesses")
+ op.drop_table("businesses")
# ### end Alembic commands ###
diff --git a/fastapi_ecom/migrations/versions/60c8ccec25ac_create_and_update_date_with_relation_.py b/fastapi_ecom/migrations/versions/60c8ccec25ac_create_and_update_date_with_relation_.py
index 25c1932..78dc938 100644
--- a/fastapi_ecom/migrations/versions/60c8ccec25ac_create_and_update_date_with_relation_.py
+++ b/fastapi_ecom/migrations/versions/60c8ccec25ac_create_and_update_date_with_relation_.py
@@ -5,38 +5,38 @@
Create Date: 2024-10-31 16:03:13.152354
"""
+
from collections.abc import Sequence
-from typing import Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
-revision: str = '60c8ccec25ac'
-down_revision: Union[str, None] = '29f5c5a0304d'
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
+revision: str = "60c8ccec25ac"
+down_revision: str | None = "29f5c5a0304d"
+branch_labels: str | Sequence[str] | None = None
+depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
- op.add_column('businesses', sa.Column('update_date', sa.TIMESTAMP(timezone=True), nullable=False))
- op.add_column('customers', sa.Column('update_date', sa.TIMESTAMP(timezone=True), nullable=False))
- op.add_column('order_details', sa.Column('creation_date', sa.TIMESTAMP(timezone=True), nullable=False))
- op.add_column('order_details', sa.Column('update_date', sa.TIMESTAMP(timezone=True), nullable=False))
- op.add_column('orders', sa.Column('update_date', sa.TIMESTAMP(timezone=True), nullable=False))
- op.add_column('products', sa.Column('creation_date', sa.TIMESTAMP(timezone=True), nullable=False))
- op.add_column('products', sa.Column('update_date', sa.TIMESTAMP(timezone=True), nullable=False))
+ op.add_column("businesses", sa.Column("update_date", sa.TIMESTAMP(timezone=True), nullable=False))
+ op.add_column("customers", sa.Column("update_date", sa.TIMESTAMP(timezone=True), nullable=False))
+ op.add_column("order_details", sa.Column("creation_date", sa.TIMESTAMP(timezone=True), nullable=False))
+ op.add_column("order_details", sa.Column("update_date", sa.TIMESTAMP(timezone=True), nullable=False))
+ op.add_column("orders", sa.Column("update_date", sa.TIMESTAMP(timezone=True), nullable=False))
+ op.add_column("products", sa.Column("creation_date", sa.TIMESTAMP(timezone=True), nullable=False))
+ op.add_column("products", sa.Column("update_date", sa.TIMESTAMP(timezone=True), nullable=False))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
- op.drop_column('products', 'update_date')
- op.drop_column('products', 'creation_date')
- op.drop_column('orders', 'update_date')
- op.drop_column('order_details', 'update_date')
- op.drop_column('order_details', 'creation_date')
- op.drop_column('customers', 'update_date')
- op.drop_column('businesses', 'update_date')
+ op.drop_column("products", "update_date")
+ op.drop_column("products", "creation_date")
+ op.drop_column("orders", "update_date")
+ op.drop_column("order_details", "update_date")
+ op.drop_column("order_details", "creation_date")
+ op.drop_column("customers", "update_date")
+ op.drop_column("businesses", "update_date")
# ### end Alembic commands ###
diff --git a/fastapi_ecom/migrations/versions/7d1eff027259_remove_creation_date_from_base_models_.py b/fastapi_ecom/migrations/versions/7d1eff027259_remove_creation_date_from_base_models_.py
index 2909bd0..796c5d1 100644
--- a/fastapi_ecom/migrations/versions/7d1eff027259_remove_creation_date_from_base_models_.py
+++ b/fastapi_ecom/migrations/versions/7d1eff027259_remove_creation_date_from_base_models_.py
@@ -5,40 +5,28 @@
Create Date: 2024-07-26 22:47:38.245376
"""
+
from collections.abc import Sequence
-from typing import Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
-revision: str = '7d1eff027259'
-down_revision: Union[str, None] = 'd6dd218459b7'
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
+revision: str = "7d1eff027259"
+down_revision: str | None = "d6dd218459b7"
+branch_labels: str | Sequence[str] | None = None
+depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
- op.alter_column('businesses', 'creation_date',
- existing_type=sa.DATE(),
- type_=sa.DateTime(),
- existing_nullable=False)
- op.alter_column('customers', 'creation_date',
- existing_type=sa.DATE(),
- type_=sa.DateTime(),
- existing_nullable=False)
+ op.alter_column("businesses", "creation_date", existing_type=sa.DATE(), type_=sa.DateTime(), existing_nullable=False)
+ op.alter_column("customers", "creation_date", existing_type=sa.DATE(), type_=sa.DateTime(), existing_nullable=False)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
- op.alter_column('customers', 'creation_date',
- existing_type=sa.DateTime(),
- type_=sa.DATE(),
- existing_nullable=False)
- op.alter_column('businesses', 'creation_date',
- existing_type=sa.DateTime(),
- type_=sa.DATE(),
- existing_nullable=False)
+ op.alter_column("customers", "creation_date", existing_type=sa.DateTime(), type_=sa.DATE(), existing_nullable=False)
+ op.alter_column("businesses", "creation_date", existing_type=sa.DateTime(), type_=sa.DATE(), existing_nullable=False)
# ### end Alembic commands ###
diff --git a/fastapi_ecom/migrations/versions/97e78a849e66_add_creation_date_mixin_in_orders.py b/fastapi_ecom/migrations/versions/97e78a849e66_add_creation_date_mixin_in_orders.py
index 891c59a..fab3b53 100644
--- a/fastapi_ecom/migrations/versions/97e78a849e66_add_creation_date_mixin_in_orders.py
+++ b/fastapi_ecom/migrations/versions/97e78a849e66_add_creation_date_mixin_in_orders.py
@@ -5,26 +5,26 @@
Create Date: 2024-07-30 20:05:56.484586
"""
+
from collections.abc import Sequence
-from typing import Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
-revision: str = '97e78a849e66'
-down_revision: Union[str, None] = '7d1eff027259'
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
+revision: str = "97e78a849e66"
+down_revision: str | None = "7d1eff027259"
+branch_labels: str | Sequence[str] | None = None
+depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
- op.add_column('orders', sa.Column('creation_date', sa.DateTime(), nullable=False))
+ op.add_column("orders", sa.Column("creation_date", sa.DateTime(), nullable=False))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
- op.drop_column('orders', 'creation_date')
+ op.drop_column("orders", "creation_date")
# ### end Alembic commands ###
diff --git a/fastapi_ecom/migrations/versions/d6dd218459b7_change_datatype_of_password_column.py b/fastapi_ecom/migrations/versions/d6dd218459b7_change_datatype_of_password_column.py
index f558a91..9b9a9d9 100644
--- a/fastapi_ecom/migrations/versions/d6dd218459b7_change_datatype_of_password_column.py
+++ b/fastapi_ecom/migrations/versions/d6dd218459b7_change_datatype_of_password_column.py
@@ -5,40 +5,28 @@
Create Date: 2024-07-26 22:07:29.440283
"""
+
from collections.abc import Sequence
-from typing import Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
-revision: str = 'd6dd218459b7'
-down_revision: Union[str, None] = '306d801996a6'
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
+revision: str = "d6dd218459b7"
+down_revision: str | None = "306d801996a6"
+branch_labels: str | Sequence[str] | None = None
+depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
- op.alter_column('businesses', 'password',
- existing_type=sa.VARCHAR(length=100),
- type_=sa.Text(),
- existing_nullable=False)
- op.alter_column('customers', 'password',
- existing_type=sa.VARCHAR(length=100),
- type_=sa.Text(),
- existing_nullable=False)
+ op.alter_column("businesses", "password", existing_type=sa.VARCHAR(length=100), type_=sa.Text(), existing_nullable=False)
+ op.alter_column("customers", "password", existing_type=sa.VARCHAR(length=100), type_=sa.Text(), existing_nullable=False)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
- op.alter_column('customers', 'password',
- existing_type=sa.Text(),
- type_=sa.VARCHAR(length=100),
- existing_nullable=False)
- op.alter_column('businesses', 'password',
- existing_type=sa.Text(),
- type_=sa.VARCHAR(length=100),
- existing_nullable=False)
+ op.alter_column("customers", "password", existing_type=sa.Text(), type_=sa.VARCHAR(length=100), existing_nullable=False)
+ op.alter_column("businesses", "password", existing_type=sa.Text(), type_=sa.VARCHAR(length=100), existing_nullable=False)
# ### end Alembic commands ###
diff --git a/fastapi_ecom/router/business.py b/fastapi_ecom/router/business.py
index 5772fd4..1d0619a 100644
--- a/fastapi_ecom/router/business.py
+++ b/fastapi_ecom/router/business.py
@@ -23,6 +23,7 @@
router = APIRouter(prefix="/business")
+
@router.post("/create", status_code=status.HTTP_201_CREATED, response_model=BusinessResult, tags=["business"])
async def create_business(business: BusinessCreate, db: AsyncSession = Depends(get_db)) -> BusinessResult:
"""
@@ -40,16 +41,16 @@ async def create_business(business: BusinessCreate, db: AsyncSession = Depends(g
- If there are other database errors, it returns a 500 Internal Server Error.
"""
salt = bcrypt.gensalt()
- hashed_password = bcrypt.hashpw(business.password.strip().encode('utf-8'), salt)
+ hashed_password = bcrypt.hashpw(business.password.strip().encode("utf-8"), salt)
db_business = Business(
email=business.email.strip(),
- password=hashed_password.decode('utf-8'),
+ password=hashed_password.decode("utf-8"),
name=business.name.strip(),
addr_line_1=business.addr_line_1.strip(),
addr_line_2=business.addr_line_2.strip(),
city=business.city.strip(),
state=business.state.strip(),
- uuid=uuid4().hex[0:8] # Assign UUID manually; One UUID per transaction
+ uuid=uuid4().hex[0:8], # Assign UUID manually; One UUID per transaction
)
general(f"Adding account for business in database: {business.email}")
db.add(db_business)
@@ -57,24 +58,16 @@ async def create_business(business: BusinessCreate, db: AsyncSession = Depends(g
await db.flush()
except IntegrityError as expt:
failure(f"Business account creation failed - Uniqueness constraint violation for email: {business.email}")
- raise HTTPException(
- status_code=status.HTTP_409_CONFLICT,
- detail="Uniqueness constraint failed - Please try again"
- ) from expt
+ raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Uniqueness constraint failed - Please try again") from expt
except Exception as expt:
failure(f"Business account creation failed with unexpected error for email: {business.email}")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
success(f"Business account created successfully with email: {business.email}")
- return {
- "action": "post",
- "business": BusinessView.model_validate(db_business).model_dump()
- }
+ return {"action": "post", "business": BusinessView.model_validate(db_business).model_dump()}
+
@router.get("/me", status_code=status.HTTP_200_OK, tags=["business"])
-async def get_business_me(business_auth = Depends(verify_business_cred)) -> dict[str, str]:
+async def get_business_me(business_auth=Depends(verify_business_cred)) -> dict[str, str]:
"""
Endpoint to fetch the email of the currently authenticated business.
@@ -83,16 +76,14 @@ async def get_business_me(business_auth = Depends(verify_business_cred)) -> dict
:return: Dictionary containing the action type and the authenticated business's email.
"""
general(f"Business authentication successful for: {business_auth.email}")
- return {
- "action": "get",
- "email": business_auth.email
- }
+ return {"action": "get", "email": business_auth.email}
+
@router.get("/search", status_code=status.HTTP_200_OK, response_model=BusinessManyResult, tags=["business"])
async def get_businesses(
skip: int = Query(0, ge=0, description="Number of records to skip (must be between 0 and int64)"),
limit: int = Query(100, ge=1, le=100, description="Maximum number of records to return (must be between 1 and 100)"),
- db: AsyncSession = Depends(get_db)
+ db: AsyncSession = Depends(get_db),
) -> BusinessManyResult:
"""
Endpoint fetches a paginated list of businesses from the database.
@@ -113,18 +104,13 @@ async def get_businesses(
businesses = result.scalars().all()
if not businesses:
warning("No businesses found in database")
- raise HTTPException(
- status_code=status.HTTP_404_NOT_FOUND,
- detail="No business present in database"
- )
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="No business present in database")
success(f"Found {len(businesses)} businesses")
- return {
- "action": "get",
- "businesses": [BusinessView.model_validate(business).model_dump() for business in businesses]
- }
+ return {"action": "get", "businesses": [BusinessView.model_validate(business).model_dump() for business in businesses]}
+
@router.delete("/delete/me", status_code=status.HTTP_202_ACCEPTED, response_model=BusinessResult, tags=["business"])
-async def delete_business(db: AsyncSession = Depends(get_db), business_auth = Depends(verify_business_cred)) -> BusinessResult:
+async def delete_business(db: AsyncSession = Depends(get_db), business_auth=Depends(verify_business_cred)) -> BusinessResult:
"""
Endpoint for an authenticated business to delete its own record.
@@ -146,28 +132,21 @@ async def delete_business(db: AsyncSession = Depends(get_db), business_auth = De
await db.execute(query)
try:
await db.flush()
- except Exception as expt: #pragma: no cover
+ except Exception as expt: # pragma: no cover
"""
This part of the code cannot be tested as this endpoint performs multiple database
interactions due to which mocking one part wont produce the desired result. Thus,
we will keep it uncovered until a alternative can be made for testing this exception block.
"""
failure(f"Business account deletion failed for: {business_auth.email}")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
success(f"Business account deleted successfully: {business_auth.email}")
- return {
- "action": "delete",
- "business": BusinessView.model_validate(business_to_delete).model_dump()
- }
+ return {"action": "delete", "business": BusinessView.model_validate(business_to_delete).model_dump()}
+
@router.put("/update/me", status_code=status.HTTP_202_ACCEPTED, response_model=BusinessResult, tags=["business"])
async def update_business(
- business: BusinessUpdate,
- db: AsyncSession = Depends(get_db),
- business_auth = Depends(verify_business_cred)
+ business: BusinessUpdate, db: AsyncSession = Depends(get_db), business_auth=Depends(verify_business_cred)
) -> BusinessResult:
"""
Endpoint for an authenticated business to update its own record.
@@ -191,13 +170,13 @@ async def update_business(
is_updated = False
bus_cols = ["email", "name", "addr_line_1", "addr_line_2", "city", "state"]
for item in bus_cols:
- if getattr(business, item) != "":
- setattr(business_to_update, item, getattr(business, item).strip())
- is_updated = True
+ if getattr(business, item) != "":
+ setattr(business_to_update, item, getattr(business, item).strip())
+ is_updated = True
if business.password != "":
salt = bcrypt.gensalt()
- hashed_password = bcrypt.hashpw(business.password.encode('utf-8'), salt)
- business_to_update.password = hashed_password.decode('utf-8')
+ hashed_password = bcrypt.hashpw(business.password.encode("utf-8"), salt)
+ business_to_update.password = hashed_password.decode("utf-8")
is_updated = True
if is_updated:
business_to_update.update_date = datetime.now(timezone.utc)
@@ -205,23 +184,14 @@ async def update_business(
await db.flush()
except IntegrityError as expt:
failure(f"Business update failed - Uniqueness constraint violation for: {business_email}")
- raise HTTPException(
- status_code=status.HTTP_409_CONFLICT,
- detail="Uniqueness constraint failed - Please try again"
- ) from expt
- except Exception as expt: #pragma: no cover
+ raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Uniqueness constraint failed - Please try again") from expt
+ except Exception as expt: # pragma: no cover
"""
This part of the code cannot be tested as this endpoint performs multiple database
interactions due to which mocking one part wont produce the desired result. Thus,
we will keep it uncovered until a alternative can be made for testing this exception block.
"""
failure(f"Business update failed with unexpected error for: {business_email}")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
success(f"Business details updated successfully: {business_email}")
- return {
- "action": "put",
- "business": BusinessView.model_validate(business_to_update).model_dump()
- }
+ return {"action": "put", "business": BusinessView.model_validate(business_to_update).model_dump()}
diff --git a/fastapi_ecom/router/customer.py b/fastapi_ecom/router/customer.py
index 275798e..0554df7 100644
--- a/fastapi_ecom/router/customer.py
+++ b/fastapi_ecom/router/customer.py
@@ -23,6 +23,7 @@
router = APIRouter(prefix="/customer")
+
@router.post("/create", status_code=status.HTTP_201_CREATED, response_model=CustomerResult, tags=["customer"])
async def create_customer(customer: CustomerCreate, db: AsyncSession = Depends(get_db)) -> CustomerResult:
"""
@@ -40,16 +41,16 @@ async def create_customer(customer: CustomerCreate, db: AsyncSession = Depends(g
serialized using the `BusinessView` schema.
"""
salt = bcrypt.gensalt()
- hashed_password = bcrypt.hashpw(customer.password.strip().encode('utf-8'), salt)
+ hashed_password = bcrypt.hashpw(customer.password.strip().encode("utf-8"), salt)
db_customer = Customer(
- email = customer.email.strip(),
- password = hashed_password.decode('utf-8'),
- name = customer.name.strip(),
- addr_line_1 = customer.addr_line_1.strip(),
- addr_line_2 = customer.addr_line_2.strip(),
- city = customer.city.strip(),
- state = customer.state.strip(),
- uuid = uuid4().hex[0:8] # Assign UUID manually; One UUID per transaction
+ email=customer.email.strip(),
+ password=hashed_password.decode("utf-8"),
+ name=customer.name.strip(),
+ addr_line_1=customer.addr_line_1.strip(),
+ addr_line_2=customer.addr_line_2.strip(),
+ city=customer.city.strip(),
+ state=customer.state.strip(),
+ uuid=uuid4().hex[0:8], # Assign UUID manually; One UUID per transaction
)
general(f"Adding account for customer in database: {customer.email}")
db.add(db_customer)
@@ -57,24 +58,16 @@ async def create_customer(customer: CustomerCreate, db: AsyncSession = Depends(g
await db.flush()
except IntegrityError as expt:
failure(f"Customer account creation failed - Uniqueness constraint violation for email: {customer.email}")
- raise HTTPException(
- status_code=status.HTTP_409_CONFLICT,
- detail="Uniqueness constraint failed - Please try again"
- ) from expt
+ raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Uniqueness constraint failed - Please try again") from expt
except Exception as expt:
failure(f"Customer account creation failed with unexpected error for email: {customer.email}")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
success(f"Customer account created successfully with email: {customer.email}")
- return {
- "action": "post",
- "customer": CustomerView.model_validate(db_customer).model_dump()
- }
+ return {"action": "post", "customer": CustomerView.model_validate(db_customer).model_dump()}
+
@router.get("/me", status_code=status.HTTP_200_OK, tags=["customer"])
-async def get_customer_me(customer_auth = Depends(verify_cust_cred)) -> dict[str, str]:
+async def get_customer_me(customer_auth=Depends(verify_cust_cred)) -> dict[str, str]:
"""
Endpoint to fetch the email of the currently authenticated customer.
@@ -83,16 +76,14 @@ async def get_customer_me(customer_auth = Depends(verify_cust_cred)) -> dict[str
:return: Dictionary containing the action type and the authenticated customer's email.
"""
general(f"Customer authentication successful for: {customer_auth.email}")
- return {
- "action": "get",
- "email": customer_auth.email
- }
+ return {"action": "get", "email": customer_auth.email}
+
@router.get("/search", status_code=status.HTTP_200_OK, response_model=CustomerManyResult, tags=["customer"])
async def get_customers(
skip: int = Query(0, ge=0, description="Number of records to skip (must be between 0 and int64)"),
limit: int = Query(100, ge=1, le=100, description="Maximum number of records to return (must be between 1 and 100)"),
- db: AsyncSession = Depends(get_db)
+ db: AsyncSession = Depends(get_db),
) -> CustomerManyResult:
"""
Endpoint fetches a paginated list of customers from the database.
@@ -113,18 +104,13 @@ async def get_customers(
customers = result.scalars().all()
if not customers:
warning("No customers found in database")
- raise HTTPException(
- status_code=status.HTTP_404_NOT_FOUND,
- detail="No customer present in database"
- )
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="No customer present in database")
success(f"Found {len(customers)} customers")
- return {
- "action": "get",
- "customers": [CustomerView.model_validate(customer).model_dump() for customer in customers]
- }
+ return {"action": "get", "customers": [CustomerView.model_validate(customer).model_dump() for customer in customers]}
+
@router.delete("/delete/me", status_code=status.HTTP_202_ACCEPTED, response_model=CustomerResult, tags=["customer"])
-async def delete_customer(db: AsyncSession = Depends(get_db), customer_auth = Depends(verify_cust_cred)) -> CustomerResult:
+async def delete_customer(db: AsyncSession = Depends(get_db), customer_auth=Depends(verify_cust_cred)) -> CustomerResult:
"""
Endpoint for an authenticated customer to delete its own record.
@@ -146,25 +132,20 @@ async def delete_customer(db: AsyncSession = Depends(get_db), customer_auth = De
await db.execute(query)
try:
await db.flush()
- except Exception as expt: #pragma: no cover
+ except Exception as expt: # pragma: no cover
"""
This part of the code cannot be tested as this endpoint performs multiple database
interactions due to which mocking one part wont produce the desired result. Thus,
we will keep it uncovered until a alternative can be made for testing this exception block.
"""
failure(f"Customer account deletion failed for: {customer_auth.email}")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
success(f"Customer account deleted successfully: {customer_auth.email}")
- return {
- "action": "delete",
- "customer": CustomerView.model_validate(customer_to_delete).model_dump()
- }
+ return {"action": "delete", "customer": CustomerView.model_validate(customer_to_delete).model_dump()}
+
@router.put("/update/me", status_code=status.HTTP_202_ACCEPTED, response_model=CustomerResult, tags=["customer"])
-async def update_customer(customer: CustomerUpdate, db: AsyncSession = Depends(get_db), customer_auth = Depends(verify_cust_cred)) -> CustomerResult:
+async def update_customer(customer: CustomerUpdate, db: AsyncSession = Depends(get_db), customer_auth=Depends(verify_cust_cred)) -> CustomerResult:
"""
Endpoint for an authenticated customer to update its own record.
@@ -186,13 +167,13 @@ async def update_customer(customer: CustomerUpdate, db: AsyncSession = Depends(g
is_updated = False
cus_cols = ["email", "name", "addr_line_1", "addr_line_2", "city", "state"]
for item in cus_cols:
- if getattr(customer, item) != "":
- setattr(customer_to_update, item, getattr(customer, item).strip())
- is_updated = True
+ if getattr(customer, item) != "":
+ setattr(customer_to_update, item, getattr(customer, item).strip())
+ is_updated = True
if customer.password != "":
salt = bcrypt.gensalt()
- hashed_password = bcrypt.hashpw(customer.password.encode('utf-8'), salt)
- customer_to_update.password = hashed_password.decode('utf-8')
+ hashed_password = bcrypt.hashpw(customer.password.encode("utf-8"), salt)
+ customer_to_update.password = hashed_password.decode("utf-8")
is_updated = True
if is_updated:
customer_to_update.update_date = datetime.now(timezone.utc)
@@ -200,23 +181,14 @@ async def update_customer(customer: CustomerUpdate, db: AsyncSession = Depends(g
await db.flush()
except IntegrityError as expt:
failure(f"Customer update failed - Uniqueness constraint violation for: {customer_email}")
- raise HTTPException(
- status_code=status.HTTP_409_CONFLICT,
- detail="Uniqueness constraint failed - Please try again"
- ) from expt
- except Exception as expt: #pragma: no cover
+ raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Uniqueness constraint failed - Please try again") from expt
+ except Exception as expt: # pragma: no cover
"""
This part of the code cannot be tested as this endpoint performs multiple database
interactions due to which mocking one part wont produce the desired result. Thus,
we will keep it uncovered until a alternative can be made for testing this exception block.
"""
failure(f"Customer update failed with unexpected error for: {customer_email}")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
success(f"Customer details updated successfully: {customer_email}")
- return {
- "action": "put",
- "customer": CustomerView.model_validate(customer_to_update).model_dump()
- }
+ return {"action": "put", "customer": CustomerView.model_validate(customer_to_update).model_dump()}
diff --git a/fastapi_ecom/router/order.py b/fastapi_ecom/router/order.py
index a0287ee..36c6b74 100644
--- a/fastapi_ecom/router/order.py
+++ b/fastapi_ecom/router/order.py
@@ -28,8 +28,9 @@
router = APIRouter(prefix="/order")
+
@router.post("/create", status_code=status.HTTP_201_CREATED, response_model=OrderResultInternal, tags=["order"])
-async def create_order(order: OrderCreate, db: AsyncSession = Depends(get_db), customer_auth = Depends(verify_cust_cred)) -> OrderResultInternal:
+async def create_order(order: OrderCreate, db: AsyncSession = Depends(get_db), customer_auth=Depends(verify_cust_cred)) -> OrderResultInternal:
"""
Endpoint to place an order by the authenticated customer.
@@ -44,25 +45,22 @@ async def create_order(order: OrderCreate, db: AsyncSession = Depends(get_db), c
If database integrity constraints fails, it returns a 500 Internal Server Error.
"""
new_order = Order(
- user_id = customer_auth.uuid,
- order_date = order.order_date,
- total_price = 0.0, # Initial total, calculated below
- uuid = uuid4().hex[0:8] # Assign UUID manually; One UUID per transaction
+ user_id=customer_auth.uuid,
+ order_date=order.order_date,
+ total_price=0.0, # Initial total, calculated below
+ uuid=uuid4().hex[0:8], # Assign UUID manually; One UUID per transaction
)
db.add(new_order)
try:
await db.flush() # Generate order ID for relations
- except Exception as expt: #pragma: no cover
+ except Exception as expt: # pragma: no cover
"""
This part of the code cannot be tested as this endpoint performs multiple database
interactions due to which mocking one part wont produce the desired result. Thus,
we will keep it uncovered until a alternative can be made for testing this exception block.
"""
failure(f"Order creation failed with unexpected error for customer: {customer_auth.email}")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
# Create OrderDetail entries and calculate the total price
total_price = 0
@@ -72,17 +70,14 @@ async def create_order(order: OrderCreate, db: AsyncSession = Depends(get_db), c
result = await db.execute(query)
product = result.scalar_one_or_none()
if not product:
- raise HTTPException(
- status_code = status.HTTP_404_NOT_FOUND,
- detail = f"Product with ID: {item.product_id} does not exist."
- )
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Product with ID: {item.product_id} does not exist.")
total_price += product.price * item.quantity
order_detail = OrderDetail(
- order_id = new_order.uuid,
- product_id = item.product_id,
- quantity = item.quantity,
- price = product.price,
- uuid = uuid4().hex[0:8] # Assign UUID manually; One UUID per transaction
+ order_id=new_order.uuid,
+ product_id=item.product_id,
+ quantity=item.quantity,
+ price=product.price,
+ uuid=uuid4().hex[0:8], # Assign UUID manually; One UUID per transaction
)
db.add(order_detail)
order_items.append(order_detail)
@@ -91,29 +86,24 @@ async def create_order(order: OrderCreate, db: AsyncSession = Depends(get_db), c
new_order.total_price = total_price
try:
await db.flush()
- except Exception as expt: #pragma: no cover
+ except Exception as expt: # pragma: no cover
"""
This part of the code cannot be tested as this endpoint performs multiple database
interactions due to which mocking one part wont produce the desired result. Thus,
we will keep it uncovered until a alternative can be made for testing this exception block.
"""
failure(f"Order creation failed with unexpected error for customer: {customer_auth.email}")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
new_order.order_items = order_items
- return {
- "action": "post",
- "order": OrderViewInternal.model_validate(new_order).model_dump()
- }
+ return {"action": "post", "order": OrderViewInternal.model_validate(new_order).model_dump()}
+
@router.get("/search", status_code=status.HTTP_200_OK, response_model=OrderManyResult, tags=["order"])
async def get_orders(
skip: int = Query(0, ge=0, description="Number of records to skip (must be between 0 and int64)"),
limit: int = Query(100, ge=1, le=100, description="Maximum number of records to return (must be between 1 and 100)"),
db: AsyncSession = Depends(get_db),
- customer_auth = Depends(verify_cust_cred)
+ customer_auth=Depends(verify_cust_cred),
) -> OrderManyResult:
"""
Endpoint fetches a paginated list of orders and its details associated with the authenticated
@@ -144,37 +134,20 @@ async def get_orders(
orders = result.scalars().unique().all()
if not orders:
warning(f"No order found in database for customer {customer_auth.email}")
- raise HTTPException(
- status_code=status.HTTP_404_NOT_FOUND,
- detail="No orders in database"
- )
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="No orders in database")
order_views = []
for order in orders:
- order_items = [
- OrderDetailsView(
- product_id = detail.product_id,
- quantity = detail.quantity,
- price = detail.price
- )
- for detail in order.order_details
- ]
- order_view_data = OrderView(
- uuid = order.uuid,
- order_date = order.order_date,
- total_price = order.total_price,
- order_items = order_items
- )
+ order_items = [OrderDetailsView(product_id=detail.product_id, quantity=detail.quantity, price=detail.price) for detail in order.order_details]
+ order_view_data = OrderView(uuid=order.uuid, order_date=order.order_date, total_price=order.total_price, order_items=order_items)
order_views.append(order_view_data.model_dump())
- return {
- "action": "get",
- "orders": order_views
- }
+ return {"action": "get", "orders": order_views}
+
@router.get("/search/internal", status_code=status.HTTP_200_OK, response_model=OrderManyResultInternal, tags=["order"])
async def get_orders_internal(
skip: int = Query(0, ge=0, description="Number of records to skip (must be between 0 and int64)"),
limit: int = Query(100, ge=1, le=100, description="Maximum number of records to return (must be between 1 and 100)"),
- db: AsyncSession = Depends(get_db)
+ db: AsyncSession = Depends(get_db),
) -> OrderManyResultInternal:
"""
Endpoint fetches a paginated list of orders and its details.
@@ -202,36 +175,22 @@ async def get_orders_internal(
orders = result.scalars().unique().all()
if not orders:
warning("No order found in database")
- raise HTTPException(
- status_code=status.HTTP_404_NOT_FOUND,
- detail="No orders in database"
- )
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="No orders in database")
order_views = []
for order in orders:
order_items = [
- OrderDetailsViewInternal(
- product_id = detail.product_id,
- quantity = detail.quantity,
- price = detail.price,
- uuid = detail.uuid
- )
+ OrderDetailsViewInternal(product_id=detail.product_id, quantity=detail.quantity, price=detail.price, uuid=detail.uuid)
for detail in order.order_details
]
order_view_data = OrderViewInternal(
- uuid = order.uuid,
- user_id = order.user_id,
- order_date = order.order_date,
- total_price = order.total_price,
- order_items = order_items
+ uuid=order.uuid, user_id=order.user_id, order_date=order.order_date, total_price=order.total_price, order_items=order_items
)
order_views.append(order_view_data.model_dump())
- return {
- "action": "get",
- "orders": order_views
- }
+ return {"action": "get", "orders": order_views}
+
@router.get("/search/uuid/{order_id}", status_code=status.HTTP_200_OK, response_model=OrderResult, tags=["order"])
-async def get_order_by_uuid(order_id: str, db: AsyncSession = Depends(get_db), customer_auth = Depends(verify_cust_cred)):
+async def get_order_by_uuid(order_id: str, db: AsyncSession = Depends(get_db), customer_auth=Depends(verify_cust_cred)):
"""
Endpoint fetches a specific order and its details by its UUID associated with the authenticated
customer.
@@ -251,28 +210,11 @@ async def get_order_by_uuid(order_id: str, db: AsyncSession = Depends(get_db), c
order = result.scalar_one_or_none()
if not order:
warning(f"Order {order_id} no present in database")
- raise HTTPException(
- status_code = status.HTTP_404_NOT_FOUND,
- detail = "Order not present in database"
- )
- order_items = [
- OrderDetailsView(
- product_id = detail.product_id,
- quantity = detail.quantity,
- price = detail.price
- )
- for detail in order.order_details
- ]
- order_view = OrderView(
- uuid = order.uuid,
- order_date = order.order_date,
- total_price = order.total_price,
- order_items = order_items
- )
- return {
- "action": "get",
- "order": OrderView.model_validate(order_view).model_dump()
- }
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Order not present in database")
+ order_items = [OrderDetailsView(product_id=detail.product_id, quantity=detail.quantity, price=detail.price) for detail in order.order_details]
+ order_view = OrderView(uuid=order.uuid, order_date=order.order_date, total_price=order.total_price, order_items=order_items)
+ return {"action": "get", "order": OrderView.model_validate(order_view).model_dump()}
+
# @router.put("/update/uuid/{order_id}", response_model=OrderResult, tags=["order"])
# async def update_order():
@@ -280,8 +222,9 @@ async def get_order_by_uuid(order_id: str, db: AsyncSession = Depends(get_db), c
# TODO: Update endpoint will be made available one the carting system is implemented
# """
+
@router.delete("/delete/uuid/{order_id}", status_code=status.HTTP_202_ACCEPTED, response_model=OrderResult, tags=["order"])
-async def delete_order(order_id: str, db: AsyncSession = Depends(get_db), customer_auth = Depends(verify_cust_cred)) -> OrderResult:
+async def delete_order(order_id: str, db: AsyncSession = Depends(get_db), customer_auth=Depends(verify_cust_cred)) -> OrderResult:
"""
Endpoint to delete a order by its UUID associated for an authenticated customer.
@@ -302,40 +245,23 @@ async def delete_order(order_id: str, db: AsyncSession = Depends(get_db), custom
order_to_delete = result.scalar_one_or_none()
if not order_to_delete:
warning(f"Order {order_id} no present in database")
- raise HTTPException(
- status_code = status.HTTP_404_NOT_FOUND,
- detail = "Order not present in database"
- )
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Order not present in database")
order_items = [
- OrderDetailsView(
- product_id = detail.product_id,
- quantity = detail.quantity,
- price = detail.price
- )
- for detail in order_to_delete.order_details
+ OrderDetailsView(product_id=detail.product_id, quantity=detail.quantity, price=detail.price) for detail in order_to_delete.order_details
]
order_view = OrderView(
- uuid = order_to_delete.uuid,
- order_date = order_to_delete.order_date,
- total_price = order_to_delete.total_price,
- order_items = order_items
+ uuid=order_to_delete.uuid, order_date=order_to_delete.order_date, total_price=order_to_delete.total_price, order_items=order_items
)
query = delete(Order).where(and_(Order.user_id == customer_auth.uuid, Order.uuid == order_id))
await db.execute(query)
try:
await db.flush()
- except Exception as expt: #pragma: no cover
+ except Exception as expt: # pragma: no cover
"""
This part of the code cannot be tested as this endpoint performs multiple database
interactions due to which mocking one part wont produce the desired result. Thus,
we will keep it uncovered until a alternative can be made for testing this exception block.
"""
failure(f"Order deletion failed with unexpected error for customer: {customer_auth.email}")
- raise HTTPException(
- status_code = status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail = "Failed while deleting"
- ) from expt
- return {
- "action": "delete",
- "order": OrderView.model_validate(order_view)
- }
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed while deleting") from expt
+ return {"action": "delete", "order": OrderView.model_validate(order_view)}
diff --git a/fastapi_ecom/router/product.py b/fastapi_ecom/router/product.py
index 2f1f383..a9a83f6 100644
--- a/fastapi_ecom/router/product.py
+++ b/fastapi_ecom/router/product.py
@@ -24,11 +24,10 @@
router = APIRouter(prefix="/product")
+
@router.post("/create", status_code=status.HTTP_201_CREATED, response_model=ProductResultInternal, tags=["product"])
async def add_product(
- product: ProductCreate,
- db: AsyncSession = Depends(get_db),
- business_auth = Depends(verify_business_cred)
+ product: ProductCreate, db: AsyncSession = Depends(get_db), business_auth=Depends(verify_business_cred)
) -> ProductResultInternal:
"""
Endpoint to add a new product by currently authenticated business.
@@ -45,41 +44,36 @@ async def add_product(
- If there are other database errors, it returns a 500 Internal Server Error.
"""
db_product = Product(
- name = product.name.strip(),
- description = product.description.strip(),
- category = product.category.strip(),
- mfg_date = product.mfg_date,
- exp_date = product.exp_date,
- price = product.price,
- business_id = business_auth.uuid,
- uuid=uuid4().hex[0:8] # Assign UUID manually; One UUID per transaction
+ name=product.name.strip(),
+ description=product.description.strip(),
+ category=product.category.strip(),
+ mfg_date=product.mfg_date,
+ exp_date=product.exp_date,
+ price=product.price,
+ business_id=business_auth.uuid,
+ uuid=uuid4().hex[0:8], # Assign UUID manually; One UUID per transaction
)
general(f"Adding product '{product.name}' to database by {business_auth.email}")
db.add(db_product)
try:
await db.flush()
- except IntegrityError as expt: #pragma: no cover
+ except IntegrityError as expt: # pragma: no cover
"""
This part of the code cannot be tested as this endpoint performs multiple database
interactions due to which mocking one part wont produce the desired result. Thus,
we will keep it uncovered until a alternative can be made for testing this exception block.
"""
failure(f"Product creation failed for '{product.name}' with unexpected error")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
success(f"Product '{product.name}' created successfully by business: {business_auth.uuid}")
- return {
- "action": "post",
- "product": ProductViewInternal.model_validate(db_product).model_dump()
- }
+ return {"action": "post", "product": ProductViewInternal.model_validate(db_product).model_dump()}
+
@router.get("/search", status_code=status.HTTP_200_OK, response_model=ProductManyResult, tags=["product"])
async def get_products(
skip: int = Query(0, ge=0, description="Number of records to skip (must be between 0 and int64)"),
limit: int = Query(100, ge=1, le=100, description="Maximum number of records to return (must be between 1 and 100)"),
- db: AsyncSession = Depends(get_db)
+ db: AsyncSession = Depends(get_db),
) -> ProductManyResult:
"""
Endpoint fetches a paginated list of products.
@@ -101,22 +95,17 @@ async def get_products(
products = result.scalars().all()
if not products:
warning("No products found in database")
- raise HTTPException(
- status_code=status.HTTP_404_NOT_FOUND,
- detail="No product present in database"
- )
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="No product present in database")
success(f"Found {len(products)} products")
- return {
- "action": "get",
- "products": [ProductView.model_validate(product).model_dump() for product in products]
- }
+ return {"action": "get", "products": [ProductView.model_validate(product).model_dump() for product in products]}
+
@router.get("/search/name/{text}", status_code=status.HTTP_200_OK, response_model=ProductManyResult, tags=["product"])
async def get_product_by_text(
text: str,
skip: int = Query(0, ge=0, description="Number of records to skip (must be between 0 and int64)"),
limit: int = Query(100, ge=1, le=100, description="Maximum number of records to return (must be between 1 and 100)"),
- db: AsyncSession = Depends(get_db)
+ db: AsyncSession = Depends(get_db),
) -> ProductManyResult:
"""
Endpoint fetches a paginated list of products by name or description.
@@ -133,29 +122,28 @@ async def get_product_by_text(
If no matching products exists in the database, it raises 404 Not Found.
"""
general(f"Searching products by text '{text}' with skip={skip}, limit={limit}")
- query = select(Product).where(
- or_(Product.name.ilike(f"%{text}%"), Product.description.ilike(f"%{text}%"))
- ).options(selectinload("*")).offset(skip).limit(limit)
+ query = (
+ select(Product)
+ .where(or_(Product.name.ilike(f"%{text}%"), Product.description.ilike(f"%{text}%")))
+ .options(selectinload("*"))
+ .offset(skip)
+ .limit(limit)
+ )
result = await db.execute(query)
products = result.scalars().all()
if not products:
warning(f"No products found matching text '{text}'")
- raise HTTPException(
- status_code=status.HTTP_404_NOT_FOUND,
- detail="No such product present in database"
- )
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="No such product present in database")
success(f"Found {len(products)} products matching text '{text}'")
- return {
- "action": "get",
- "products": [ProductView.model_validate(product).model_dump() for product in products]
- }
+ return {"action": "get", "products": [ProductView.model_validate(product).model_dump() for product in products]}
+
@router.get("/search/internal", status_code=status.HTTP_200_OK, response_model=ProductManyResultInternal, tags=["product"])
async def get_products_internal(
skip: int = Query(0, ge=0, description="Number of records to skip (must be between 0 and int64)"),
limit: int = Query(100, ge=1, le=100, description="Maximum number of records to return (must be between 1 and 100)"),
db: AsyncSession = Depends(get_db),
- business_auth = Depends(verify_business_cred)
+ business_auth=Depends(verify_business_cred),
) -> ProductManyResultInternal:
"""
Endpoint fetches a paginated list of products associated with the authenticated business.
@@ -177,21 +165,14 @@ async def get_products_internal(
products = result.scalars().all()
if not products:
warning(f"No products found for business {business_auth.email}")
- raise HTTPException(
- status_code=status.HTTP_404_NOT_FOUND,
- detail="No product present in database"
- )
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="No product present in database")
success(f"Found {len(products)} products for business {business_auth.email}")
- return {
- "action": "get",
- "products": [ProductViewInternal.model_validate(product).model_dump() for product in products]
- }
+ return {"action": "get", "products": [ProductViewInternal.model_validate(product).model_dump() for product in products]}
+
@router.get("/search/uuid/{product_id}", status_code=status.HTTP_200_OK, response_model=ProductResultInternal, tags=["product"])
async def get_product_by_uuid(
- product_id: str,
- db: AsyncSession = Depends(get_db),
- business_auth = Depends(verify_business_cred)
+ product_id: str, db: AsyncSession = Depends(get_db), business_auth=Depends(verify_business_cred)
) -> ProductResultInternal:
"""
Endpoint fetches a specific product by its UUID associated with the authenticated business.
@@ -213,22 +194,13 @@ async def get_product_by_uuid(
product_by_uuid = result.scalar_one_or_none()
if not product_by_uuid:
warning(f"Product {product_id} not found for business {business_auth.uuid}")
- raise HTTPException(
- status_code=status.HTTP_404_NOT_FOUND,
- detail="Product not present in database"
- )
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Product not present in database")
success(f"Found product {product_id} for business {business_auth.uuid}")
- return {
- "action": "get",
- "product": ProductViewInternal.model_validate(product_by_uuid)
- }
+ return {"action": "get", "product": ProductViewInternal.model_validate(product_by_uuid)}
+
@router.delete("/delete/uuid/{product_id}", status_code=status.HTTP_202_ACCEPTED, response_model=ProductResultInternal, tags=["product"])
-async def delete_product(
- product_id: str,
- db: AsyncSession = Depends(get_db),
- business_auth = Depends(verify_business_cred)
-) -> ProductResultInternal:
+async def delete_product(product_id: str, db: AsyncSession = Depends(get_db), business_auth=Depends(verify_business_cred)) -> ProductResultInternal:
"""
Endpoint to delete a product by its UUID associated for an authenticated business.
@@ -250,37 +222,26 @@ async def delete_product(
product_to_delete = result.scalar_one_or_none()
if not product_to_delete:
warning(f"Product {product_id} not found for deletion for business {business_auth.uuid}")
- raise HTTPException(
- status_code=status.HTTP_404_NOT_FOUND,
- detail="Product not present in database"
- )
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Product not present in database")
query = delete(Product).where(and_(Product.uuid == product_id, Product.business_id == business_auth.uuid))
await db.execute(query)
try:
await db.flush()
- except Exception as expt: #pragma: no cover
+ except Exception as expt: # pragma: no cover
"""
This part of the code cannot be tested as this endpoint performs multiple database
interactions due to which mocking one part wont produce the desired result. Thus,
we will keep it uncovered until a alternative can be made for testing this exception block.
"""
failure(f"Product deletion failed for {product_id} for business {business_auth.uuid}")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
success(f"Product {product_id} deleted successfully for business {business_auth.uuid}")
- return {
- "action": "delete",
- "product": ProductViewInternal.model_validate(product_to_delete).model_dump()
- }
+ return {"action": "delete", "product": ProductViewInternal.model_validate(product_to_delete).model_dump()}
+
@router.put("/update/uuid/{product_id}", status_code=status.HTTP_202_ACCEPTED, response_model=ProductResultInternal, tags=["product"])
async def update_product(
- product_id: str,
- product: ProductUpdate,
- db: AsyncSession = Depends(get_db),
- business_auth = Depends(verify_business_cred)
+ product_id: str, product: ProductUpdate, db: AsyncSession = Depends(get_db), business_auth=Depends(verify_business_cred)
) -> ProductResultInternal:
"""
Endpoint to update a product by its UUID associated for an authenticated business.
@@ -303,10 +264,7 @@ async def update_product(
product_to_update = result.scalar_one_or_none()
if not product_to_update:
warning(f"Product {product_id} not found for update for business {business_auth.uuid}")
- raise HTTPException(
- status_code=status.HTTP_404_NOT_FOUND,
- detail="Product not present in database"
- )
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Product not present in database")
is_updated = False
if product.name != "":
product_to_update.name = product.name
@@ -330,19 +288,13 @@ async def update_product(
product_to_update.update_date = datetime.now(timezone.utc)
try:
await db.flush()
- except Exception as expt: #pragma: no cover
+ except Exception as expt: # pragma: no cover
"""
This part of the code cannot be tested as this endpoint performs multiple database
interactions due to which mocking one part wont produce the desired result. Thus,
we will keep it uncovered until a alternative can be made for testing this exception block.
"""
failure(f"Product update failed for {product_id} for business {business_auth.uuid} with unexpected error")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
success(f"Product {product_id} updated successfully for business {business_auth.uuid}")
- return {
- "action": "put",
- "product": ProductViewInternal.model_validate(product_to_update).model_dump()
- }
+ return {"action": "put", "product": ProductViewInternal.model_validate(product_to_update).model_dump()}
diff --git a/fastapi_ecom/utils/auth.py b/fastapi_ecom/utils/auth.py
index 783beb7..9cf5eb2 100644
--- a/fastapi_ecom/utils/auth.py
+++ b/fastapi_ecom/utils/auth.py
@@ -1,5 +1,3 @@
-from typing import Optional
-
from fastapi import Depends, HTTPException, status
from fastapi_ecom.database.models.business import Business
@@ -10,8 +8,7 @@
async def verify_cust_cred(
- basic_customer: Optional[Customer] = Depends(verify_basic_customer_cred),
- oauth_customer: Optional[Customer] = Depends(verify_oauth_customer_cred)
+ basic_customer: Customer | None = Depends(verify_basic_customer_cred), oauth_customer: Customer | None = Depends(verify_oauth_customer_cred)
) -> Customer:
"""
Verify customer credentials using either HTTP Basic Authentication or OAuth.
@@ -35,13 +32,13 @@ async def verify_cust_cred(
failure("Customer authentication failed")
raise HTTPException(
- status_code = status.HTTP_401_UNAUTHORIZED,
- detail = "Not Authenticated",
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="Not Authenticated",
)
+
async def verify_business_cred(
- basic_business: Optional[Business] = Depends(verify_basic_business_cred),
- oauth_business: Optional[Business] = Depends(verify_oauth_business_cred)
+ basic_business: Business | None = Depends(verify_basic_business_cred), oauth_business: Business | None = Depends(verify_oauth_business_cred)
) -> Business:
"""
Verify business credentials using either HTTP Basic Authentication or OAuth.
@@ -65,6 +62,6 @@ async def verify_business_cred(
failure("Business authentication failed")
raise HTTPException(
- status_code = status.HTTP_401_UNAUTHORIZED,
- detail = "Not Authenticated",
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="Not Authenticated",
)
diff --git a/fastapi_ecom/utils/basic_auth.py b/fastapi_ecom/utils/basic_auth.py
index 7c9123b..9515941 100644
--- a/fastapi_ecom/utils/basic_auth.py
+++ b/fastapi_ecom/utils/basic_auth.py
@@ -14,6 +14,7 @@
# This will prompt users for a username and password when accessing secured endpoints.
security = HTTPBasic(auto_error=False)
+
async def verify_basic_customer_cred(credentials: HTTPBasicCredentials | None = Depends(security), db: AsyncSession = Depends(get_db)) -> Customer:
"""
Verify customer credentials using HTTP Basic Authentication.
@@ -26,7 +27,7 @@ async def verify_basic_customer_cred(credentials: HTTPBasicCredentials | None =
:return: The customer object if authentication is successful else returns None.
"""
- if not credentials: #pragma: no cover
+ if not credentials: # pragma: no cover
return None
query = select(Customer).where(Customer.email == credentials.username).options(selectinload("*"))
@@ -36,13 +37,14 @@ async def verify_basic_customer_cred(credentials: HTTPBasicCredentials | None =
if not customer_by_email:
warning(f"No customer account found for email: {credentials.username}")
return None
- elif not bcrypt.checkpw(credentials.password.encode('utf-8'), customer_by_email.password.encode('utf-8')):
+ elif not bcrypt.checkpw(credentials.password.encode("utf-8"), customer_by_email.password.encode("utf-8")):
warning(f"Invalid password for customer: {credentials.username}")
return None
else:
success(f"Customer authenticated via basic auth: {credentials.username}")
return customer_by_email
+
async def verify_basic_business_cred(credentials: HTTPBasicCredentials | None = Depends(security), db: AsyncSession = Depends(get_db)) -> Business:
"""
Verify business credentials using HTTP Basic Authentication.
@@ -55,7 +57,7 @@ async def verify_basic_business_cred(credentials: HTTPBasicCredentials | None =
:return: The business object if authentication is successful else returns None.
"""
- if not credentials: #pragma: no cover
+ if not credentials: # pragma: no cover
return None
query = select(Business).where(Business.email == credentials.username).options(selectinload("*"))
@@ -65,7 +67,7 @@ async def verify_basic_business_cred(credentials: HTTPBasicCredentials | None =
if not business_by_email:
warning(f"No business account found for email: {credentials.username}")
return None
- elif not bcrypt.checkpw(credentials.password.encode('utf-8'), business_by_email.password.encode('utf-8')):
+ elif not bcrypt.checkpw(credentials.password.encode("utf-8"), business_by_email.password.encode("utf-8")):
warning(f"Invalid password for business: {credentials.username}")
return None
else:
diff --git a/fastapi_ecom/utils/logging_setup.py b/fastapi_ecom/utils/logging_setup.py
index 98cc26b..089aa18 100644
--- a/fastapi_ecom/utils/logging_setup.py
+++ b/fastapi_ecom/utils/logging_setup.py
@@ -8,14 +8,18 @@
GENERAL = style("INFO:", fg="white", bold=True)
STDS = " "
+
def success(message):
logger.info(SUCCESS + STDS + style(message, fg="green", bold=True))
+
def failure(message):
logger.error(FAILURE + STDS + style(message, fg="red", bold=True))
+
def warning(message):
logger.warning(WARNING + STDS + style(message, fg="yellow", bold=True))
+
def general(message):
logger.info(GENERAL + STDS + message)
diff --git a/fastapi_ecom/utils/oauth.py b/fastapi_ecom/utils/oauth.py
index 3e9308c..6fcb39b 100644
--- a/fastapi_ecom/utils/oauth.py
+++ b/fastapi_ecom/utils/oauth.py
@@ -18,24 +18,19 @@
server_metadata_url = "https://accounts.google.com/.well-known/openid-configuration"
-oidc = OpenIdConnect(
- openIdConnectUrl=server_metadata_url,
- scheme_name="OpenID Connect",
- auto_error=False
-)
+oidc = OpenIdConnect(openIdConnectUrl=server_metadata_url, scheme_name="OpenID Connect", auto_error=False)
oauth = OAuth()
oauth.register(
- name = "google",
- client_id = config.GOOGLE_CLIENT_ID,
- client_secret = config.GOOGLE_CLIENT_SECRET,
- server_metadata_url = server_metadata_url,
- client_kwargs = {
- 'scope': 'openid email profile'
- }
+ name="google",
+ client_id=config.GOOGLE_CLIENT_ID,
+ client_secret=config.GOOGLE_CLIENT_SECRET,
+ server_metadata_url=server_metadata_url,
+ client_kwargs={"scope": "openid email profile"},
)
+
class OIDCUser(BaseModel):
"""
Pydantic model representing an authenticated OIDC user.
@@ -47,6 +42,7 @@ class OIDCUser(BaseModel):
:cvar name: Full name of the authenticated user.
:cvar sub: Subject identifier from the OIDC provider.
"""
+
email: str
name: str | None = None
sub: str
@@ -56,6 +52,7 @@ def from_userinfo(cls, userinfo: UserInfo) -> "OIDCUser":
fields = {field: userinfo[field] for field in cls.model_fields if field in userinfo}
return cls(**fields)
+
async def current_user(token: str = Depends(oidc)) -> OIDCUser | None:
"""
Extract and validate the current authenticated user from OIDC token.
@@ -85,6 +82,7 @@ async def current_user(token: str = Depends(oidc)) -> OIDCUser | None:
return None
return OIDCUser.from_userinfo(userinfo)
+
async def verify_oauth_customer_cred(oidc: OIDCUser = Depends(current_user), db: AsyncSession = Depends(get_db)) -> Customer:
"""
Verify OAuth customer credentials and retrieve or create customer record.
@@ -117,13 +115,10 @@ async def verify_oauth_customer_cred(oidc: OIDCUser = Depends(current_user), db:
db.add(customer_by_email)
try:
await db.flush()
- except Exception as expt: #pragma: no cover
+ except Exception as expt: # pragma: no cover
# HTTP status code 500 is already tested in other parts of the codebase
failure("Failed to update customer details in database due to unexpected error")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
else:
query = select(Customer).where(Customer.oauth_email == oidc.email).options(selectinload("*"))
result = await db.execute(query)
@@ -131,28 +126,26 @@ async def verify_oauth_customer_cred(oidc: OIDCUser = Depends(current_user), db:
if not customer_by_email:
general(f"Creating new customer via OAuth: {oidc.email}")
customer_by_email = Customer(
- email = oidc.email,
- name = oidc.name,
- uuid = uuid4().hex[0:8],
- is_verified = True, # OAuth emails are pre-verified
- oauth_provider = "google",
- oauth_id = oidc.sub,
- oauth_email = oidc.email,
- created_via_oauth = True
+ email=oidc.email,
+ name=oidc.name,
+ uuid=uuid4().hex[0:8],
+ is_verified=True, # OAuth emails are pre-verified
+ oauth_provider="google",
+ oauth_id=oidc.sub,
+ oauth_email=oidc.email,
+ created_via_oauth=True,
)
db.add(customer_by_email)
try:
await db.flush()
- except Exception as expt: #pragma: no cover
+ except Exception as expt: # pragma: no cover
# HTTP status code 500 is already tested in other parts of the codebase
failure("Failed to create customer account in database due to unexpected error")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
success(f"Customer OAuth authentication successful: {oidc.email}")
return customer_by_email
+
async def verify_oauth_business_cred(oidc: OIDCUser = Depends(current_user), db: AsyncSession = Depends(get_db)) -> Business:
"""
Verify OAuth business credentials and retrieve or create business record.
@@ -185,13 +178,10 @@ async def verify_oauth_business_cred(oidc: OIDCUser = Depends(current_user), db:
db.add(business_by_email)
try:
await db.flush()
- except Exception as expt: #pragma: no cover
+ except Exception as expt: # pragma: no cover
# HTTP status code 500 is already tested in other parts of the codebase
failure("Failed to update business details in database due to unexpected error")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
else:
query = select(Business).where(Business.oauth_email == oidc.email).options(selectinload("*"))
result = await db.execute(query)
@@ -199,24 +189,21 @@ async def verify_oauth_business_cred(oidc: OIDCUser = Depends(current_user), db:
if not business_by_email:
general(f"Creating new business via OAuth: {oidc.email}")
business_by_email = Business(
- email = oidc.email,
- name = oidc.name,
- uuid = uuid4().hex[0:8],
- is_verified = True, # OAuth emails are pre-verified
- oauth_provider = "google",
- oauth_id = oidc.sub,
- oauth_email = oidc.email,
- created_via_oauth = True
+ email=oidc.email,
+ name=oidc.name,
+ uuid=uuid4().hex[0:8],
+ is_verified=True, # OAuth emails are pre-verified
+ oauth_provider="google",
+ oauth_id=oidc.sub,
+ oauth_email=oidc.email,
+ created_via_oauth=True,
)
db.add(business_by_email)
try:
await db.flush()
- except Exception as expt: #pragma: no cover
+ except Exception as expt: # pragma: no cover
# HTTP status code 500 is already tested in other parts of the codebase
failure("Failed to create business account in database due to unexpected error")
- raise HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- ) from expt
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.") from expt
success(f"Business OAuth authentication successful: {oidc.email}")
return business_by_email
diff --git a/poetry.lock b/poetry.lock
index 0f4952c..66ed32f 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1257,30 +1257,30 @@ dev = ["pre-commit", "pytest-asyncio", "tox"]
[[package]]
name = "ruff"
-version = "0.13.1"
+version = "0.14.0"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
- {file = "ruff-0.13.1-py3-none-linux_armv6l.whl", hash = "sha256:b2abff595cc3cbfa55e509d89439b5a09a6ee3c252d92020bd2de240836cf45b"},
- {file = "ruff-0.13.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4ee9f4249bf7f8bb3984c41bfaf6a658162cdb1b22e3103eabc7dd1dc5579334"},
- {file = "ruff-0.13.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5c5da4af5f6418c07d75e6f3224e08147441f5d1eac2e6ce10dcce5e616a3bae"},
- {file = "ruff-0.13.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80524f84a01355a59a93cef98d804e2137639823bcee2931f5028e71134a954e"},
- {file = "ruff-0.13.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff7f5ce8d7988767dd46a148192a14d0f48d1baea733f055d9064875c7d50389"},
- {file = "ruff-0.13.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c55d84715061f8b05469cdc9a446aa6c7294cd4bd55e86a89e572dba14374f8c"},
- {file = "ruff-0.13.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ac57fed932d90fa1624c946dc67a0a3388d65a7edc7d2d8e4ca7bddaa789b3b0"},
- {file = "ruff-0.13.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c366a71d5b4f41f86a008694f7a0d75fe409ec298685ff72dc882f882d532e36"},
- {file = "ruff-0.13.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4ea9d1b5ad3e7a83ee8ebb1229c33e5fe771e833d6d3dcfca7b77d95b060d38"},
- {file = "ruff-0.13.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0f70202996055b555d3d74b626406476cc692f37b13bac8828acff058c9966a"},
- {file = "ruff-0.13.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f8cff7a105dad631085d9505b491db33848007d6b487c3c1979dd8d9b2963783"},
- {file = "ruff-0.13.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:9761e84255443316a258dd7dfbd9bfb59c756e52237ed42494917b2577697c6a"},
- {file = "ruff-0.13.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:3d376a88c3102ef228b102211ef4a6d13df330cb0f5ca56fdac04ccec2a99700"},
- {file = "ruff-0.13.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cbefd60082b517a82c6ec8836989775ac05f8991715d228b3c1d86ccc7df7dae"},
- {file = "ruff-0.13.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:dd16b9a5a499fe73f3c2ef09a7885cb1d97058614d601809d37c422ed1525317"},
- {file = "ruff-0.13.1-py3-none-win32.whl", hash = "sha256:55e9efa692d7cb18580279f1fbb525146adc401f40735edf0aaeabd93099f9a0"},
- {file = "ruff-0.13.1-py3-none-win_amd64.whl", hash = "sha256:3a3fb595287ee556de947183489f636b9f76a72f0fa9c028bdcabf5bab2cc5e5"},
- {file = "ruff-0.13.1-py3-none-win_arm64.whl", hash = "sha256:c0bae9ffd92d54e03c2bf266f466da0a65e145f298ee5b5846ed435f6a00518a"},
- {file = "ruff-0.13.1.tar.gz", hash = "sha256:88074c3849087f153d4bb22e92243ad4c1b366d7055f98726bc19aa08dc12d51"},
+ {file = "ruff-0.14.0-py3-none-linux_armv6l.whl", hash = "sha256:58e15bffa7054299becf4bab8a1187062c6f8cafbe9f6e39e0d5aface455d6b3"},
+ {file = "ruff-0.14.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:838d1b065f4df676b7c9957992f2304e41ead7a50a568185efd404297d5701e8"},
+ {file = "ruff-0.14.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:703799d059ba50f745605b04638fa7e9682cc3da084b2092feee63500ff3d9b8"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ba9a8925e90f861502f7d974cc60e18ca29c72bb0ee8bfeabb6ade35a3abde7"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e41f785498bd200ffc276eb9e1570c019c1d907b07cfb081092c8ad51975bbe7"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30a58c087aef4584c193aebf2700f0fbcfc1e77b89c7385e3139956fa90434e2"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f8d07350bc7af0a5ce8812b7d5c1a7293cf02476752f23fdfc500d24b79b783c"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eec3bbbf3a7d5482b5c1f42d5fc972774d71d107d447919fca620b0be3e3b75e"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16b68e183a0e28e5c176d51004aaa40559e8f90065a10a559176713fcf435206"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb732d17db2e945cfcbbc52af0143eda1da36ca8ae25083dd4f66f1542fdf82e"},
+ {file = "ruff-0.14.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:c958f66ab884b7873e72df38dcabee03d556a8f2ee1b8538ee1c2bbd619883dd"},
+ {file = "ruff-0.14.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7eb0499a2e01f6e0c285afc5bac43ab380cbfc17cd43a2e1dd10ec97d6f2c42d"},
+ {file = "ruff-0.14.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c63b2d99fafa05efca0ab198fd48fa6030d57e4423df3f18e03aa62518c565f"},
+ {file = "ruff-0.14.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:668fce701b7a222f3f5327f86909db2bbe99c30877c8001ff934c5413812ac02"},
+ {file = "ruff-0.14.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a86bf575e05cb68dcb34e4c7dfe1064d44d3f0c04bbc0491949092192b515296"},
+ {file = "ruff-0.14.0-py3-none-win32.whl", hash = "sha256:7450a243d7125d1c032cb4b93d9625dea46c8c42b4f06c6b709baac168e10543"},
+ {file = "ruff-0.14.0-py3-none-win_amd64.whl", hash = "sha256:ea95da28cd874c4d9c922b39381cbd69cb7e7b49c21b8152b014bd4f52acddc2"},
+ {file = "ruff-0.14.0-py3-none-win_arm64.whl", hash = "sha256:f42c9495f5c13ff841b1da4cb3c2a42075409592825dada7c5885c2c844ac730"},
+ {file = "ruff-0.14.0.tar.gz", hash = "sha256:62ec8969b7510f77945df916de15da55311fade8d6050995ff7f680afe582c57"},
]
[[package]]
@@ -1495,4 +1495,4 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
-content-hash = "aea202e07520074419c7336d054ab5efcdfbb7778844df474b52327ffb49d209"
+content-hash = "a33e373758d2d880aecbacff121af7d322c8f74d87fd8475d901de8153019488"
diff --git a/pyproject.toml b/pyproject.toml
index 45aaaa7..9bf1ead 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -21,7 +21,7 @@ httpx = "^0.28.1"
itsdangerous = "^2.2.0"
[tool.poetry.group.dev.dependencies]
-ruff = "^0.13.0"
+ruff = "^0.14.0"
pytest = "^8.3.3"
tox = "^4.23.2"
pytest-cov = "^7.0.0"
diff --git a/tests/business/__init__.py b/tests/business/__init__.py
index 01eecb7..36a48be 100644
--- a/tests/business/__init__.py
+++ b/tests/business/__init__.py
@@ -10,33 +10,33 @@ def _test_data_business() -> dict[str, Business]:
data = {
"test_business": Business(
email="test_business@example.com",
- password="$2b$12$cV19rR.8VjQwJW3s/NtSv.AYjXPN9FaK/DZXItCpylBeCRpsBVF9.", #Hashed password
+ password="$2b$12$cV19rR.8VjQwJW3s/NtSv.AYjXPN9FaK/DZXItCpylBeCRpsBVF9.", # Hashed password
name="test_business",
addr_line_1="abc",
addr_line_2="xyz",
city="aaa",
state="bbb",
- uuid="d76a11f2" #Specific UUID for testing of Product Endpoint
+ uuid="d76a11f2", # Specific UUID for testing of Product Endpoint
),
"duplicate_business": Business(
email="duplicate_business@example.com",
- password="$2b$12$4M6sS/Fk13etEBHOkb7fD.ors5LAQxyOtSC2p2ST53EnmyPFDoE0O", #Hashed password
+ password="$2b$12$4M6sS/Fk13etEBHOkb7fD.ors5LAQxyOtSC2p2ST53EnmyPFDoE0O", # Hashed password
name="duplicate_business",
addr_line_1="abc",
addr_line_2="xyz",
city="aaa",
state="bbb",
- uuid="fd4a8cac" #Specific UUID for testing of Product Endpoint
+ uuid="fd4a8cac", # Specific UUID for testing of Product Endpoint
),
"delete_business": Business(
email="delete@example.com",
- password="$2b$12$S/g71WXkT3QK1GOdIXHjc.cHudJ/m62nBga0t91xikWRh7gQ/ni3.", #Hashed password
+ password="$2b$12$S/g71WXkT3QK1GOdIXHjc.cHudJ/m62nBga0t91xikWRh7gQ/ni3.", # Hashed password
name="delete",
addr_line_1="abc",
addr_line_2="xyz",
city="aaa",
state="bbb",
- uuid="5c1c48fb" #Specific UUID for testing of Product Endpoint
- )
+ uuid="5c1c48fb", # Specific UUID for testing of Product Endpoint
+ ),
}
return data
diff --git a/tests/business/test_business_delete.py b/tests/business/test_business_delete.py
index 9e89569..6a852dc 100644
--- a/tests/business/test_business_delete.py
+++ b/tests/business/test_business_delete.py
@@ -1,23 +1,11 @@
-
import pytest
from httpx import AsyncClient
from tests.business import _test_data_business
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="BUSINESS DELETE Endpoint - Deletes currently authenticated business")
- ]
-)
-async def test_delete_business(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- _: None
-) -> None:
+@pytest.mark.parametrize("_", [pytest.param(None, id="BUSINESS DELETE Endpoint - Deletes currently authenticated business")])
+async def test_delete_business(client: AsyncClient, db_test_create: None, db_test_data: None, apply_security_override: None, _: None) -> None:
"""
Test the `delete` endpoint for deleting the currently authenticated business of the Business
API.
@@ -51,6 +39,6 @@ async def test_delete_business(
"addr_line_1": data["delete_business"].addr_line_1,
"addr_line_2": data["delete_business"].addr_line_2,
"city": data["delete_business"].city,
- "state": data["delete_business"].state
- }
+ "state": data["delete_business"].state,
+ },
}
diff --git a/tests/business/test_business_get.py b/tests/business/test_business_get.py
index 6f43ef7..cea2be2 100644
--- a/tests/business/test_business_get.py
+++ b/tests/business/test_business_get.py
@@ -1,4 +1,3 @@
-
import pytest
from httpx import AsyncClient
from pytest_mock import MockerFixture
@@ -6,19 +5,8 @@
from tests.business import _test_data_business
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="BUSINESS GET Endpoint - Fetch email of the authenticated business")
- ]
-)
-async def test_get_business_me(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- _: None
-) -> None:
+@pytest.mark.parametrize("_", [pytest.param(None, id="BUSINESS GET Endpoint - Fetch email of the authenticated business")])
+async def test_get_business_me(client: AsyncClient, db_test_create: None, db_test_data: None, apply_security_override: None, _: None) -> None:
"""
Test the `get` endpoint for the currently authenticated business of the Business API.
@@ -45,22 +33,13 @@ async def test_get_business_me(
assert response.status_code == 200
assert response.json() == {
"action": "get",
- "email": data["delete_business"].email #The override for the authentication uses this email
+ "email": data["delete_business"].email, # The override for the authentication uses this email
}
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="BUSINESS GET Endpoint - Fail authentication for business with incorrect password")
- ]
-)
+
+@pytest.mark.parametrize("_", [pytest.param(None, id="BUSINESS GET Endpoint - Fail authentication for business with incorrect password")])
async def test_get_business_me_fail_pwd(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- mocker: MockerFixture,
- _: None
+ client: AsyncClient, db_test_create: None, db_test_data: None, apply_security_override: None, mocker: MockerFixture, _: None
) -> None:
"""
Test the `get` endpoint for the incorrectly authenticated business of the Business API.
@@ -89,18 +68,9 @@ async def test_get_business_me_fail_pwd(
assert response.status_code == 401
assert response.json()["detail"] == "Not Authenticated"
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="BUSINESS GET Endpoint - Fail authentication for business with no user")
- ]
-)
-async def test_get_business_me_fail_no_user(
- client: AsyncClient,
- db_test_create: None,
- apply_security_override: None,
- _: None
-) -> None:
+
+@pytest.mark.parametrize("_", [pytest.param(None, id="BUSINESS GET Endpoint - Fail authentication for business with no user")])
+async def test_get_business_me_fail_no_user(client: AsyncClient, db_test_create: None, apply_security_override: None, _: None) -> None:
"""
Test the `get` endpoint for the incorrectly authenticated business of the Business API.
@@ -122,33 +92,26 @@ async def test_get_business_me_fail_no_user(
assert response.status_code == 401
assert response.json()["detail"] == "Not Authenticated"
+
@pytest.mark.parametrize(
"mock_oidc_user",
[
pytest.param(
- {
- "email": "dummy_user@example.com",
- "name": "dummy user",
- "sub": "dummy_user_sub"
- },
- id="BUSINESS GET Endpoint - Fetch email of the `dummy` authenticated business with oidc"
+ {"email": "dummy_user@example.com", "name": "dummy user", "sub": "dummy_user_sub"},
+ id="BUSINESS GET Endpoint - Fetch email of the `dummy` authenticated business with oidc",
),
pytest.param(
- {
- "email": "delete@example.com",
- "name": "delete user",
- "sub": "delete_sub"
- },
- id="BUSINESS GET Endpoint - Fetch email of the `delete` authenticated business with oidc"
- )
- ]
+ {"email": "delete@example.com", "name": "delete user", "sub": "delete_sub"},
+ id="BUSINESS GET Endpoint - Fetch email of the `delete` authenticated business with oidc",
+ ),
+ ],
)
async def test_get_business_me_oauth(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- mock_oidc_user: dict,
- mocker: MockerFixture,
+ client: AsyncClient,
+ db_test_create: None,
+ db_test_data: None,
+ mock_oidc_user: dict,
+ mocker: MockerFixture,
) -> None:
"""
Test the `get` endpoint for the currently authenticated business of the Business API.
@@ -180,23 +143,21 @@ async def test_get_business_me_oauth(
Test the response
"""
assert response.status_code == 200
- assert response.json() == {
- "action": "get",
- "email": f"{mock_oidc_user["email"]}"
- }
+ assert response.json() == {"action": "get", "email": f"{mock_oidc_user['email']}"}
+
@pytest.mark.parametrize(
"token",
[
pytest.param("Bearer", id="BUSINESS GET Endpoint - Invalid Bearer token length"),
- pytest.param("Dummy token", id="BUSINESS GET Endpoint - Dummy Bearer token")
- ]
+ pytest.param("Dummy token", id="BUSINESS GET Endpoint - Dummy Bearer token"),
+ ],
)
async def test_get_business_me_oauth_token_issue(
- client: AsyncClient,
- db_test_create: None,
- token: str,
- mocker: MockerFixture,
+ client: AsyncClient,
+ db_test_create: None,
+ token: str,
+ mocker: MockerFixture,
) -> None:
"""
Test the `get` endpoint for the currently authenticated business of the Business API.
@@ -224,17 +185,13 @@ async def test_get_business_me_oauth_token_issue(
assert response.status_code == 401
assert response.json()["detail"] == "Not Authenticated"
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="CUSTOMER GET Endpoint - Fail to get user info from oauth provider")
- ]
-)
+
+@pytest.mark.parametrize("_", [pytest.param(None, id="CUSTOMER GET Endpoint - Fail to get user info from oauth provider")])
async def test_get_business_me_oauth_fail(
- client: AsyncClient,
- db_test_create: None,
- mocker: MockerFixture,
- _,
+ client: AsyncClient,
+ db_test_create: None,
+ mocker: MockerFixture,
+ _,
) -> None:
"""
Test the `get` endpoint for the currently authenticated business of the Business API.
@@ -266,18 +223,9 @@ async def test_get_business_me_oauth_fail(
assert response.status_code == 401
assert response.json()["detail"] == "Not Authenticated"
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="BUSINESS GET Endpoint - Fetch all the businesses")
- ]
-)
-async def test_get_businesses(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- _: None
-) -> None:
+
+@pytest.mark.parametrize("_", [pytest.param(None, id="BUSINESS GET Endpoint - Fetch all the businesses")])
+async def test_get_businesses(client: AsyncClient, db_test_create: None, db_test_data: None, _: None) -> None:
"""
Test the `get` endpoint for fetching all the businesses of the Business API.
@@ -298,8 +246,9 @@ async def test_get_businesses(
"addr_line_1": business.addr_line_1,
"addr_line_2": business.addr_line_2,
"city": business.city,
- "state": business.state
- } for business in data.values()
+ "state": business.state,
+ }
+ for business in data.values()
]
"""
@@ -311,17 +260,10 @@ async def test_get_businesses(
Test the response
"""
assert response.status_code == 200
- assert response.json() == {
- "action": "get",
- "businesses": businesses
- }
+ assert response.json() == {"action": "get", "businesses": businesses}
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="BUSINESS GET Endpoint - Fail to fetch business")
- ]
-)
+
+@pytest.mark.parametrize("_", [pytest.param(None, id="BUSINESS GET Endpoint - Fail to fetch business")])
async def test_get_businesses_fail(
client: AsyncClient,
db_test_create: None,
diff --git a/tests/business/test_business_post.py b/tests/business/test_business_post.py
index 6fdfe8e..1933a44 100644
--- a/tests/business/test_business_post.py
+++ b/tests/business/test_business_post.py
@@ -1,4 +1,3 @@
-
import pytest
from fastapi import HTTPException, status
from httpx import AsyncClient
@@ -17,7 +16,7 @@
"addr_line_2": "xyz",
"city": "aaa",
"state": "bbb",
- "password": "test_busi"
+ "password": "test_busi",
},
"create",
id="BUSINESS Post Endpoint - 201 Created",
@@ -30,20 +29,14 @@
"addr_line_2": "xyz",
"city": "aaa",
"state": "bbb",
- "password": "duplicate_business"
+ "password": "duplicate_business",
},
"duplicate",
id="BUSINESS Post Endpoint - 409 Conflict",
- )
- ]
+ ),
+ ],
)
-async def test_create_business(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- payload: dict[str, str],
- type: str
-) -> None:
+async def test_create_business(client: AsyncClient, db_test_create: None, db_test_data: None, payload: dict[str, str], type: str) -> None:
"""
Test the `create` endpoint of the Business API.
@@ -60,7 +53,7 @@ async def test_create_business(
"""
Perform the action of visiting the endpoint
"""
- response = await client.post("/api/v1/business/create", json = payload)
+ response = await client.post("/api/v1/business/create", json=payload)
"""
Test the response
@@ -75,13 +68,14 @@ async def test_create_business(
"addr_line_1": payload["addr_line_1"],
"addr_line_2": payload["addr_line_2"],
"city": payload["city"],
- "state": payload["state"]
- }
+ "state": payload["state"],
+ },
}
else:
assert response.status_code == 409
assert response.json()["detail"] == "Uniqueness constraint failed - Please try again"
+
@pytest.mark.parametrize(
"payload",
[
@@ -93,17 +87,13 @@ async def test_create_business(
"addr_line_2": "xyz",
"city": "aaa",
"state": "bbb",
- "password": "test_busi"
+ "password": "test_busi",
},
- id="BUSINESS Post Endpoint - 500 Internal Server Error")
- ]
+ id="BUSINESS Post Endpoint - 500 Internal Server Error",
+ )
+ ],
)
-async def test_create_business_fail(
- client: AsyncClient,
- get_test_database_url: URL,
- mocker: MockerFixture,
- payload: dict[str, str]
-) -> None:
+async def test_create_business_fail(client: AsyncClient, get_test_database_url: URL, mocker: MockerFixture, payload: dict[str, str]) -> None:
"""
Test the `create` endpoint of the Business API for HTTP_500_INTERNAL_SERVER_ERROR.
@@ -120,10 +110,7 @@ async def test_create_business_fail(
Create a mock database session and configure the `flush` method to raise an HTTPException.
"""
mock_db = mocker.AsyncMock()
- mock_db.flush.side_effect = HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- )
+ mock_db.flush.side_effect = HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.")
mocker.patch("fastapi_ecom.router.business.get_db", return_value=mock_db)
"""
diff --git a/tests/business/test_business_put.py b/tests/business/test_business_put.py
index 71ae2a6..80bd25d 100644
--- a/tests/business/test_business_put.py
+++ b/tests/business/test_business_put.py
@@ -1,4 +1,3 @@
-
import pytest
from httpx import AsyncClient
@@ -14,7 +13,7 @@
"addr_line_2": "xyz",
"city": "aaa",
"state": "bbb",
- "password": "update_business"
+ "password": "update_business",
},
"update",
id="BUSINESS PUT Endpoint - Updates currently authenticated business",
@@ -27,20 +26,15 @@
"addr_line_2": "xyz",
"city": "aaa",
"state": "bbb",
- "password": "duplicate_business"
+ "password": "duplicate_business",
},
"duplicate",
id="BUSINESS PUT Endpoint - 409 Conflict",
- )
- ]
+ ),
+ ],
)
async def test_update_business(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- payload: dict[str, str],
- type: str
+ client: AsyncClient, db_test_create: None, db_test_data: None, apply_security_override: None, payload: dict[str, str], type: str
) -> None:
"""
Test the `put` endpoint for updating the currently authenticated business of the Business API.
@@ -57,7 +51,7 @@ async def test_update_business(
"""
Perform the action of visiting the endpoint
"""
- response = await client.put("/api/v1/business/update/me", json = payload)
+ response = await client.put("/api/v1/business/update/me", json=payload)
"""
Test the response
@@ -72,8 +66,8 @@ async def test_update_business(
"addr_line_1": payload["addr_line_1"],
"addr_line_2": payload["addr_line_2"],
"city": payload["city"],
- "state": payload["state"]
- }
+ "state": payload["state"],
+ },
}
else:
assert response.status_code == 409
diff --git a/tests/conftest.py b/tests/conftest.py
index 1f3b7b9..ca2f412 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,6 +1,5 @@
-from collections.abc import AsyncGenerator
+from collections.abc import AsyncGenerator, Callable
from pathlib import PosixPath
-from typing import Callable
import pytest
from click.testing import CliRunner
@@ -29,6 +28,7 @@ def runner() -> CliRunner:
"""
return CliRunner()
+
@pytest.fixture
async def test_app() -> FastAPI:
"""
@@ -38,6 +38,7 @@ async def test_app() -> FastAPI:
"""
return app
+
@pytest.fixture
async def client(test_app: FastAPI) -> AsyncGenerator[AsyncClient, None]:
"""
@@ -51,6 +52,7 @@ async def client(test_app: FastAPI) -> AsyncGenerator[AsyncClient, None]:
async with AsyncClient(transport=transport, base_url="http://test") as client:
yield client
+
@pytest.fixture
async def get_test_database_url(tmp_path: PosixPath, mocker: MockerFixture) -> URL:
"""
@@ -69,6 +71,7 @@ async def get_test_database_url(tmp_path: PosixPath, mocker: MockerFixture) -> U
cnfg.confecho = False
return SQLALCHEMY_DATABASE_URL
+
@pytest.fixture
async def db_test_create(get_test_database_url: URL) -> None:
"""
@@ -84,6 +87,7 @@ async def db_test_create(get_test_database_url: URL) -> None:
await conn.run_sync(baseobjc.metadata.drop_all) # Ensure no old tables persist
await conn.run_sync(baseobjc.metadata.create_all)
+
@pytest.fixture
async def db_test_data(get_test_database_url: URL) -> None:
"""
@@ -108,6 +112,7 @@ async def db_test_data(get_test_database_url: URL) -> None:
await db.commit()
+
@pytest.fixture
async def override_security(mocker: MockerFixture) -> Callable[[], HTTPBasicCredentials]:
"""
@@ -121,6 +126,7 @@ async def override_security(mocker: MockerFixture) -> Callable[[], HTTPBasicCred
mocker.patch("fastapi.security.http.HTTPBasic.__call__", return_value=mock_credentials)
return lambda: mock_credentials
+
@pytest.fixture
async def apply_security_override(test_app, override_security) -> None:
"""
diff --git a/tests/customer/__init__.py b/tests/customer/__init__.py
index 35dd1b1..c7571b8 100644
--- a/tests/customer/__init__.py
+++ b/tests/customer/__init__.py
@@ -10,33 +10,33 @@ def _test_data_customer() -> dict[str, Customer]:
data = {
"test_customer": Customer(
email="test_customer@example.com",
- password="$2b$12$cP3LsL.tlDCRczXcOPjoie74MmDAxgB0lnYUQXF98lkFVoM49gHzW", #Hashed password
+ password="$2b$12$cP3LsL.tlDCRczXcOPjoie74MmDAxgB0lnYUQXF98lkFVoM49gHzW", # Hashed password
name="test_customer",
addr_line_1="abc",
addr_line_2="xyz",
city="aaa",
state="bbb",
- uuid="2c92f0e8" #Specific UUID for testing of Order Endpoint
+ uuid="2c92f0e8", # Specific UUID for testing of Order Endpoint
),
"duplicate_customer": Customer(
email="duplicate_customer@example.com",
- password="$2b$12$Gzw8nGR9x/CxcC0jxNtqAu58NbrtcjBRO9MDH4WMerAeRDkl8vKBC", #Hashed password
+ password="$2b$12$Gzw8nGR9x/CxcC0jxNtqAu58NbrtcjBRO9MDH4WMerAeRDkl8vKBC", # Hashed password
name="duplicate_customer",
addr_line_1="abc",
addr_line_2="xyz",
city="aaa",
state="bbb",
- uuid="b73a7a28" #Specific UUID for testing of Order Endpoint
+ uuid="b73a7a28", # Specific UUID for testing of Order Endpoint
),
"delete_customer": Customer(
email="delete@example.com",
- password="$2b$12$S/g71WXkT3QK1GOdIXHjc.cHudJ/m62nBga0t91xikWRh7gQ/ni3.", #Hashed password
+ password="$2b$12$S/g71WXkT3QK1GOdIXHjc.cHudJ/m62nBga0t91xikWRh7gQ/ni3.", # Hashed password
name="delete",
addr_line_1="abc",
addr_line_2="xyz",
city="aaa",
state="bbb",
- uuid="2b203687" #Specific UUID for testing of Order Endpoint
- )
+ uuid="2b203687", # Specific UUID for testing of Order Endpoint
+ ),
}
return data
diff --git a/tests/customer/test_customer_delete.py b/tests/customer/test_customer_delete.py
index 740b115..1e47882 100644
--- a/tests/customer/test_customer_delete.py
+++ b/tests/customer/test_customer_delete.py
@@ -1,23 +1,11 @@
-
import pytest
from httpx import AsyncClient
from tests.customer import _test_data_customer
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="CUSTOMER DELETE Endpoint - Deletes currently authenticated customer")
- ]
-)
-async def test_delete_customer(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- _: None
-) -> None:
+@pytest.mark.parametrize("_", [pytest.param(None, id="CUSTOMER DELETE Endpoint - Deletes currently authenticated customer")])
+async def test_delete_customer(client: AsyncClient, db_test_create: None, db_test_data: None, apply_security_override: None, _: None) -> None:
"""
Test the `delete` endpoint for deleting the currently authenticated customer of the Customer
API.
@@ -51,6 +39,6 @@ async def test_delete_customer(
"addr_line_1": data["delete_customer"].addr_line_1,
"addr_line_2": data["delete_customer"].addr_line_2,
"city": data["delete_customer"].city,
- "state": data["delete_customer"].state
- }
+ "state": data["delete_customer"].state,
+ },
}
diff --git a/tests/customer/test_customer_get.py b/tests/customer/test_customer_get.py
index 0bd83e6..7a6d0e4 100644
--- a/tests/customer/test_customer_get.py
+++ b/tests/customer/test_customer_get.py
@@ -1,4 +1,3 @@
-
import pytest
from httpx import AsyncClient
from pytest_mock import MockerFixture
@@ -6,19 +5,8 @@
from tests.customer import _test_data_customer
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="CUSTOMER GET Endpoint - Fetch email of the authenticated customer")
- ]
-)
-async def test_get_customer_me(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- _: None
-) -> None:
+@pytest.mark.parametrize("_", [pytest.param(None, id="CUSTOMER GET Endpoint - Fetch email of the authenticated customer")])
+async def test_get_customer_me(client: AsyncClient, db_test_create: None, db_test_data: None, apply_security_override: None, _: None) -> None:
"""
Test the `get` endpoint for the currently authenticated customer of the Customer API.
@@ -45,22 +33,13 @@ async def test_get_customer_me(
assert response.status_code == 200
assert response.json() == {
"action": "get",
- "email": data["delete_customer"].email #The override for the authentication uses this email
+ "email": data["delete_customer"].email, # The override for the authentication uses this email
}
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="CUSTOMER GET Endpoint - Fail authentication for customer")
- ]
-)
+
+@pytest.mark.parametrize("_", [pytest.param(None, id="CUSTOMER GET Endpoint - Fail authentication for customer")])
async def test_get_customer_me_fail_pwd(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- mocker: MockerFixture,
- _: None
+ client: AsyncClient, db_test_create: None, db_test_data: None, apply_security_override: None, mocker: MockerFixture, _: None
) -> None:
"""
Test the `get` endpoint for the incorrectly authenticated customer of the Customer API.
@@ -89,18 +68,9 @@ async def test_get_customer_me_fail_pwd(
assert response.status_code == 401
assert response.json()["detail"] == "Not Authenticated"
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="CUSTOMER GET Endpoint - Fail authentication for customer with no user")
- ]
-)
-async def test_get_customer_me_fail_no_user(
- client: AsyncClient,
- db_test_create: None,
- apply_security_override: None,
- _: None
-) -> None:
+
+@pytest.mark.parametrize("_", [pytest.param(None, id="CUSTOMER GET Endpoint - Fail authentication for customer with no user")])
+async def test_get_customer_me_fail_no_user(client: AsyncClient, db_test_create: None, apply_security_override: None, _: None) -> None:
"""
Test the `get` endpoint for the incorrectly authenticated customer of the Customer API.
@@ -122,33 +92,26 @@ async def test_get_customer_me_fail_no_user(
assert response.status_code == 401
assert response.json()["detail"] == "Not Authenticated"
+
@pytest.mark.parametrize(
"mock_oidc_user",
[
pytest.param(
- {
- "email": "dummy_user@example.com",
- "name": "dummy user",
- "sub": "dummy_user_sub"
- },
- id="CUSTOMER GET Endpoint - Fetch email of the `dummy` authenticated customer with oidc"
+ {"email": "dummy_user@example.com", "name": "dummy user", "sub": "dummy_user_sub"},
+ id="CUSTOMER GET Endpoint - Fetch email of the `dummy` authenticated customer with oidc",
),
pytest.param(
- {
- "email": "delete@example.com",
- "name": "delete user",
- "sub": "delete_sub"
- },
- id="CUSTOMER GET Endpoint - Fetch email of the `delete` authenticated customer with oidc"
- )
- ]
+ {"email": "delete@example.com", "name": "delete user", "sub": "delete_sub"},
+ id="CUSTOMER GET Endpoint - Fetch email of the `delete` authenticated customer with oidc",
+ ),
+ ],
)
async def test_get_customer_me_oauth(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- mock_oidc_user,
- mocker: MockerFixture,
+ client: AsyncClient,
+ db_test_create: None,
+ db_test_data: None,
+ mock_oidc_user,
+ mocker: MockerFixture,
) -> None:
"""
Test the `get` endpoint for the currently authenticated customer of the Business API.
@@ -180,23 +143,21 @@ async def test_get_customer_me_oauth(
Test the response
"""
assert response.status_code == 200
- assert response.json() == {
- "action": "get",
- "email": f"{mock_oidc_user["email"]}"
- }
+ assert response.json() == {"action": "get", "email": f"{mock_oidc_user['email']}"}
+
@pytest.mark.parametrize(
"token",
[
pytest.param("Bearer", id="CUSTOMER GET Endpoint - Invalid Bearer token length"),
- pytest.param("Dummy token", id="CUSTOMER GET Endpoint - Dummy Bearer token")
- ]
+ pytest.param("Dummy token", id="CUSTOMER GET Endpoint - Dummy Bearer token"),
+ ],
)
async def test_get_customer_me_oauth_token_issue(
- client: AsyncClient,
- db_test_create: None,
- token: str,
- mocker: MockerFixture,
+ client: AsyncClient,
+ db_test_create: None,
+ token: str,
+ mocker: MockerFixture,
) -> None:
"""
Test the `get` endpoint for the currently authenticated customer of the Business API.
@@ -224,17 +185,13 @@ async def test_get_customer_me_oauth_token_issue(
assert response.status_code == 401
assert response.json()["detail"] == "Not Authenticated"
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="CUSTOMER GET Endpoint - Fail to get user info from oauth provider")
- ]
-)
+
+@pytest.mark.parametrize("_", [pytest.param(None, id="CUSTOMER GET Endpoint - Fail to get user info from oauth provider")])
async def test_get_customer_me_oauth_fail(
- client: AsyncClient,
- db_test_create: None,
- mocker: MockerFixture,
- _,
+ client: AsyncClient,
+ db_test_create: None,
+ mocker: MockerFixture,
+ _,
) -> None:
"""
Test the `get` endpoint for the currently authenticated customer of the Business API.
@@ -266,17 +223,9 @@ async def test_get_customer_me_oauth_fail(
assert response.status_code == 401
assert response.json()["detail"] == "Not Authenticated"
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="CUSTOMER GET Endpoint - Fetch all the customers")
- ]
-)
-async def test_get_customers(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- _: None) -> None:
+
+@pytest.mark.parametrize("_", [pytest.param(None, id="CUSTOMER GET Endpoint - Fetch all the customers")])
+async def test_get_customers(client: AsyncClient, db_test_create: None, db_test_data: None, _: None) -> None:
"""
Test the `get` endpoint for fetching all the customers of the Customer API.
@@ -297,8 +246,9 @@ async def test_get_customers(
"addr_line_1": customer.addr_line_1,
"addr_line_2": customer.addr_line_2,
"city": customer.city,
- "state": customer.state
- } for customer in data.values()
+ "state": customer.state,
+ }
+ for customer in data.values()
]
"""
@@ -310,17 +260,10 @@ async def test_get_customers(
Test the response
"""
assert response.status_code == 200
- assert response.json() == {
- "action": "get",
- "customers": customers
- }
+ assert response.json() == {"action": "get", "customers": customers}
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="CUSTOMER GET Endpoint - Fail to fetch customer")
- ]
-)
+
+@pytest.mark.parametrize("_", [pytest.param(None, id="CUSTOMER GET Endpoint - Fail to fetch customer")])
async def test_get_customers_fail(
client: AsyncClient,
db_test_create: None,
diff --git a/tests/customer/test_customer_post.py b/tests/customer/test_customer_post.py
index c753b52..869c0f9 100644
--- a/tests/customer/test_customer_post.py
+++ b/tests/customer/test_customer_post.py
@@ -1,4 +1,3 @@
-
import pytest
from fastapi import HTTPException, status
from httpx import AsyncClient
@@ -17,7 +16,7 @@
"addr_line_2": "xyz",
"city": "aaa",
"state": "bbb",
- "password": "test_cust"
+ "password": "test_cust",
},
"create",
id="CUSTOMER Post Endpoint - 201 Created",
@@ -30,19 +29,14 @@
"addr_line_2": "xyz",
"city": "aaa",
"state": "bbb",
- "password": "duplicate_customer"
+ "password": "duplicate_customer",
},
"duplicate",
id="CUSTOMER Post Endpoint - 409 Conflict",
- )
- ]
+ ),
+ ],
)
-async def test_create_customer(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- payload: dict[str, str],
- type: str) -> None:
+async def test_create_customer(client: AsyncClient, db_test_create: None, db_test_data: None, payload: dict[str, str], type: str) -> None:
"""
Test the `create` endpoint of the Customer API.
@@ -59,7 +53,7 @@ async def test_create_customer(
"""
Perform the action of visiting the endpoint
"""
- response = await client.post("/api/v1/customer/create", json = payload)
+ response = await client.post("/api/v1/customer/create", json=payload)
"""
Test the response
@@ -74,13 +68,14 @@ async def test_create_customer(
"addr_line_1": payload["addr_line_1"],
"addr_line_2": payload["addr_line_2"],
"city": payload["city"],
- "state": payload["state"]
- }
+ "state": payload["state"],
+ },
}
else:
assert response.status_code == 409
assert response.json()["detail"] == "Uniqueness constraint failed - Please try again"
+
@pytest.mark.parametrize(
"payload",
[
@@ -92,17 +87,13 @@ async def test_create_customer(
"addr_line_2": "xyz",
"city": "aaa",
"state": "bbb",
- "password": "test_cust"
+ "password": "test_cust",
},
- id="CUSTOMER Post Endpoint - 500 Internal Server Error")
- ]
+ id="CUSTOMER Post Endpoint - 500 Internal Server Error",
+ )
+ ],
)
-async def test_create_customer_fail(
- client: AsyncClient,
- get_test_database_url: URL,
- mocker: MockerFixture,
- payload: dict[str, str]
-) -> None:
+async def test_create_customer_fail(client: AsyncClient, get_test_database_url: URL, mocker: MockerFixture, payload: dict[str, str]) -> None:
"""
Test the `create` endpoint of the Customer API for HTTP_500_INTERNAL_SERVER_ERROR.
@@ -119,10 +110,7 @@ async def test_create_customer_fail(
Create a mock database session and configure the `flush` method to raise an HTTPException.
"""
mock_db = mocker.AsyncMock()
- mock_db.flush.side_effect = HTTPException(
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
- detail="An unexpected database error occurred."
- )
+ mock_db.flush.side_effect = HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An unexpected database error occurred.")
mocker.patch("fastapi_ecom.router.customer.get_db", return_value=mock_db)
"""
diff --git a/tests/customer/test_customer_put.py b/tests/customer/test_customer_put.py
index b04b300..7b71975 100644
--- a/tests/customer/test_customer_put.py
+++ b/tests/customer/test_customer_put.py
@@ -1,4 +1,3 @@
-
import pytest
from httpx import AsyncClient
@@ -14,7 +13,7 @@
"addr_line_2": "xyz",
"city": "aaa",
"state": "bbb",
- "password": "update_customer"
+ "password": "update_customer",
},
"update",
id="CUSTOMER PUT Endpoint - Updates currently authenticated customer",
@@ -27,20 +26,15 @@
"addr_line_2": "xyz",
"city": "aaa",
"state": "bbb",
- "password": "duplicate_customer"
+ "password": "duplicate_customer",
},
"duplicate",
id="CUSTOMER PUT Endpoint - 409 Conflict",
- )
- ]
+ ),
+ ],
)
async def test_update_customer(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- payload: dict[str, str],
- type: str
+ client: AsyncClient, db_test_create: None, db_test_data: None, apply_security_override: None, payload: dict[str, str], type: str
) -> None:
"""
Test the `put` endpoint for updating the currently authenticated customer of the Customer API.
@@ -57,7 +51,7 @@ async def test_update_customer(
"""
Perform the action of visiting the endpoint
"""
- response = await client.put("/api/v1/customer/update/me", json = payload)
+ response = await client.put("/api/v1/customer/update/me", json=payload)
"""
Test the response
@@ -72,8 +66,8 @@ async def test_update_customer(
"addr_line_1": payload["addr_line_1"],
"addr_line_2": payload["addr_line_2"],
"city": payload["city"],
- "state": payload["state"]
- }
+ "state": payload["state"],
+ },
}
else:
assert response.status_code == 409
diff --git a/tests/main/test_comd_create_migration.py b/tests/main/test_comd_create_migration.py
index e20a8b6..fa2dc14 100644
--- a/tests/main/test_comd_create_migration.py
+++ b/tests/main/test_comd_create_migration.py
@@ -10,18 +10,10 @@
@pytest.mark.parametrize(
"cmd, autogen, comment, code",
- [
- pytest.param("create-migration", "--autogenerate", "TEST", 0, id="MAIN Function - CREATE-MIGRATION - Create a new revision for the database")
- ]
+ [pytest.param("create-migration", "--autogenerate", "TEST", 0, id="MAIN Function - CREATE-MIGRATION - Create a new revision for the database")],
)
def test_comd_create_migration(
- runner: CliRunner,
- get_test_database_url: URL,
- mocker: MockerFixture,
- cmd: str,
- autogen: str,
- comment: str,
- code: int
+ runner: CliRunner, get_test_database_url: URL, mocker: MockerFixture, cmd: str, autogen: str, comment: str, code: int
) -> None:
"""
Test the functionality cli `create-migration` command.
diff --git a/tests/main/test_comd_db_version.py b/tests/main/test_comd_db_version.py
index 0f7028e..163ddc0 100644
--- a/tests/main/test_comd_db_version.py
+++ b/tests/main/test_comd_db_version.py
@@ -1,4 +1,3 @@
-
import pytest
from alembic import command, config
from click.testing import CliRunner
@@ -9,18 +8,9 @@
@pytest.mark.parametrize(
- "cmd, code",
- [
- pytest.param("db-version", 0, id="MAIN Function - DB-VERSION - Check the current revision of the database schema")
- ]
+ "cmd, code", [pytest.param("db-version", 0, id="MAIN Function - DB-VERSION - Check the current revision of the database schema")]
)
-def test_comd_db_version(
- runner: CliRunner,
- db_test_create: None,
- get_test_database_url: URL,
- cmd: str,
- code: int
-) -> None:
+def test_comd_db_version(runner: CliRunner, db_test_create: None, get_test_database_url: URL, cmd: str, code: int) -> None:
"""
Test the functionality cli `db-version` command.
diff --git a/tests/main/test_comd_downgrade_db.py b/tests/main/test_comd_downgrade_db.py
index 134a19f..8618fe6 100644
--- a/tests/main/test_comd_downgrade_db.py
+++ b/tests/main/test_comd_downgrade_db.py
@@ -1,4 +1,3 @@
-
import pytest
from alembic import command, config
from click.testing import CliRunner
@@ -12,16 +11,16 @@
"cmd, revision, code",
[
pytest.param("downgrade-db", "base", 0, id="MAIN Function - DOWNGRADE-DB - Downgrade the database to the base revision"),
- pytest.param("downgrade-db", "306d801996a6", 0, id="MAIN Function - DOWNGRADE-DB - Downgrade the database to the same revision")
- ]
+ pytest.param("downgrade-db", "306d801996a6", 0, id="MAIN Function - DOWNGRADE-DB - Downgrade the database to the same revision"),
+ ],
)
def test_comd_downgrade_db(
- runner: CliRunner,
- db_test_create: None,
- get_test_database_url: URL,
- cmd: str,
- revision: str,
- code: int,
+ runner: CliRunner,
+ db_test_create: None,
+ get_test_database_url: URL,
+ cmd: str,
+ revision: str,
+ code: int,
) -> None:
"""
Test the functionality cli `downgrade-db` command.
diff --git a/tests/main/test_comd_setup.py b/tests/main/test_comd_setup.py
index 17615a1..73e8a2b 100644
--- a/tests/main/test_comd_setup.py
+++ b/tests/main/test_comd_setup.py
@@ -8,20 +8,8 @@
from fastapi_ecom.main import main
-@pytest.mark.parametrize(
- "cmd, code",
- [
- pytest.param("setup", 0, id="MAIN Function - SETUP - Setup the database")
- ]
-)
-def test_comd_setup(
- runner: CliRunner,
- get_test_database_url: URL,
- tmp_path: PosixPath,
- mocker: MockerFixture,
- cmd: str,
- code: int
-) -> None:
+@pytest.mark.parametrize("cmd, code", [pytest.param("setup", 0, id="MAIN Function - SETUP - Setup the database")])
+def test_comd_setup(runner: CliRunner, get_test_database_url: URL, tmp_path: PosixPath, mocker: MockerFixture, cmd: str, code: int) -> None:
"""
Test the functionality cli `setup` command.
diff --git a/tests/main/test_comd_start.py b/tests/main/test_comd_start.py
index 404a61d..20848d6 100644
--- a/tests/main/test_comd_start.py
+++ b/tests/main/test_comd_start.py
@@ -6,18 +6,8 @@
from fastapi_ecom.main import main
-@pytest.mark.parametrize(
- "cmd, code",
- [
- pytest.param("start", 0, id="MAIN Function - START - Start FastAPI server")
- ]
-)
-def test_comd_start(
- runner: CliRunner,
- mocker: MockerFixture,
- cmd: str,
- code: int
-) -> None:
+@pytest.mark.parametrize("cmd, code", [pytest.param("start", 0, id="MAIN Function - START - Start FastAPI server")])
+def test_comd_start(runner: CliRunner, mocker: MockerFixture, cmd: str, code: int) -> None:
"""
Test the functionality cli `start` command.
@@ -44,9 +34,4 @@ def test_comd_start(
assert result.exit_code == code
# Check that `uvicorn.run` was called with the correct arguments
- mock_run.assert_called_once_with(
- "fastapi_ecom.app:app",
- host=config.servhost,
- port=config.servport,
- reload=config.cgreload
- )
+ mock_run.assert_called_once_with("fastapi_ecom.app:app", host=config.servhost, port=config.servport, reload=config.cgreload)
diff --git a/tests/main/test_comd_upgrade.py b/tests/main/test_comd_upgrade.py
index 16f0971..878f495 100644
--- a/tests/main/test_comd_upgrade.py
+++ b/tests/main/test_comd_upgrade.py
@@ -1,4 +1,3 @@
-
import pytest
from alembic import command, config
from click.testing import CliRunner
@@ -10,18 +9,9 @@
@pytest.mark.parametrize(
"cmd, revision, code",
- [
- pytest.param("upgrade-db", "306d801996a6", 0, id="MAIN Function - UPGRADE-DB - Upgrade the database to the certain revision")
- ]
+ [pytest.param("upgrade-db", "306d801996a6", 0, id="MAIN Function - UPGRADE-DB - Upgrade the database to the certain revision")],
)
-def test_comd_upgrade_db(
- runner: CliRunner,
- db_test_create: None,
- get_test_database_url: URL,
- cmd: str,
- revision: str,
- code: int
-) -> None:
+def test_comd_upgrade_db(runner: CliRunner, db_test_create: None, get_test_database_url: URL, cmd: str, revision: str, code: int) -> None:
"""
Test the functionality cli `upgrade-db` command.
diff --git a/tests/main/test_main_help.py b/tests/main/test_main_help.py
index be48acf..8cae8ef 100644
--- a/tests/main/test_main_help.py
+++ b/tests/main/test_main_help.py
@@ -1,4 +1,3 @@
-
import pytest
from click.testing import CliRunner
@@ -22,31 +21,21 @@
"downgrade-db Downgrade the database to a specific version",
"setup Setup the database schema",
"start Start the FastAPI eComm application",
- "upgrade-db Upgrade the database to a specific version"
+ "upgrade-db Upgrade the database to a specific version",
],
- id="MAIN Function - Basic Help"
+ id="MAIN Function - Basic Help",
),
pytest.param(
"start --help",
0,
- [
- "Usage: fastapi_ecom start [OPTIONS]",
- "Start the FastAPI eComm application",
- "Options:",
- "--help Show this message and exit."
- ],
- id="MAIN Function - START - Basic Help"
+ ["Usage: fastapi_ecom start [OPTIONS]", "Start the FastAPI eComm application", "Options:", "--help Show this message and exit."],
+ id="MAIN Function - START - Basic Help",
),
pytest.param(
"setup --help",
0,
- [
- "Usage: fastapi_ecom setup [OPTIONS]",
- "Setup the database schema",
- "Options:",
- "--help Show this message and exit."
- ],
- id="MAIN Function - SETUP - Basic Help"
+ ["Usage: fastapi_ecom setup [OPTIONS]", "Setup the database schema", "Options:", "--help Show this message and exit."],
+ id="MAIN Function - SETUP - Basic Help",
),
pytest.param(
"create-migration --help",
@@ -56,20 +45,15 @@
"Create a new migration script",
"Options:",
"--autogenerate Automatically generate the migration",
- "--help Show this message and exit."
+ "--help Show this message and exit.",
],
- id="MAIN Function - CREATE-MIGRATION - Basic Help"
+ id="MAIN Function - CREATE-MIGRATION - Basic Help",
),
pytest.param(
"db-version --help",
0,
- [
- "Usage: fastapi_ecom db-version [OPTIONS]",
- "Show the current database version",
- "Options:",
- "--help Show this message and exit."
- ],
- id="MAIN Function - DB-VERSION - Basic Help"
+ ["Usage: fastapi_ecom db-version [OPTIONS]", "Show the current database version", "Options:", "--help Show this message and exit."],
+ id="MAIN Function - DB-VERSION - Basic Help",
),
pytest.param(
"upgrade-db --help",
@@ -78,9 +62,9 @@
"Usage: fastapi_ecom upgrade-db [OPTIONS] [VERSION]",
"Upgrade the database to a specific version",
"Options:",
- "--help Show this message and exit."
+ "--help Show this message and exit.",
],
- id="MAIN Function - UPGRADE-DB - Basic Help"
+ id="MAIN Function - UPGRADE-DB - Basic Help",
),
pytest.param(
"downgrade-db --help",
@@ -89,18 +73,13 @@
"Usage: fastapi_ecom downgrade-db [OPTIONS] VERSION",
"Downgrade the database to a specific version",
"Options:",
- "--help Show this message and exit."
+ "--help Show this message and exit.",
],
- id="MAIN Function - DOWNGRAD-DB - Basic Help"
- )
- ]
+ id="MAIN Function - DOWNGRAD-DB - Basic Help",
+ ),
+ ],
)
-def test_main_help(
- runner: CliRunner,
- cmd: str,
- code: int,
- output: list[str]
-) -> None:
+def test_main_help(runner: CliRunner, cmd: str, code: int, output: list[str]) -> None:
"""
Test the basic cli help functionality.
diff --git a/tests/order/__init__.py b/tests/order/__init__.py
index e665eb7..f81f921 100644
--- a/tests/order/__init__.py
+++ b/tests/order/__init__.py
@@ -12,20 +12,21 @@ def _test_data_orders() -> dict[str, Order]:
"""
data = {
"test_order1": Order(
- uuid="feb2f4fa", #Specific UUID for testing
- user_id="2c92f0e8", #UUID from customer test data
+ uuid="feb2f4fa", # Specific UUID for testing
+ user_id="2c92f0e8", # UUID from customer test data
order_date=datetime.now(timezone.utc),
- total_price=590.0
+ total_price=590.0,
),
"test_order2": Order(
- uuid="375339b1", #Specific UUID for testing
- user_id="2b203687", #UUID from customer test data
+ uuid="375339b1", # Specific UUID for testing
+ user_id="2b203687", # UUID from customer test data
order_date=datetime.now(timezone.utc),
- total_price=700.0
- )
+ total_price=700.0,
+ ),
}
return data
+
def _test_data_order_details() -> dict[str, OrderDetail]:
"""
Provides test data for testing the order endpoint.
@@ -34,25 +35,25 @@ def _test_data_order_details() -> dict[str, OrderDetail]:
"""
data = {
"test_order_detail_1a": OrderDetail(
- uuid="8e9a7e8f", #Specific UUID for testing
- product_id="3250fcbe", #UUID from product test data
+ uuid="8e9a7e8f", # Specific UUID for testing
+ product_id="3250fcbe", # UUID from product test data
quantity=5,
price=100.0,
- order_id="feb2f4fa" #UUID from order test data
+ order_id="feb2f4fa", # UUID from order test data
),
"test_order_detail_1b": OrderDetail(
- uuid="4e88e56f", #Specific UUID for testing
- product_id="2e7a5e2d", #UUID from product test data
+ uuid="4e88e56f", # Specific UUID for testing
+ product_id="2e7a5e2d", # UUID from product test data
quantity=9,
price=10.0,
- order_id="feb2f4fa" #UUID from order test data
+ order_id="feb2f4fa", # UUID from order test data
),
"test_order_detail_2a": OrderDetail(
- uuid="9ecec3de", #Specific UUID for testing
- product_id="2e7a5e2d", #UUID from product test data
+ uuid="9ecec3de", # Specific UUID for testing
+ product_id="2e7a5e2d", # UUID from product test data
quantity=10,
price=70.0,
- order_id="375339b1" #UUID from order test data
- )
+ order_id="375339b1", # UUID from order test data
+ ),
}
return data
diff --git a/tests/order/test_order_delete.py b/tests/order/test_order_delete.py
index 9eb38ff..4d7187b 100644
--- a/tests/order/test_order_delete.py
+++ b/tests/order/test_order_delete.py
@@ -1,4 +1,3 @@
-
import pytest
from httpx import AsyncClient
@@ -9,16 +8,11 @@
"order_id, present",
[
pytest.param("375339b1", True, id="ORDER DELETE Endpoint - Delete a specified order of the authenticated customer"),
- pytest.param("xxxx1111", False, id="ORDER DELETE Endpoint - Fail to delete a specified order of the authenticated customer")
- ]
+ pytest.param("xxxx1111", False, id="ORDER DELETE Endpoint - Fail to delete a specified order of the authenticated customer"),
+ ],
)
async def test_delete_order(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- order_id: str,
- present: str
+ client: AsyncClient, db_test_create: None, db_test_data: None, apply_security_override: None, order_id: str, present: str
) -> None:
"""
Test the `delete` endpoint for deleting a specified order of the Order API.
@@ -40,11 +34,8 @@ async def test_delete_order(
for ord in ords.values():
if ord.uuid == order_id:
order_items = [
- {
- "product_id": detail.product_id,
- "quantity": detail.quantity,
- "price": detail.price
- } for detail in order_details.values()
+ {"product_id": detail.product_id, "quantity": detail.quantity, "price": detail.price}
+ for detail in order_details.values()
if detail.order_id == ord.uuid
]
total_price = sum(item["quantity"] * item["price"] for item in order_items)
@@ -52,7 +43,7 @@ async def test_delete_order(
"uuid": ord.uuid,
"order_date": ord.order_date.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"total_price": total_price,
- "order_items": order_items
+ "order_items": order_items,
}
"""
@@ -65,10 +56,7 @@ async def test_delete_order(
"""
if present:
assert response.status_code == 202
- assert response.json() == {
- "action": "delete",
- "order": order
- }
+ assert response.json() == {"action": "delete", "order": order}
else:
assert response.status_code == 404
assert response.json()["detail"] == "Order not present in database"
diff --git a/tests/order/test_order_get.py b/tests/order/test_order_get.py
index f8960c3..8e52958 100644
--- a/tests/order/test_order_get.py
+++ b/tests/order/test_order_get.py
@@ -1,4 +1,3 @@
-
import pytest
from fastapi import FastAPI
from fastapi.security import HTTPBasicCredentials
@@ -13,18 +12,18 @@
"order_id, present",
[
pytest.param("2b203687", True, id="ORDER GET Endpoint - Fetch all the orders of authenticated order"),
- pytest.param("b73a7a28", False, id="ORDER GET Endpoint - Fail to fetch orders of authenticated order")
- ]
+ pytest.param("b73a7a28", False, id="ORDER GET Endpoint - Fail to fetch orders of authenticated order"),
+ ],
)
async def test_get_orders(
- test_app: FastAPI,
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- mocker: MockerFixture,
- order_id: str,
- present: str
+ test_app: FastAPI,
+ client: AsyncClient,
+ db_test_create: None,
+ db_test_data: None,
+ apply_security_override: None,
+ mocker: MockerFixture,
+ order_id: str,
+ present: str,
) -> None:
"""
Test the `get` endpoint for fetching all the order of the authenticated order of the Order
@@ -48,13 +47,10 @@ async def test_get_orders(
order_details = _test_data_order_details()
orders = []
for ord in ords.values():
- if ord.user_id == order_id: #UUID of the default order account which is used for testing
+ if ord.user_id == order_id: # UUID of the default order account which is used for testing
order_items = [
- {
- "product_id": detail.product_id,
- "quantity": detail.quantity,
- "price": detail.price
- } for detail in order_details.values()
+ {"product_id": detail.product_id, "quantity": detail.quantity, "price": detail.price}
+ for detail in order_details.values()
if detail.order_id == ord.uuid
]
total_price = sum(item["quantity"] * item["price"] for item in order_items)
@@ -62,7 +58,7 @@ async def test_get_orders(
"uuid": ord.uuid,
"order_date": ord.order_date.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"total_price": total_price,
- "order_items": order_items
+ "order_items": order_items,
}
orders.append(order_view_data)
@@ -84,26 +80,14 @@ async def test_get_orders(
"""
if present:
assert response.status_code == 200
- assert response.json() == {
- "action": "get",
- "orders": orders
- }
+ assert response.json() == {"action": "get", "orders": orders}
else:
assert response.status_code == 404
assert response.json()["detail"] == "No orders in database"
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="ORDER GET Endpoint - Fetch all the orders")
- ]
-)
-async def test_get_orders_internal(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- _: None
-) -> None:
+
+@pytest.mark.parametrize("_", [pytest.param(None, id="ORDER GET Endpoint - Fetch all the orders")])
+async def test_get_orders_internal(client: AsyncClient, db_test_create: None, db_test_data: None, _: None) -> None:
"""
Test the `get` endpoint for fetching all the orders of the Order API.
@@ -121,12 +105,8 @@ async def test_get_orders_internal(
orders = []
for ord in ords.values():
order_items = [
- {
- "uuid": detail.uuid,
- "product_id": detail.product_id,
- "quantity": detail.quantity,
- "price": detail.price
- } for detail in order_details.values()
+ {"uuid": detail.uuid, "product_id": detail.product_id, "quantity": detail.quantity, "price": detail.price}
+ for detail in order_details.values()
if detail.order_id == ord.uuid
]
total_price = sum(item["quantity"] * item["price"] for item in order_items)
@@ -135,7 +115,7 @@ async def test_get_orders_internal(
"user_id": ord.user_id,
"order_date": ord.order_date.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"total_price": total_price,
- "order_items": order_items
+ "order_items": order_items,
}
orders.append(order_view_data)
@@ -148,17 +128,10 @@ async def test_get_orders_internal(
Test the response
"""
assert response.status_code == 200
- assert response.json() == {
- "action": "get",
- "orders": orders
- }
+ assert response.json() == {"action": "get", "orders": orders}
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="ORDER GET Endpoint - Fail to fetch order")
- ]
-)
+
+@pytest.mark.parametrize("_", [pytest.param(None, id="ORDER GET Endpoint - Fail to fetch order")])
async def test_get_orders_internal_fail(
client: AsyncClient,
db_test_create: None,
@@ -181,20 +154,16 @@ async def test_get_orders_internal_fail(
assert response.status_code == 404
assert response.json()["detail"] == "No orders in database"
+
@pytest.mark.parametrize(
"order_id, present",
[
pytest.param("375339b1", True, id="ORDER GET Endpoint - Fetch the specified order of the authenticated order"),
- pytest.param("xxxx1111", False, id="ORDER GET Endpoint - Fail to fetch the specified order of the authenticated order")
- ]
+ pytest.param("xxxx1111", False, id="ORDER GET Endpoint - Fail to fetch the specified order of the authenticated order"),
+ ],
)
async def test_get_order_by_uuid(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- order_id: str,
- present: str
+ client: AsyncClient, db_test_create: None, db_test_data: None, apply_security_override: None, order_id: str, present: str
) -> None:
"""
Test the `get` endpoint for fetching all the orders of the Order API.
@@ -216,11 +185,8 @@ async def test_get_order_by_uuid(
for ord in ords.values():
if ord.uuid == order_id:
order_items = [
- {
- "product_id": detail.product_id,
- "quantity": detail.quantity,
- "price": detail.price
- } for detail in order_details.values()
+ {"product_id": detail.product_id, "quantity": detail.quantity, "price": detail.price}
+ for detail in order_details.values()
if detail.order_id == ord.uuid
]
total_price = sum(item["quantity"] * item["price"] for item in order_items)
@@ -228,7 +194,7 @@ async def test_get_order_by_uuid(
"uuid": ord.uuid,
"order_date": ord.order_date.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"total_price": total_price,
- "order_items": order_items
+ "order_items": order_items,
}
"""
@@ -241,10 +207,7 @@ async def test_get_order_by_uuid(
"""
if present:
assert response.status_code == 200
- assert response.json() == {
- "action": "get",
- "order": order
- }
+ assert response.json() == {"action": "get", "order": order}
else:
assert response.status_code == 404
assert response.json()["detail"] == "Order not present in database"
diff --git a/tests/order/test_order_post.py b/tests/order/test_order_post.py
index 69d5fb2..7829db1 100644
--- a/tests/order/test_order_post.py
+++ b/tests/order/test_order_post.py
@@ -16,12 +16,7 @@
pytest.param(
{
"order_date": datetime.now().replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
- "order_items": [
- {
- "product_id": "3250fcbe",
- "quantity": 5
- }
- ]
+ "order_items": [{"product_id": "3250fcbe", "quantity": 5}],
},
True,
id="ORDER Post Endpoint - 201 Created",
@@ -29,27 +24,22 @@
pytest.param(
{
"order_date": datetime.now().replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
- "order_items": [
- {
- "product_id": "xxxxxxx",
- "quantity": 5
- }
- ]
+ "order_items": [{"product_id": "xxxxxxx", "quantity": 5}],
},
False,
id="ORDER Post Endpoint - Fail to place order for product which doesn't exist",
),
- ]
+ ],
)
async def test_create_order(
- test_app: FastAPI,
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- mocker: MockerFixture,
- payload: dict,
- present: bool
+ test_app: FastAPI,
+ client: AsyncClient,
+ db_test_create: None,
+ db_test_data: None,
+ apply_security_override: None,
+ mocker: MockerFixture,
+ payload: dict,
+ present: bool,
) -> None:
"""
Test the `create` endpoint of the Order API.
@@ -76,7 +66,7 @@ async def test_create_order(
"""
Perform the action of visiting the endpoint
"""
- response = await client.post("/api/v1/order/create", json = payload)
+ response = await client.post("/api/v1/order/create", json=payload)
"""
Test the response
@@ -88,18 +78,18 @@ async def test_create_order(
"order": {
"order_date": datetime.now().replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"uuid": "abcd1234",
- "total_price": payload["order_items"][0]["quantity"]*100.0, #Price from product test data
- "user_id": "2c92f0e8", #UUID from customer test data
+ "total_price": payload["order_items"][0]["quantity"] * 100.0, # Price from product test data
+ "user_id": "2c92f0e8", # UUID from customer test data
"order_items": [
{
"product_id": payload["order_items"][0]["product_id"],
"quantity": payload["order_items"][0]["quantity"],
- "price": 100.0, #Price from product test data
- "uuid": "abcd1234"
+ "price": 100.0, # Price from product test data
+ "uuid": "abcd1234",
}
- ]
- }
+ ],
+ },
}
else:
assert response.status_code == 404
- assert response.json()["detail"] == f"Product with ID: {payload["order_items"][0]["product_id"]} does not exist."
+ assert response.json()["detail"] == f"Product with ID: {payload['order_items'][0]['product_id']} does not exist."
diff --git a/tests/product/__init__.py b/tests/product/__init__.py
index eb47a16..399d907 100644
--- a/tests/product/__init__.py
+++ b/tests/product/__init__.py
@@ -18,27 +18,27 @@ def _test_data_product() -> dict[str, Product]:
exp_date=datetime.now(timezone.utc),
price=100.0,
business_id="d76a11f2",
- uuid="3250fcbe" #Specific UUID for testing of Product Endpoint
+ uuid="3250fcbe", # Specific UUID for testing of Product Endpoint
),
"test_prod_2": Product(
- name = "test_prod_2",
- description = "Second Test Product",
- category = "test",
+ name="test_prod_2",
+ description="Second Test Product",
+ category="test",
mfg_date=datetime.now(timezone.utc),
exp_date=datetime.now(timezone.utc),
- price = 250.0,
+ price=250.0,
business_id="d76a11f2",
- uuid="4e6c9aea" #Specific UUID for testing of Product Endpoint
+ uuid="4e6c9aea", # Specific UUID for testing of Product Endpoint
),
"test_prod_3": Product(
- name = "test_prod_3",
- description = "Third Test Product",
- category = "test",
+ name="test_prod_3",
+ description="Third Test Product",
+ category="test",
mfg_date=datetime.now(timezone.utc),
exp_date=datetime.now(timezone.utc),
- price = 115.0,
+ price=115.0,
business_id="d76a11f2",
- uuid="2e7a5e2d" #Specific UUID for testing of Product Endpoint
+ uuid="2e7a5e2d", # Specific UUID for testing of Product Endpoint
),
"test_prod_4": Product(
name="test_prod_4",
@@ -48,7 +48,7 @@ def _test_data_product() -> dict[str, Product]:
exp_date=datetime.now(timezone.utc),
price=25.0,
business_id="5c1c48fb",
- uuid="d5cf6983" # Specific UUID for testing of Product Endpoint
+ uuid="d5cf6983", # Specific UUID for testing of Product Endpoint
),
"test_prod_5": Product(
name="test_prod_5",
@@ -58,7 +58,7 @@ def _test_data_product() -> dict[str, Product]:
exp_date=datetime.now(timezone.utc),
price=65.0,
business_id="5c1c48fb",
- uuid="10677ef1" # Specific UUID for testing of Product Endpoint
- )
+ uuid="10677ef1", # Specific UUID for testing of Product Endpoint
+ ),
}
return data
diff --git a/tests/product/test_product_delete.py b/tests/product/test_product_delete.py
index a2a5955..ce29153 100644
--- a/tests/product/test_product_delete.py
+++ b/tests/product/test_product_delete.py
@@ -1,4 +1,3 @@
-
import pytest
from httpx import AsyncClient
@@ -8,24 +7,14 @@
@pytest.mark.parametrize(
"business_id, product_id, present",
[
+ pytest.param("5c1c48fb", "d5cf6983", True, id="PRODUCT DELETE Endpoint - Deletes the specific product of currently authenticated business"),
pytest.param(
- "5c1c48fb", "d5cf6983", True,
- id="PRODUCT DELETE Endpoint - Deletes the specific product of currently authenticated business"
- ),
- pytest.param(
- "5c1c48fb", "d76a11f2", False,
- id="PRODUCT DELETE Endpoint - Fails to find the specific product of currently authenticated business"
+ "5c1c48fb", "d76a11f2", False, id="PRODUCT DELETE Endpoint - Fails to find the specific product of currently authenticated business"
),
- ]
+ ],
)
async def test_delete_product(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- business_id: str,
- product_id: str,
- present: bool
+ client: AsyncClient, db_test_create: None, db_test_data: None, apply_security_override: None, business_id: str, product_id: str, present: bool
) -> None:
"""
Test the `delete` endpoint for deleting the specific product associated with the authenticated
@@ -47,7 +36,7 @@ async def test_delete_product(
data = _test_data_product()
product = {}
for prod in data.values():
- if prod.business_id == business_id and prod.uuid == product_id: #uuid of authenticated business; uuid of one of their products
+ if prod.business_id == business_id and prod.uuid == product_id: # uuid of authenticated business; uuid of one of their products
product = {
"name": prod.name,
"description": prod.description,
@@ -56,7 +45,7 @@ async def test_delete_product(
"exp_date": prod.exp_date.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"price": prod.price,
"uuid": prod.uuid,
- "business_id": prod.business_id
+ "business_id": prod.business_id,
}
"""
@@ -69,10 +58,7 @@ async def test_delete_product(
"""
if present:
assert response.status_code == 202
- assert response.json() == {
- "action": "delete",
- "product": product
- }
+ assert response.json() == {"action": "delete", "product": product}
else:
assert response.status_code == 404
assert response.json()["detail"] == "Product not present in database"
diff --git a/tests/product/test_product_get.py b/tests/product/test_product_get.py
index 43474f5..13dfd79 100644
--- a/tests/product/test_product_get.py
+++ b/tests/product/test_product_get.py
@@ -1,4 +1,3 @@
-
import pytest
from fastapi import FastAPI
from fastapi.security import HTTPBasicCredentials
@@ -9,18 +8,8 @@
from tests.product import _test_data_product
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="PRODUCT GET Endpoint - Fetch all the products")
- ]
-)
-async def test_get_products(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- _: None
-) -> None:
+@pytest.mark.parametrize("_", [pytest.param(None, id="PRODUCT GET Endpoint - Fetch all the products")])
+async def test_get_products(client: AsyncClient, db_test_create: None, db_test_data: None, _: None) -> None:
"""
Test the `get` endpoint for fetching all the products of the Product API.
@@ -42,7 +31,8 @@ async def test_get_products(
"mfg_date": product.mfg_date.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"exp_date": product.exp_date.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"price": product.price,
- } for product in data.values()
+ }
+ for product in data.values()
]
"""
@@ -54,17 +44,10 @@ async def test_get_products(
Test the response
"""
assert response.status_code == 200
- assert response.json() == {
- "action": "get",
- "products": products
- }
+ assert response.json() == {"action": "get", "products": products}
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="CUSTOMER GET Endpoint - Fail to fetch product")
- ]
-)
+
+@pytest.mark.parametrize("_", [pytest.param(None, id="CUSTOMER GET Endpoint - Fail to fetch product")])
async def test_get_products_fail(
client: AsyncClient,
db_test_create: None,
@@ -87,21 +70,16 @@ async def test_get_products_fail(
assert response.status_code == 404
assert response.json()["detail"] == "No product present in database"
+
@pytest.mark.parametrize(
"text, present",
[
pytest.param("Second", True, id="PRODUCT GET Endpoint - Fetch single matching product"),
pytest.param("Product", True, id="PRODUCT GET Endpoint - Fetch all the matching products"),
pytest.param("xxxxyyyy", False, id="PRODUCT GET Endpoint - Fail to fetch any matching products"),
- ]
+ ],
)
-async def test_get_product_by_text(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- text: str,
- present: bool
-) -> None:
+async def test_get_product_by_text(client: AsyncClient, db_test_create: None, db_test_data: None, text: str, present: bool) -> None:
"""
Test the `get` endpoint for fetching a single or all the matching products of the Product API.
@@ -125,7 +103,8 @@ async def test_get_product_by_text(
"mfg_date": product.mfg_date.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"exp_date": product.exp_date.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"price": product.price,
- } for product in data.values()
+ }
+ for product in data.values()
if (text.lower() in product.name.lower()) or (text.lower() in product.description.lower())
]
@@ -139,30 +118,28 @@ async def test_get_product_by_text(
"""
if present:
assert response.status_code == 200
- assert response.json() == {
- "action": "get",
- "products": products
- }
+ assert response.json() == {"action": "get", "products": products}
else:
assert response.status_code == 404
assert response.json()["detail"] == "No such product present in database"
+
@pytest.mark.parametrize(
"business_id, present",
[
pytest.param("d76a11f2", True, id="PRODUCT GET Endpoint - Fetch all the products associated with the authenticated business"),
- pytest.param("fd4a8cac", False, id="PRODUCT GET Endpoint - Fetch all the products associated with the authenticated business")
- ]
+ pytest.param("fd4a8cac", False, id="PRODUCT GET Endpoint - Fetch all the products associated with the authenticated business"),
+ ],
)
async def test_get_products_internal(
- test_app: FastAPI,
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- mocker: MockerFixture,
- business_id: str,
- present: bool
+ test_app: FastAPI,
+ client: AsyncClient,
+ db_test_create: None,
+ db_test_data: None,
+ apply_security_override: None,
+ mocker: MockerFixture,
+ business_id: str,
+ present: bool,
) -> None:
"""
Test the `get` endpoint for fetching all the products associated with the authenticated
@@ -192,9 +169,10 @@ async def test_get_products_internal(
"exp_date": product.exp_date.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"price": product.price,
"uuid": product.uuid,
- "business_id": product.business_id
- } for product in data.values()
- if product.business_id == business_id #uuid of the authenticated business
+ "business_id": product.business_id,
+ }
+ for product in data.values()
+ if product.business_id == business_id # uuid of the authenticated business
]
"""
@@ -218,37 +196,39 @@ async def test_get_products_internal(
"""
if present:
assert response.status_code == 200
- assert response.json() == {
- "action": "get",
- "products": products
- }
+ assert response.json() == {"action": "get", "products": products}
else:
assert response.status_code == 404
assert response.json()["detail"] == "No product present in database"
+
@pytest.mark.parametrize(
"business_id, product_id, present",
[
pytest.param(
- "d76a11f2", "3250fcbe", True,
- id="PRODUCT GET Endpoint - Fetch specific product by its UUID which is associated with the authenticated business"
+ "d76a11f2",
+ "3250fcbe",
+ True,
+ id="PRODUCT GET Endpoint - Fetch specific product by its UUID which is associated with the authenticated business",
),
pytest.param(
- "d76a11f2", "xxxxyyyy", False,
- id="PRODUCT GET Endpoint - Fail to fetch specific product by its UUID which is associated with the authenticated business"
- )
- ]
+ "d76a11f2",
+ "xxxxyyyy",
+ False,
+ id="PRODUCT GET Endpoint - Fail to fetch specific product by its UUID which is associated with the authenticated business",
+ ),
+ ],
)
async def test_get_product_by_uuid(
- test_app: FastAPI,
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- mocker: MockerFixture,
- business_id: str,
- product_id: str,
- present: bool
+ test_app: FastAPI,
+ client: AsyncClient,
+ db_test_create: None,
+ db_test_data: None,
+ apply_security_override: None,
+ mocker: MockerFixture,
+ business_id: str,
+ product_id: str,
+ present: bool,
) -> None:
"""
Test the `get` endpoint for fetching the specific product associated with the authenticated
@@ -271,7 +251,7 @@ async def test_get_product_by_uuid(
"""
data = _test_data_product()
for prod in data.values():
- if prod.business_id == business_id and prod.uuid == product_id: #uuid of authenticated business; uuid of one of their products
+ if prod.business_id == business_id and prod.uuid == product_id: # uuid of authenticated business; uuid of one of their products
product = {
"name": prod.name,
"description": prod.description,
@@ -280,7 +260,7 @@ async def test_get_product_by_uuid(
"exp_date": prod.exp_date.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"price": prod.price,
"uuid": prod.uuid,
- "business_id": prod.business_id
+ "business_id": prod.business_id,
}
"""
@@ -300,10 +280,7 @@ async def test_get_product_by_uuid(
"""
if present:
assert response.status_code == 200
- assert response.json() == {
- "action": "get",
- "product": product
- }
+ assert response.json() == {"action": "get", "product": product}
else:
assert response.status_code == 404
assert response.json()["detail"] == "Product not present in database"
diff --git a/tests/product/test_product_post.py b/tests/product/test_product_post.py
index 1e027fa..72c12e3 100644
--- a/tests/product/test_product_post.py
+++ b/tests/product/test_product_post.py
@@ -19,20 +19,20 @@
"category": "test",
"mfg_date": "1900-01-01T00:00:00",
"exp_date": "1900-01-01T00:00:00",
- "price": 150.05
+ "price": 150.05,
},
id="PRODUCT Post Endpoint - 201 Created",
)
- ]
+ ],
)
async def test_create_product(
- test_app: FastAPI,
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- mocker: MockerFixture,
- payload: dict[str, str]
+ test_app: FastAPI,
+ client: AsyncClient,
+ db_test_create: None,
+ db_test_data: None,
+ apply_security_override: None,
+ mocker: MockerFixture,
+ payload: dict[str, str],
) -> None:
"""
Test the `create` endpoint of the Product API.
@@ -58,7 +58,7 @@ async def test_create_product(
"""
Perform the action of visiting the endpoint
"""
- response = await client.post("/api/v1/product/create", json = payload)
+ response = await client.post("/api/v1/product/create", json=payload)
"""
Test the response
@@ -74,6 +74,6 @@ async def test_create_product(
"exp_date": payload["exp_date"],
"price": payload["price"],
"uuid": "abcd1234",
- "business_id": "d76a11f2"
- }
+ "business_id": "d76a11f2",
+ },
}
diff --git a/tests/product/test_product_put.py b/tests/product/test_product_put.py
index 7387fb0..58b4dd9 100644
--- a/tests/product/test_product_put.py
+++ b/tests/product/test_product_put.py
@@ -16,7 +16,7 @@
"category": "updated test",
"mfg_date": datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"exp_date": datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
- "price": 1950.05
+ "price": 1950.05,
},
"5c1c48fb",
"10677ef1",
@@ -30,24 +30,24 @@
"category": "",
"mfg_date": "1900-01-01 00:00:00+00:00",
"exp_date": "1900-01-01 00:00:00+00:00",
- "price": 0.05
+ "price": 0.05,
},
"5c1c48fb",
"4e6c9aea",
False,
id="PRODUCT PUT Endpoint - Fails to find the specific product of currently authenticated business",
- )
- ]
+ ),
+ ],
)
async def test_update_product(
- client: AsyncClient,
- db_test_create: None,
- db_test_data: None,
- apply_security_override: None,
- payload: dict[str, str],
- business_id: str,
- product_id: str,
- present: bool
+ client: AsyncClient,
+ db_test_create: None,
+ db_test_data: None,
+ apply_security_override: None,
+ payload: dict[str, str],
+ business_id: str,
+ product_id: str,
+ present: bool,
) -> None:
"""
Test the `create` endpoint of the Product API.
@@ -78,13 +78,13 @@ async def test_update_product(
"exp_date": prod.exp_date.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"price": prod.price,
"uuid": prod.uuid,
- "business_id": prod.business_id
+ "business_id": prod.business_id,
}
"""
Perform the action of visiting the endpoint
"""
- response = await client.put(f"/api/v1/product/update/uuid/{product_id}", json = payload)
+ response = await client.put(f"/api/v1/product/update/uuid/{product_id}", json=payload)
"""
Test the response
@@ -102,7 +102,7 @@ async def test_update_product(
"price": payload["price"],
"uuid": product["uuid"],
"business_id": product["business_id"],
- }
+ },
}
else:
assert response.status_code == 404
diff --git a/tests/root/test_root_get.py b/tests/root/test_root_get.py
index 7349d00..e59a2e4 100644
--- a/tests/root/test_root_get.py
+++ b/tests/root/test_root_get.py
@@ -2,16 +2,8 @@
from httpx import AsyncClient
-@pytest.mark.parametrize(
- "_",
- [
- pytest.param(None, id="ROOT GET Endpoint - Fetch the root endpoint of this application")
- ]
-)
-async def test_root(
- client: AsyncClient,
- _: None
-) -> None:
+@pytest.mark.parametrize("_", [pytest.param(None, id="ROOT GET Endpoint - Fetch the root endpoint of this application")])
+async def test_root(client: AsyncClient, _: None) -> None:
"""
Test the root ('/') endpoint.
@@ -31,5 +23,5 @@ async def test_root(
assert response.json() == {
"title": "FastAPI ECOM",
"description": "E-Commerce API for businesses and end users using FastAPI.",
- "version": "0.1.0"
+ "version": "0.1.0",
}