Skip to content

Commit

Permalink
Add more demo objects to the demo suite
Browse files Browse the repository at this point in the history
  • Loading branch information
uittenbroekrobbert committed Jul 3, 2024
1 parent a239a2e commit 4b2c3df
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 48 deletions.
7 changes: 7 additions & 0 deletions BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ When poetry is done installing all dependencies you can start using the tool.
poetry run python -m uvicorn tad.main:app --log-level warning
```

### Suggested development ENVIRONMENT settings
To use a demo environment during local development, you can use the following environment options. You can leave out the TRUNCATE_TABLES option if you wish to keep the state between runs.
```shell
export ENVIRONMENT=demo AUTO_CREATE_SCHEMA=true TRUNCATE_TABLES=true
```


## Database

We support most SQL database types. You can use the variable `APP_DATABASE_SCHEME` to change the database. The default scheme is sqlite.
Expand Down
1 change: 1 addition & 0 deletions tad/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Settings(BaseSettings):

DEBUG: bool = False
AUTO_CREATE_SCHEMA: bool = False
TRUNCATE_TABLES: bool = False

# todo(berry): create submodel for database settings
APP_DATABASE_SCHEME: DatabaseSchemaType = "sqlite"
Expand Down
65 changes: 48 additions & 17 deletions tad/core/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from sqlalchemy.engine import Engine
from sqlalchemy.pool import QueuePool, StaticPool
from sqlmodel import Session, SQLModel, create_engine, select
from sqlmodel import Session, SQLModel, create_engine, delete, select

from tad.core.config import get_settings
from tad.models import Status, Task, User
Expand Down Expand Up @@ -43,21 +43,52 @@ def init_db():

with Session(get_engine()) as session:
if get_settings().ENVIRONMENT == "demo":
logger.info("Creating demo data")
if get_settings().TRUNCATE_TABLES:
truncate_tables(session)

user = session.exec(select(User).where(User.name == "Robbert")).first()
if not user:
user = User(name="Robbert", avatar=None)
session.add(user)

status = session.exec(select(Status).where(Status.name == "Todo")).first()
if not status:
status = Status(name="Todo", sort_order=1)
session.add(status)

task = session.exec(select(Task).where(Task.title == "First task")).first()
if not task:
task = Task(title="First task", description="This is the first task", sort_order=1, status_id=status.id)
session.add(task)
session.commit()
logger.info("Creating demo data")
add_demo_users(session, ["default user"])
add_demo_statuses(session, ["todo", "review", "in_progress", "done"])
todo_status = session.exec(select(Status).where(Status.name == "todo")).first()
if todo_status is not None:
add_demo_tasks(session, todo_status, 3)
logger.info("Finished initializing database")


def truncate_tables(session: Session) -> None:
logger.info("Truncating tables")
session.exec(delete(Task)) # type: ignore
session.exec(delete(User)) # type: ignore
session.exec(delete(Status)) # type: ignore


def add_demo_users(session: Session, user_names: list[str]) -> None:
for user_name in user_names:
user = session.exec(select(User).where(User.name == user_name)).first()
if not user:
session.add(User(name=user_name, avatar=None))
session.commit()


def add_demo_tasks(session: Session, status: Status, number_of_tasks: int) -> None:
for index in range(1, number_of_tasks + 1):
title = "Example task " + str(index)
task = session.exec(select(Task).where(Task.title == title)).first()
if not task:
session.add(
Task(
title=title,
description="Example description " + str(index),
sort_order=index,
status_id=status.id,
)
)
session.commit()


def add_demo_statuses(session: Session, statuses: list[str]) -> None:
for index, status_name in enumerate(statuses):
status = session.exec(select(Status).where(Status.name == status_name)).first()
if not status:
session.add(Status(name=status_name, sort_order=index + 1))
session.commit()
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
status = op.create_table(
op.create_table(
"status",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
Expand Down Expand Up @@ -56,17 +56,6 @@ def upgrade() -> None:
)
# ### end Alembic commands ###

# ### custom commands ###
op.bulk_insert(
status,
[
{"name": "Todo", "sort_order": 1},
{"name": "In Progress", "sort_order": 2},
{"name": "Review", "sort_order": 3},
{"name": "Done", "sort_order": 4},
],
)


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
Expand Down
16 changes: 16 additions & 0 deletions tests/constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from sqlmodel import select
from sqlmodel.sql._expression_select_cls import SelectOfScalar
from tad.models import Status, Task, User


Expand Down Expand Up @@ -37,3 +39,17 @@ def default_task(
user_id: int | None = None,
) -> Task:
return Task(title=title, description=description, sort_order=sort_order, status_id=status_id, user_id=user_id)


def expected_selects_demo_suite() -> list[SelectOfScalar[User] | SelectOfScalar[Status] | SelectOfScalar[Task]]:
return [
select(User).where(User.name == "Robbert"),
select(Status).where(Status.name == "todo"),
select(Status).where(Status.name == "in_progress"),
select(Status).where(Status.name == "review"),
select(Status).where(Status.name == "done"),
select(Status).where(Status.name == "done"),
select(Task).where(Task.title == "Test task 1"),
select(Task).where(Task.title == "Test task 2"),
select(Task).where(Task.title == "Test task 3"),
]
84 changes: 65 additions & 19 deletions tests/core/test_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
from unittest.mock import MagicMock

import pytest
from sqlmodel import Session, select
from sqlmodel import Session, delete, select
from sqlmodel.sql._expression_select_cls import SelectOfScalar
from tad.core.config import Settings
from tad.core.db import check_db, init_db
from tad.models import Status, Task, User

from tests.constants import expected_selects_demo_suite

logger = logging.getLogger(__name__)


Expand All @@ -21,47 +24,90 @@ def test_check_database():


@pytest.mark.parametrize(
"patch_settings",
[{"ENVIRONMENT": "demo", "AUTO_CREATE_SCHEMA": True}],
indirect=True,
("patch_settings", "expected_selects"),
[({"ENVIRONMENT": "demo", "AUTO_CREATE_SCHEMA": True}, expected_selects_demo_suite())],
indirect=["patch_settings"],
)
def test_init_database_none(patch_settings: Settings):
def test_init_database_none(
patch_settings: Settings,
expected_selects: list[SelectOfScalar[User] | SelectOfScalar[Status] | SelectOfScalar[Task]],
):
org_exec = Session.exec
Session.exec = MagicMock()
Session.exec.return_value.first.return_value = None

init_db()

expected = [
(select(User).where(User.name == "Robbert"),),
(select(Status).where(Status.name == "Todo"),),
(select(Task).where(Task.title == "First task"),),
]
for i, call_args in enumerate(Session.exec.call_args_list):
assert str(expected_selects[i]) == str(call_args.args[0])

Session.exec = org_exec


@pytest.mark.parametrize(
("patch_settings", "expected_selects"),
[({"ENVIRONMENT": "demo", "AUTO_CREATE_SCHEMA": True}, expected_selects_demo_suite())],
indirect=["patch_settings"],
)
def test_init_database_none_with_todo_status(
patch_settings: Settings,
expected_selects: list[SelectOfScalar[User] | SelectOfScalar[Status] | SelectOfScalar[Task]],
):
org_exec = Session.exec
Session.exec = MagicMock()
todo_status = Status(id=1, name="todo", sort_order=1)
Session.exec.return_value.first.side_effect = [None, None, None, None, None, todo_status, None, None, None]

init_db()

for i, call_args in enumerate(Session.exec.call_args_list):
assert str(expected_selects[i]) == str(call_args.args[0])

Session.exec = org_exec


@pytest.mark.parametrize(
("patch_settings", "expected_selects"),
[({"ENVIRONMENT": "demo", "AUTO_CREATE_SCHEMA": True}, expected_selects_demo_suite())],
indirect=["patch_settings"],
)
def test_init_database(
patch_settings: Settings,
expected_selects: list[SelectOfScalar[User] | SelectOfScalar[Status] | SelectOfScalar[Task]],
):
org_exec = Session.exec
Session.exec = MagicMock()

init_db()

for i, call_args in enumerate(Session.exec.call_args_list):
assert str(expected[i][0]) == str(call_args.args[0])
assert str(expected_selects[i]) == str(call_args.args[0])

Session.exec = org_exec


@pytest.mark.parametrize(
"patch_settings",
[{"ENVIRONMENT": "demo", "AUTO_CREATE_SCHEMA": True}],
indirect=True,
("patch_settings", "expected_selects"),
[({"ENVIRONMENT": "demo", "AUTO_CREATE_SCHEMA": True, "TRUNCATE_TABLES": True}, expected_selects_demo_suite())],
indirect=["patch_settings"],
)
def test_init_database(patch_settings: Settings):
def test_truncate_database(
patch_settings: Settings,
expected_selects: list[SelectOfScalar[User] | SelectOfScalar[Status] | SelectOfScalar[Task]],
):
org_exec = Session.exec
Session.exec = MagicMock()

init_db()

expected = [
(select(User).where(User.name == "Robbert"),),
(select(Status).where(Status.name == "Todo"),),
(select(Task).where(Task.title == "First task"),),
delete(Task),
delete(User),
delete(Status),
*expected_selects,
]

for i, call_args in enumerate(Session.exec.call_args_list):
assert str(expected[i][0]) == str(call_args.args[0])
assert str(expected[i]) == str(call_args.args[0])

Session.exec = org_exec

0 comments on commit 4b2c3df

Please sign in to comment.