split tests into packages
This commit is contained in:
0
tests/test_fastapi/__init__.py
Normal file
0
tests/test_fastapi/__init__.py
Normal file
142
tests/test_fastapi/test_choices_schema.py
Normal file
142
tests/test_fastapi/test_choices_schema.py
Normal file
@ -0,0 +1,142 @@
|
||||
import datetime
|
||||
import decimal
|
||||
import uuid
|
||||
from enum import Enum
|
||||
|
||||
import databases
|
||||
import pydantic
|
||||
import pytest
|
||||
import sqlalchemy
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
import ormar
|
||||
from tests.settings import DATABASE_URL
|
||||
|
||||
app = FastAPI()
|
||||
database = databases.Database(DATABASE_URL, force_rollback=True)
|
||||
metadata = sqlalchemy.MetaData()
|
||||
app.state.database = database
|
||||
|
||||
uuid1 = uuid.uuid4()
|
||||
uuid2 = uuid.uuid4()
|
||||
|
||||
|
||||
class EnumTest(Enum):
|
||||
val1 = "Val1"
|
||||
val2 = "Val2"
|
||||
|
||||
|
||||
class Organisation(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "org"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
ident: str = ormar.String(max_length=100, choices=["ACME Ltd", "Other ltd"])
|
||||
priority: int = ormar.Integer(choices=[1, 2, 3, 4, 5])
|
||||
priority2: int = ormar.BigInteger(choices=[1, 2, 3, 4, 5])
|
||||
expire_date: datetime.date = ormar.Date(
|
||||
choices=[datetime.date(2021, 1, 1), datetime.date(2022, 5, 1)]
|
||||
)
|
||||
expire_time: datetime.time = ormar.Time(
|
||||
choices=[datetime.time(10, 0, 0), datetime.time(12, 30)]
|
||||
)
|
||||
|
||||
expire_datetime: datetime.datetime = ormar.DateTime(
|
||||
choices=[
|
||||
datetime.datetime(2021, 1, 1, 10, 0, 0),
|
||||
datetime.datetime(2022, 5, 1, 12, 30),
|
||||
]
|
||||
)
|
||||
random_val: float = ormar.Float(choices=[2.0, 3.5])
|
||||
random_decimal: decimal.Decimal = ormar.Decimal(
|
||||
scale=2, precision=4, choices=[decimal.Decimal(12.4), decimal.Decimal(58.2)]
|
||||
)
|
||||
random_json: pydantic.Json = ormar.JSON(choices=["aa", '{"aa":"bb"}'])
|
||||
random_uuid: uuid.UUID = ormar.UUID(choices=[uuid1, uuid2])
|
||||
enum_string: str = ormar.String(max_length=100, choices=list(EnumTest))
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup() -> None:
|
||||
database_ = app.state.database
|
||||
if not database_.is_connected:
|
||||
await database_.connect()
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown() -> None:
|
||||
database_ = app.state.database
|
||||
if database_.is_connected:
|
||||
await database_.disconnect()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
def create_test_database():
|
||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
||||
metadata.create_all(engine)
|
||||
yield
|
||||
metadata.drop_all(engine)
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Organisation)
|
||||
async def create_item(item: Organisation):
|
||||
await item.save()
|
||||
return item
|
||||
|
||||
|
||||
def test_all_endpoints():
|
||||
client = TestClient(app)
|
||||
with client as client:
|
||||
response = client.post(
|
||||
"/items/",
|
||||
json={"id": 1, "ident": "", "priority": 4, "expire_date": "2022-05-01"},
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
response = client.post(
|
||||
"/items/",
|
||||
json={
|
||||
"id": 1,
|
||||
"ident": "ACME Ltd",
|
||||
"priority": 4,
|
||||
"priority2": 2,
|
||||
"expire_date": "2022-05-01",
|
||||
"expire_time": "10:00:00",
|
||||
"expire_datetime": "2022-05-01T12:30:00",
|
||||
"random_val": 3.5,
|
||||
"random_decimal": 12.4,
|
||||
"random_json": '{"aa":"bb"}',
|
||||
"random_uuid": str(uuid1),
|
||||
"enum_string": EnumTest.val1.value,
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
item = Organisation(**response.json())
|
||||
assert item.pk is not None
|
||||
response = client.get("/docs/")
|
||||
assert response.status_code == 200
|
||||
assert b"<title>FastAPI - Swagger UI</title>" in response.content
|
||||
|
||||
|
||||
def test_schema_modification():
|
||||
schema = Organisation.schema()
|
||||
for field in ["ident", "priority", "expire_date"]:
|
||||
assert field in schema["properties"]
|
||||
assert schema["properties"].get(field).get("enum") == list(
|
||||
Organisation.Meta.model_fields.get(field).choices
|
||||
)
|
||||
assert "An enumeration." in schema["properties"].get(field).get("description")
|
||||
|
||||
|
||||
def test_schema_gen():
|
||||
schema = app.openapi()
|
||||
assert "Organisation" in schema["components"]["schemas"]
|
||||
props = schema["components"]["schemas"]["Organisation"]["properties"]
|
||||
for field in [k for k in Organisation.Meta.model_fields.keys() if k != "id"]:
|
||||
assert "enum" in props.get(field)
|
||||
assert "description" in props.get(field)
|
||||
assert "An enumeration." in props.get(field).get("description")
|
||||
@ -0,0 +1,72 @@
|
||||
from typing import Optional
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
import databases
|
||||
import sqlalchemy
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
import ormar
|
||||
|
||||
app = FastAPI()
|
||||
DATABASE_URL = "sqlite:///db.sqlite"
|
||||
database = databases.Database(DATABASE_URL)
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
|
||||
class BaseMeta(ormar.ModelMeta):
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
|
||||
class CA(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "cas"
|
||||
|
||||
id: UUID = ormar.UUID(primary_key=True, default=uuid4)
|
||||
ca_name: str = ormar.Text(default="")
|
||||
|
||||
|
||||
class CB1(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "cb1s"
|
||||
|
||||
id: UUID = ormar.UUID(primary_key=True, default=uuid4)
|
||||
cb1_name: str = ormar.Text(default="")
|
||||
ca1: Optional[CA] = ormar.ForeignKey(CA, nullable=True)
|
||||
|
||||
|
||||
class CB2(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "cb2s"
|
||||
|
||||
id: UUID = ormar.UUID(primary_key=True, default=uuid4)
|
||||
cb2_name: str = ormar.Text(default="")
|
||||
ca2: Optional[CA] = ormar.ForeignKey(CA, nullable=True)
|
||||
|
||||
|
||||
@app.get("/ca", response_model=CA)
|
||||
async def get_ca(): # pragma: no cover
|
||||
return None
|
||||
|
||||
|
||||
@app.get("/cb1", response_model=CB1)
|
||||
async def get_cb1(): # pragma: no cover
|
||||
return None
|
||||
|
||||
|
||||
@app.get("/cb2", response_model=CB2)
|
||||
async def get_cb2(): # pragma: no cover
|
||||
return None
|
||||
|
||||
|
||||
def test_all_endpoints():
|
||||
client = TestClient(app)
|
||||
with client as client:
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
schema = response.json()
|
||||
components = schema["components"]["schemas"]
|
||||
assert all(x in components for x in ["CA", "CB1", "CB2"])
|
||||
pk_onlys = [x for x in list(components.keys()) if x.startswith("PkOnly")]
|
||||
assert len(pk_onlys) == 2
|
||||
132
tests/test_fastapi/test_fastapi_docs.py
Normal file
132
tests/test_fastapi/test_fastapi_docs.py
Normal file
@ -0,0 +1,132 @@
|
||||
from typing import List
|
||||
|
||||
import databases
|
||||
import pytest
|
||||
import sqlalchemy
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
import ormar
|
||||
from tests.settings import DATABASE_URL
|
||||
|
||||
app = FastAPI()
|
||||
metadata = sqlalchemy.MetaData()
|
||||
database = databases.Database(DATABASE_URL, force_rollback=True)
|
||||
app.state.database = database
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup() -> None:
|
||||
database_ = app.state.database
|
||||
if not database_.is_connected:
|
||||
await database_.connect()
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown() -> None:
|
||||
database_ = app.state.database
|
||||
if database_.is_connected:
|
||||
await database_.disconnect()
|
||||
|
||||
|
||||
class LocalMeta:
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
|
||||
class Category(ormar.Model):
|
||||
class Meta(LocalMeta):
|
||||
tablename = "categories"
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100)
|
||||
|
||||
|
||||
class Item(ormar.Model):
|
||||
class Meta(LocalMeta):
|
||||
pass
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100)
|
||||
categories = ormar.ManyToMany(Category)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
def create_test_database():
|
||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
||||
metadata.create_all(engine)
|
||||
yield
|
||||
metadata.drop_all(engine)
|
||||
|
||||
|
||||
@app.get("/items/", response_model=List[Item])
|
||||
async def get_items():
|
||||
items = await Item.objects.select_related("categories").all()
|
||||
return items
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item)
|
||||
async def create_item(item: Item):
|
||||
await item.save()
|
||||
return item
|
||||
|
||||
|
||||
@app.post("/items/add_category/", response_model=Item)
|
||||
async def add_item_category(item: Item, category: Category):
|
||||
await item.categories.add(category)
|
||||
return item
|
||||
|
||||
|
||||
@app.post("/categories/", response_model=Category)
|
||||
async def create_category(category: Category):
|
||||
await category.save()
|
||||
return category
|
||||
|
||||
|
||||
def test_all_endpoints():
|
||||
client = TestClient(app)
|
||||
with client as client:
|
||||
response = client.post("/categories/", json={"name": "test cat"})
|
||||
category = response.json()
|
||||
response = client.post("/categories/", json={"name": "test cat2"})
|
||||
category2 = response.json()
|
||||
|
||||
response = client.post("/items/", json={"name": "test", "id": 1})
|
||||
item = Item(**response.json())
|
||||
assert item.pk is not None
|
||||
|
||||
response = client.post(
|
||||
"/items/add_category/", json={"item": item.dict(), "category": category}
|
||||
)
|
||||
item = Item(**response.json())
|
||||
assert len(item.categories) == 1
|
||||
assert item.categories[0].name == "test cat"
|
||||
|
||||
client.post(
|
||||
"/items/add_category/", json={"item": item.dict(), "category": category2}
|
||||
)
|
||||
|
||||
response = client.get("/items/")
|
||||
items = [Item(**item) for item in response.json()]
|
||||
assert items[0] == item
|
||||
assert len(items[0].categories) == 2
|
||||
assert items[0].categories[0].name == "test cat"
|
||||
assert items[0].categories[1].name == "test cat2"
|
||||
|
||||
response = client.get("/docs/")
|
||||
assert response.status_code == 200
|
||||
assert b"<title>FastAPI - Swagger UI</title>" in response.content
|
||||
|
||||
|
||||
def test_schema_modification():
|
||||
schema = Item.schema()
|
||||
assert any(
|
||||
x.get("type") == "array" for x in schema["properties"]["categories"]["anyOf"]
|
||||
)
|
||||
assert schema["properties"]["categories"]["title"] == "Categories"
|
||||
|
||||
|
||||
def test_schema_gen():
|
||||
schema = app.openapi()
|
||||
assert "Category" in schema["components"]["schemas"]
|
||||
assert "Item" in schema["components"]["schemas"]
|
||||
60
tests/test_fastapi/test_fastapi_usage.py
Normal file
60
tests/test_fastapi/test_fastapi_usage.py
Normal file
@ -0,0 +1,60 @@
|
||||
from typing import Optional
|
||||
|
||||
import databases
|
||||
import sqlalchemy
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
import ormar
|
||||
from tests.settings import DATABASE_URL
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
database = databases.Database(DATABASE_URL, force_rollback=True)
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
|
||||
class Category(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "categories"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100)
|
||||
|
||||
|
||||
class Item(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "items"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100)
|
||||
category: Optional[Category] = ormar.ForeignKey(Category, nullable=True)
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item)
|
||||
async def create_item(item: Item):
|
||||
return item
|
||||
|
||||
|
||||
def test_read_main():
|
||||
client = TestClient(app)
|
||||
with client as client:
|
||||
response = client.post(
|
||||
"/items/", json={"name": "test", "id": 1, "category": {"name": "test cat"}}
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"category": {
|
||||
"id": None,
|
||||
"items": [{"id": 1, "name": "test"}],
|
||||
"name": "test cat",
|
||||
},
|
||||
"id": 1,
|
||||
"name": "test",
|
||||
}
|
||||
item = Item(**response.json())
|
||||
assert item.id == 1
|
||||
219
tests/test_fastapi/test_inheritance_concrete_fastapi.py
Normal file
219
tests/test_fastapi/test_inheritance_concrete_fastapi.py
Normal file
@ -0,0 +1,219 @@
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
import sqlalchemy
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from tests.settings import DATABASE_URL
|
||||
from tests.test_inheritance.test_inheritance_concrete import ( # type: ignore
|
||||
Category,
|
||||
Subject,
|
||||
Person,
|
||||
Bus,
|
||||
Truck,
|
||||
Bus2,
|
||||
Truck2,
|
||||
db as database,
|
||||
metadata,
|
||||
)
|
||||
|
||||
app = FastAPI()
|
||||
app.state.database = database
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup() -> None:
|
||||
database_ = app.state.database
|
||||
if not database_.is_connected:
|
||||
await database_.connect()
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown() -> None:
|
||||
database_ = app.state.database
|
||||
if database_.is_connected:
|
||||
await database_.disconnect()
|
||||
|
||||
|
||||
@app.post("/subjects/", response_model=Subject)
|
||||
async def create_item(item: Subject):
|
||||
return item
|
||||
|
||||
|
||||
@app.post("/categories/", response_model=Category)
|
||||
async def create_category(category: Category):
|
||||
await category.save()
|
||||
return category
|
||||
|
||||
|
||||
@app.post("/buses/", response_model=Bus)
|
||||
async def create_bus(bus: Bus):
|
||||
await bus.save()
|
||||
return bus
|
||||
|
||||
|
||||
@app.get("/buses/{item_id}", response_model=Bus)
|
||||
async def get_bus(item_id: int):
|
||||
bus = await Bus.objects.select_related(["owner", "co_owner"]).get(pk=item_id)
|
||||
return bus
|
||||
|
||||
|
||||
@app.post("/trucks/", response_model=Truck)
|
||||
async def create_truck(truck: Truck):
|
||||
await truck.save()
|
||||
return truck
|
||||
|
||||
|
||||
@app.post("/persons/", response_model=Person)
|
||||
async def create_person(person: Person):
|
||||
await person.save()
|
||||
return person
|
||||
|
||||
|
||||
@app.post("/buses2/", response_model=Bus2)
|
||||
async def create_bus2(bus: Bus2):
|
||||
await bus.save()
|
||||
return bus
|
||||
|
||||
|
||||
@app.post("/buses2/{item_id}/add_coowner/", response_model=Bus2)
|
||||
async def add_bus_coowner(item_id: int, person: Person):
|
||||
bus = await Bus2.objects.select_related(["owner", "co_owners"]).get(pk=item_id)
|
||||
await bus.co_owners.add(person)
|
||||
return bus
|
||||
|
||||
|
||||
@app.post("/trucks2/", response_model=Truck2)
|
||||
async def create_truck2(truck: Truck2):
|
||||
await truck.save()
|
||||
return truck
|
||||
|
||||
|
||||
@app.post("/trucks2/{item_id}/add_coowner/", response_model=Truck2)
|
||||
async def add_truck_coowner(item_id: int, person: Person):
|
||||
truck = await Truck2.objects.select_related(["owner", "co_owners"]).get(pk=item_id)
|
||||
await truck.co_owners.add(person)
|
||||
return truck
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
def create_test_database():
|
||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
||||
metadata.create_all(engine)
|
||||
yield
|
||||
metadata.drop_all(engine)
|
||||
|
||||
|
||||
def test_read_main():
|
||||
client = TestClient(app)
|
||||
with client as client:
|
||||
test_category = dict(name="Foo", code=123, created_by="Sam", updated_by="Max")
|
||||
test_subject = dict(name="Bar")
|
||||
|
||||
response = client.post("/categories/", json=test_category)
|
||||
assert response.status_code == 200
|
||||
cat = Category(**response.json())
|
||||
assert cat.name == "Foo"
|
||||
assert cat.created_by == "Sam"
|
||||
assert cat.created_date is not None
|
||||
assert cat.id == 1
|
||||
|
||||
cat_dict = cat.dict()
|
||||
cat_dict["updated_date"] = cat_dict["updated_date"].strftime(
|
||||
"%Y-%m-%d %H:%M:%S.%f"
|
||||
)
|
||||
cat_dict["created_date"] = cat_dict["created_date"].strftime(
|
||||
"%Y-%m-%d %H:%M:%S.%f"
|
||||
)
|
||||
test_subject["category"] = cat_dict
|
||||
response = client.post("/subjects/", json=test_subject)
|
||||
assert response.status_code == 200
|
||||
sub = Subject(**response.json())
|
||||
assert sub.name == "Bar"
|
||||
assert sub.category.pk == cat.pk
|
||||
assert isinstance(sub.updated_date, datetime.datetime)
|
||||
|
||||
|
||||
def test_inheritance_with_relation():
|
||||
client = TestClient(app)
|
||||
with client as client:
|
||||
sam = Person(**client.post("/persons/", json={"name": "Sam"}).json())
|
||||
joe = Person(**client.post("/persons/", json={"name": "Joe"}).json())
|
||||
|
||||
truck_dict = dict(
|
||||
name="Shelby wanna be",
|
||||
max_capacity=1400,
|
||||
owner=sam.dict(),
|
||||
co_owner=joe.dict(),
|
||||
)
|
||||
bus_dict = dict(
|
||||
name="Unicorn", max_persons=50, owner=sam.dict(), co_owner=joe.dict()
|
||||
)
|
||||
unicorn = Bus(**client.post("/buses/", json=bus_dict).json())
|
||||
shelby = Truck(**client.post("/trucks/", json=truck_dict).json())
|
||||
|
||||
assert shelby.name == "Shelby wanna be"
|
||||
assert shelby.owner.name == "Sam"
|
||||
assert shelby.co_owner.name == "Joe"
|
||||
assert shelby.co_owner == joe
|
||||
assert shelby.max_capacity == 1400
|
||||
|
||||
assert unicorn.name == "Unicorn"
|
||||
assert unicorn.owner == sam
|
||||
assert unicorn.owner.name == "Sam"
|
||||
assert unicorn.co_owner.name == "Joe"
|
||||
assert unicorn.max_persons == 50
|
||||
|
||||
unicorn2 = Bus(**client.get(f"/buses/{unicorn.pk}").json())
|
||||
assert unicorn2.name == "Unicorn"
|
||||
assert unicorn2.owner == sam
|
||||
assert unicorn2.owner.name == "Sam"
|
||||
assert unicorn2.co_owner.name == "Joe"
|
||||
assert unicorn2.max_persons == 50
|
||||
|
||||
|
||||
def test_inheritance_with_m2m_relation():
|
||||
client = TestClient(app)
|
||||
with client as client:
|
||||
sam = Person(**client.post("/persons/", json={"name": "Sam"}).json())
|
||||
joe = Person(**client.post("/persons/", json={"name": "Joe"}).json())
|
||||
alex = Person(**client.post("/persons/", json={"name": "Alex"}).json())
|
||||
|
||||
truck_dict = dict(name="Shelby wanna be", max_capacity=2000, owner=sam.dict())
|
||||
bus_dict = dict(name="Unicorn", max_persons=80, owner=sam.dict())
|
||||
|
||||
unicorn = Bus2(**client.post("/buses2/", json=bus_dict).json())
|
||||
shelby = Truck2(**client.post("/trucks2/", json=truck_dict).json())
|
||||
|
||||
unicorn = Bus2(
|
||||
**client.post(f"/buses2/{unicorn.pk}/add_coowner/", json=joe.dict()).json()
|
||||
)
|
||||
unicorn = Bus2(
|
||||
**client.post(f"/buses2/{unicorn.pk}/add_coowner/", json=alex.dict()).json()
|
||||
)
|
||||
|
||||
assert shelby.name == "Shelby wanna be"
|
||||
assert shelby.owner.name == "Sam"
|
||||
assert len(shelby.co_owners) == 0
|
||||
assert shelby.max_capacity == 2000
|
||||
|
||||
assert unicorn.name == "Unicorn"
|
||||
assert unicorn.owner == sam
|
||||
assert unicorn.owner.name == "Sam"
|
||||
assert unicorn.co_owners[0].name == "Joe"
|
||||
assert unicorn.co_owners[1] == alex
|
||||
assert unicorn.max_persons == 80
|
||||
|
||||
client.post(f"/trucks2/{shelby.pk}/add_coowner/", json=alex.dict())
|
||||
|
||||
shelby = Truck2(
|
||||
**client.post(f"/trucks2/{shelby.pk}/add_coowner/", json=joe.dict()).json()
|
||||
)
|
||||
|
||||
assert shelby.name == "Shelby wanna be"
|
||||
assert shelby.owner.name == "Sam"
|
||||
assert len(shelby.co_owners) == 2
|
||||
assert shelby.co_owners[0] == alex
|
||||
assert shelby.co_owners[1] == joe
|
||||
assert shelby.max_capacity == 2000
|
||||
75
tests/test_fastapi/test_inheritance_mixins_fastapi.py
Normal file
75
tests/test_fastapi/test_inheritance_mixins_fastapi.py
Normal file
@ -0,0 +1,75 @@
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
import sqlalchemy
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from tests.settings import DATABASE_URL
|
||||
from tests.test_inheritance.test_inheritance_mixins import Category, Subject, metadata, db as database # type: ignore
|
||||
|
||||
app = FastAPI()
|
||||
app.state.database = database
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup() -> None:
|
||||
database_ = app.state.database
|
||||
if not database_.is_connected:
|
||||
await database_.connect()
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown() -> None:
|
||||
database_ = app.state.database
|
||||
if database_.is_connected:
|
||||
await database_.disconnect()
|
||||
|
||||
|
||||
@app.post("/subjects/", response_model=Subject)
|
||||
async def create_item(item: Subject):
|
||||
return item
|
||||
|
||||
|
||||
@app.post("/categories/", response_model=Category)
|
||||
async def create_category(category: Category):
|
||||
await category.save()
|
||||
return category
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
def create_test_database():
|
||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
||||
metadata.create_all(engine)
|
||||
yield
|
||||
metadata.drop_all(engine)
|
||||
|
||||
|
||||
def test_read_main():
|
||||
client = TestClient(app)
|
||||
with client as client:
|
||||
test_category = dict(name="Foo", code=123, created_by="Sam", updated_by="Max")
|
||||
test_subject = dict(name="Bar")
|
||||
|
||||
response = client.post("/categories/", json=test_category)
|
||||
assert response.status_code == 200
|
||||
cat = Category(**response.json())
|
||||
assert cat.name == "Foo"
|
||||
assert cat.created_by == "Sam"
|
||||
assert cat.created_date is not None
|
||||
assert cat.id == 1
|
||||
|
||||
cat_dict = cat.dict()
|
||||
cat_dict["updated_date"] = cat_dict["updated_date"].strftime(
|
||||
"%Y-%m-%d %H:%M:%S.%f"
|
||||
)
|
||||
cat_dict["created_date"] = cat_dict["created_date"].strftime(
|
||||
"%Y-%m-%d %H:%M:%S.%f"
|
||||
)
|
||||
test_subject["category"] = cat_dict
|
||||
response = client.post("/subjects/", json=test_subject)
|
||||
assert response.status_code == 200
|
||||
sub = Subject(**response.json())
|
||||
assert sub.name == "Bar"
|
||||
assert sub.category.pk == cat.pk
|
||||
assert isinstance(sub.updated_date, datetime.datetime)
|
||||
181
tests/test_fastapi/test_json_field_fastapi.py
Normal file
181
tests/test_fastapi/test_json_field_fastapi.py
Normal file
@ -0,0 +1,181 @@
|
||||
# type: ignore
|
||||
import uuid
|
||||
from typing import List
|
||||
|
||||
import databases
|
||||
import pydantic
|
||||
import pytest
|
||||
import sqlalchemy
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
import ormar
|
||||
from tests.settings import DATABASE_URL
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
database = databases.Database(DATABASE_URL, force_rollback=True)
|
||||
metadata = sqlalchemy.MetaData()
|
||||
app.state.database = database
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup() -> None:
|
||||
database_ = app.state.database
|
||||
if not database_.is_connected:
|
||||
await database_.connect()
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown() -> None:
|
||||
database_ = app.state.database
|
||||
if database_.is_connected:
|
||||
await database_.disconnect()
|
||||
|
||||
|
||||
class BaseMeta(ormar.ModelMeta):
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
|
||||
class Thing(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "things"
|
||||
|
||||
id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4)
|
||||
name: str = ormar.Text(default="")
|
||||
js: pydantic.Json = ormar.JSON()
|
||||
|
||||
|
||||
@app.get("/things", response_model=List[Thing])
|
||||
async def read_things():
|
||||
return await Thing.objects.order_by("name").all()
|
||||
|
||||
|
||||
@app.get("/things_with_sample", response_model=List[Thing])
|
||||
async def read_things_sample():
|
||||
await Thing(name="b", js=["asdf", "asdf", "bobby", "nigel"]).save()
|
||||
await Thing(name="a", js='["lemon", "raspberry", "lime", "pumice"]').save()
|
||||
return await Thing.objects.order_by("name").all()
|
||||
|
||||
|
||||
@app.get("/things_with_sample_after_init", response_model=Thing)
|
||||
async def read_things_init():
|
||||
thing1 = Thing(js="{}")
|
||||
thing1.name = "d"
|
||||
thing1.js = ["js", "set", "after", "constructor"]
|
||||
await thing1.save()
|
||||
return thing1
|
||||
|
||||
|
||||
@app.put("/update_thing", response_model=Thing)
|
||||
async def update_things(thing: Thing):
|
||||
thing.js = ["js", "set", "after", "update"] # type: ignore
|
||||
await thing.update()
|
||||
return thing
|
||||
|
||||
|
||||
@app.post("/things", response_model=Thing)
|
||||
async def create_things(thing: Thing):
|
||||
thing = await thing.save()
|
||||
return thing
|
||||
|
||||
|
||||
@app.get("/things_untyped")
|
||||
async def read_things_untyped():
|
||||
return await Thing.objects.order_by("name").all()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
def create_test_database():
|
||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
||||
metadata.create_all(engine)
|
||||
yield
|
||||
metadata.drop_all(engine)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_json_is_required_if_not_nullable():
|
||||
with pytest.raises(pydantic.ValidationError):
|
||||
Thing()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_json_is_not_required_if_nullable():
|
||||
class Thing2(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "things2"
|
||||
|
||||
id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4)
|
||||
name: str = ormar.Text(default="")
|
||||
js: pydantic.Json = ormar.JSON(nullable=True)
|
||||
|
||||
Thing2()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_setting_values_after_init():
|
||||
async with database:
|
||||
t1 = Thing(id="67a82813-d90c-45ff-b546-b4e38d7030d7", name="t1", js=["thing1"])
|
||||
assert '["thing1"]' in t1.json()
|
||||
await t1.save()
|
||||
t1.json()
|
||||
assert '["thing1"]' in t1.json()
|
||||
|
||||
assert '["thing1"]' in (await Thing.objects.get(id=t1.id)).json()
|
||||
await t1.update()
|
||||
assert '["thing1"]' in (await Thing.objects.get(id=t1.id)).json()
|
||||
|
||||
|
||||
def test_read_main():
|
||||
client = TestClient(app)
|
||||
with client as client:
|
||||
response = client.get("/things_with_sample")
|
||||
assert response.status_code == 200
|
||||
|
||||
# check if raw response not double encoded
|
||||
assert '["lemon","raspberry","lime","pumice"]' in response.text
|
||||
|
||||
# parse json and check that we get lists not strings
|
||||
resp = response.json()
|
||||
assert resp[0].get("js") == ["lemon", "raspberry", "lime", "pumice"]
|
||||
assert resp[1].get("js") == ["asdf", "asdf", "bobby", "nigel"]
|
||||
|
||||
# create a new one
|
||||
response = client.post("/things", json={"js": ["test", "test2"], "name": "c"})
|
||||
assert response.json().get("js") == ["test", "test2"]
|
||||
|
||||
# get all with new one
|
||||
response = client.get("/things")
|
||||
assert response.status_code == 200
|
||||
assert '["test","test2"]' in response.text
|
||||
resp = response.json()
|
||||
assert resp[0].get("js") == ["lemon", "raspberry", "lime", "pumice"]
|
||||
assert resp[1].get("js") == ["asdf", "asdf", "bobby", "nigel"]
|
||||
assert resp[2].get("js") == ["test", "test2"]
|
||||
|
||||
response = client.get("/things_with_sample_after_init")
|
||||
assert response.status_code == 200
|
||||
resp = response.json()
|
||||
assert resp.get("js") == ["js", "set", "after", "constructor"]
|
||||
|
||||
# test new with after constructor
|
||||
response = client.get("/things")
|
||||
resp = response.json()
|
||||
assert resp[0].get("js") == ["lemon", "raspberry", "lime", "pumice"]
|
||||
assert resp[1].get("js") == ["asdf", "asdf", "bobby", "nigel"]
|
||||
assert resp[2].get("js") == ["test", "test2"]
|
||||
assert resp[3].get("js") == ["js", "set", "after", "constructor"]
|
||||
|
||||
response = client.put("/update_thing", json=resp[3])
|
||||
assert response.status_code == 200
|
||||
resp = response.json()
|
||||
assert resp.get("js") == ["js", "set", "after", "update"]
|
||||
|
||||
# test new with after constructor
|
||||
response = client.get("/things_untyped")
|
||||
resp = response.json()
|
||||
assert resp[0].get("js") == ["lemon", "raspberry", "lime", "pumice"]
|
||||
assert resp[1].get("js") == ["asdf", "asdf", "bobby", "nigel"]
|
||||
assert resp[2].get("js") == ["test", "test2"]
|
||||
assert resp[3].get("js") == ["js", "set", "after", "update"]
|
||||
158
tests/test_fastapi/test_more_reallife_fastapi.py
Normal file
158
tests/test_fastapi/test_more_reallife_fastapi.py
Normal file
@ -0,0 +1,158 @@
|
||||
import asyncio
|
||||
from typing import List, Optional
|
||||
|
||||
import databases
|
||||
import pytest
|
||||
import sqlalchemy
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
import ormar
|
||||
from tests.settings import DATABASE_URL
|
||||
|
||||
app = FastAPI()
|
||||
metadata = sqlalchemy.MetaData()
|
||||
database = databases.Database(DATABASE_URL, force_rollback=True)
|
||||
app.state.database = database
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup() -> None:
|
||||
database_ = app.state.database
|
||||
if not database_.is_connected:
|
||||
await database_.connect()
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown() -> None:
|
||||
database_ = app.state.database
|
||||
if database_.is_connected:
|
||||
await database_.disconnect()
|
||||
|
||||
|
||||
class Category(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "categories"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100)
|
||||
|
||||
|
||||
class Item(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "items"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100)
|
||||
category: Optional[Category] = ormar.ForeignKey(Category, nullable=True)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
def create_test_database():
|
||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
||||
metadata.create_all(engine)
|
||||
yield
|
||||
metadata.drop_all(engine)
|
||||
|
||||
|
||||
@app.get("/items/", response_model=List[Item])
|
||||
async def get_items():
|
||||
items = await Item.objects.select_related("category").all()
|
||||
return items
|
||||
|
||||
|
||||
@app.get("/items/raw/", response_model=List[Item])
|
||||
async def get_raw_items():
|
||||
items = await Item.objects.all()
|
||||
return items
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item)
|
||||
async def create_item(item: Item):
|
||||
await item.save()
|
||||
return item
|
||||
|
||||
|
||||
@app.post("/categories/", response_model=Category)
|
||||
async def create_category(category: Category):
|
||||
await category.save()
|
||||
return category
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def get_item(item_id: int):
|
||||
item = await Item.objects.get(pk=item_id)
|
||||
return item
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def update_item(item_id: int, item: Item):
|
||||
item_db = await Item.objects.get(pk=item_id)
|
||||
return await item_db.update(**item.dict())
|
||||
|
||||
|
||||
@app.delete("/items/{item_id}")
|
||||
async def delete_item(item_id: int, item: Item = None):
|
||||
if item:
|
||||
return {"deleted_rows": await item.delete()}
|
||||
item_db = await Item.objects.get(pk=item_id)
|
||||
return {"deleted_rows": await item_db.delete()}
|
||||
|
||||
|
||||
def test_all_endpoints():
|
||||
client = TestClient(app)
|
||||
with client as client:
|
||||
response = client.post("/categories/", json={"name": "test cat"})
|
||||
category = response.json()
|
||||
response = client.post(
|
||||
"/items/", json={"name": "test", "id": 1, "category": category}
|
||||
)
|
||||
item = Item(**response.json())
|
||||
assert item.pk is not None
|
||||
|
||||
response = client.get("/items/")
|
||||
items = [Item(**item) for item in response.json()]
|
||||
assert items[0] == item
|
||||
|
||||
item.name = "New name"
|
||||
response = client.put(f"/items/{item.pk}", json=item.dict())
|
||||
assert response.json() == item.dict()
|
||||
|
||||
response = client.get("/items/")
|
||||
items = [Item(**item) for item in response.json()]
|
||||
assert items[0].name == "New name"
|
||||
|
||||
response = client.get("/items/raw/")
|
||||
items = [Item(**item) for item in response.json()]
|
||||
assert items[0].name == "New name"
|
||||
assert items[0].category.name is None
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(items[0].category.load())
|
||||
assert items[0].category.name is not None
|
||||
|
||||
response = client.get(f"/items/{item.pk}")
|
||||
new_item = Item(**response.json())
|
||||
assert new_item == item
|
||||
|
||||
response = client.delete(f"/items/{item.pk}")
|
||||
assert response.json().get("deleted_rows", "__UNDEFINED__") != "__UNDEFINED__"
|
||||
response = client.get("/items/")
|
||||
items = response.json()
|
||||
assert len(items) == 0
|
||||
|
||||
client.post("/items/", json={"name": "test_2", "id": 2, "category": category})
|
||||
response = client.get("/items/")
|
||||
items = response.json()
|
||||
assert len(items) == 1
|
||||
|
||||
item = Item(**items[0])
|
||||
response = client.delete(f"/items/{item.pk}", json=item.dict())
|
||||
assert response.json().get("deleted_rows", "__UNDEFINED__") != "__UNDEFINED__"
|
||||
|
||||
response = client.get("/docs/")
|
||||
assert response.status_code == 200
|
||||
147
tests/test_fastapi/test_wekref_exclusion.py
Normal file
147
tests/test_fastapi/test_wekref_exclusion.py
Normal file
@ -0,0 +1,147 @@
|
||||
from typing import List, Optional
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
import databases
|
||||
import pydantic
|
||||
import pytest
|
||||
import sqlalchemy
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
import ormar
|
||||
from tests.settings import DATABASE_URL
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
database = databases.Database(DATABASE_URL, force_rollback=True)
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
app.state.database = database
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup() -> None:
|
||||
database_ = app.state.database
|
||||
if not database_.is_connected:
|
||||
await database_.connect()
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown() -> None:
|
||||
database_ = app.state.database
|
||||
if database_.is_connected:
|
||||
await database_.disconnect()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
def create_test_database():
|
||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
||||
metadata.create_all(engine)
|
||||
yield
|
||||
metadata.drop_all(engine)
|
||||
|
||||
|
||||
class BaseMeta(ormar.ModelMeta):
|
||||
database = database
|
||||
metadata = metadata
|
||||
|
||||
|
||||
class OtherThing(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "other_things"
|
||||
|
||||
id: UUID = ormar.UUID(primary_key=True, default=uuid4)
|
||||
name: str = ormar.Text(default="")
|
||||
ot_contents: str = ormar.Text(default="")
|
||||
|
||||
|
||||
class Thing(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "things"
|
||||
|
||||
id: UUID = ormar.UUID(primary_key=True, default=uuid4)
|
||||
name: str = ormar.Text(default="")
|
||||
js: pydantic.Json = ormar.JSON(nullable=True)
|
||||
other_thing: Optional[OtherThing] = ormar.ForeignKey(OtherThing, nullable=True)
|
||||
|
||||
|
||||
@app.post("/test/1")
|
||||
async def post_test_1():
|
||||
# don't split initialization and attribute assignment
|
||||
ot = await OtherThing(ot_contents="otc").save()
|
||||
await Thing(other_thing=ot, name="t1").save()
|
||||
await Thing(other_thing=ot, name="t2").save()
|
||||
await Thing(other_thing=ot, name="t3").save()
|
||||
|
||||
# if you do not care about returned object you can even go with bulk_create
|
||||
# all of them are created in one transaction
|
||||
# things = [Thing(other_thing=ot, name='t1'),
|
||||
# Thing(other_thing=ot, name="t2"),
|
||||
# Thing(other_thing=ot, name="t3")]
|
||||
# await Thing.objects.bulk_create(things)
|
||||
|
||||
|
||||
@app.get("/test/2", response_model=List[Thing])
|
||||
async def get_test_2():
|
||||
# if you only query for one use get or first
|
||||
ot = await OtherThing.objects.get()
|
||||
ts = await ot.things.all()
|
||||
# specifically null out the relation on things before return
|
||||
for t in ts:
|
||||
t.remove(ot, name="other_thing")
|
||||
return ts
|
||||
|
||||
|
||||
@app.get("/test/3", response_model=List[Thing])
|
||||
async def get_test_3():
|
||||
ot = await OtherThing.objects.select_related("things").get()
|
||||
# exclude unwanted field while ot is still in scope
|
||||
# in order not to pass it to fastapi
|
||||
return [t.dict(exclude={"other_thing"}) for t in ot.things]
|
||||
|
||||
|
||||
@app.get("/test/4", response_model=List[Thing], response_model_exclude={"other_thing"})
|
||||
async def get_test_4():
|
||||
ot = await OtherThing.objects.get()
|
||||
# query from the active side
|
||||
return await Thing.objects.all(other_thing=ot)
|
||||
|
||||
|
||||
@app.get("/get_ot/", response_model=OtherThing)
|
||||
async def get_ot():
|
||||
return await OtherThing.objects.get()
|
||||
|
||||
|
||||
# more real life (usually) is not getting some random OT and get it's Things
|
||||
# but query for a specific one by some kind of id
|
||||
@app.get(
|
||||
"/test/5/{thing_id}",
|
||||
response_model=List[Thing],
|
||||
response_model_exclude={"other_thing"},
|
||||
)
|
||||
async def get_test_5(thing_id: UUID):
|
||||
return await Thing.objects.all(other_thing__id=thing_id)
|
||||
|
||||
|
||||
def test_endpoints():
|
||||
client = TestClient(app)
|
||||
with client:
|
||||
resp = client.post("/test/1")
|
||||
assert resp.status_code == 200
|
||||
|
||||
resp2 = client.get("/test/2")
|
||||
assert resp2.status_code == 200
|
||||
assert len(resp2.json()) == 3
|
||||
|
||||
resp3 = client.get("/test/3")
|
||||
assert resp3.status_code == 200
|
||||
assert len(resp3.json()) == 3
|
||||
|
||||
resp4 = client.get("/test/4")
|
||||
assert resp4.status_code == 200
|
||||
assert len(resp4.json()) == 3
|
||||
|
||||
ot = OtherThing(**client.get("/get_ot/").json())
|
||||
resp5 = client.get(f"/test/5/{ot.id}")
|
||||
assert resp5.status_code == 200
|
||||
assert len(resp5.json()) == 3
|
||||
Reference in New Issue
Block a user