From d205f4909e0b0a4dc96305fef383a8200401832d Mon Sep 17 00:00:00 2001 From: bronsen Date: Sun, 22 Feb 2026 18:08:39 +0100 Subject: [PATCH] test: ensure we read expected data from database --- src/teilchensammler_cli/main.py | 6 +-- src/teilchensammler_cli/models.py | 6 +-- tests.py | 85 +++++++++++++++++++++++++++++-- 3 files changed, 86 insertions(+), 11 deletions(-) diff --git a/src/teilchensammler_cli/main.py b/src/teilchensammler_cli/main.py index 42feda2..04c218d 100644 --- a/src/teilchensammler_cli/main.py +++ b/src/teilchensammler_cli/main.py @@ -12,7 +12,7 @@ from textual.screen import Screen from textual.widget import Widget from textual.widgets import Button, DataTable, Footer, Header, Input, Static -from .database import create_db_and_tables +from .database import create_db_and_tables, engine from .models import add_to_database, load_initial_data, make_teilchen_input logging.basicConfig( @@ -68,7 +68,7 @@ class SearchBar(Static): event.input.value = "" - teilchen = await add_to_database(tc) + teilchen = await add_to_database(tc, engine) self.screen.query_one(DataTable).add_row( teilchen.id, teilchen.name, teilchen.description, teilchen.number, teilchen.tags ) @@ -81,7 +81,7 @@ class SearchResults(Widget): async def on_mount(self) -> None: table: DataTable = self.query_one(DataTable) table.add_columns(*TEILCHEN_DATA_HEADER) - table.add_rows(await load_initial_data()) + table.add_rows(await load_initial_data(engine)) class AddInventoryScreen(Screen): diff --git a/src/teilchensammler_cli/models.py b/src/teilchensammler_cli/models.py index bca6c42..4ed6afc 100644 --- a/src/teilchensammler_cli/models.py +++ b/src/teilchensammler_cli/models.py @@ -9,7 +9,6 @@ from sqlmodel import ( select, ) -from .database import engine logger = logging.getLogger(__name__) @@ -82,8 +81,7 @@ async def make_teilchen_input(text: str) -> TeilchenCreate | None: return teilchen -async def load_initial_data() -> Sequence[Teilchen]: - from .database import engine +async def load_initial_data(engine) -> Sequence[Teilchen]: with Session(engine) as session: statement = select( @@ -93,7 +91,7 @@ async def load_initial_data() -> Sequence[Teilchen]: return all_teilchen -async def add_to_database(tc: TeilchenCreate) -> Teilchen: +async def add_to_database(tc: TeilchenCreate, engine) -> Teilchen: with Session(engine) as session: teilchen = Teilchen.model_validate(tc) session.add(teilchen) diff --git a/tests.py b/tests.py index 4eb3280..f035e5d 100644 --- a/tests.py +++ b/tests.py @@ -1,6 +1,15 @@ -import pytest +from typing import Generator, Sequence -from teilchensammler_cli.models import TeilchenCreate, make_teilchen_input +import pytest +from sqlalchemy import Engine +from sqlmodel import Session + +from teilchensammler_cli.models import ( + Teilchen, + TeilchenCreate, + load_initial_data, + make_teilchen_input, +) @pytest.mark.final # don't run while we are fiddling with the app @@ -10,7 +19,7 @@ def test_initial_layout(snap_compare): assert snap_compare(app, terminal_size=(130, 40)) -empty_teilchen = { +empty_teilchen_data = { "name": "", "description": "", "tags": "", @@ -32,7 +41,7 @@ def TC(**kwargs) -> TeilchenCreate | None: - an instance of `TeilchenCreate` """ if kwargs: - arguments = empty_teilchen | kwargs + arguments = empty_teilchen_data | kwargs return TeilchenCreate(**arguments) # ty:ignore[invalid-argument-type] @@ -46,11 +55,15 @@ def TC(**kwargs) -> TeilchenCreate | None: (".a.", TC()), ("a", TC()), ("aa", TC()), + (" .", TC()), # still an empty string # Just enough for "name" ("a.", TC(name="a", text="a.")), ("aa.", TC(name="aa", text="aa.")), ("a..", TC(name="a", text="a..")), ("a.b.", TC(name="a", text="a.b.")), + ("1.", TC(name="1", text="1.")), # numbers can be names + ("_.", TC(name="_", text="_.")), # underscores can be names + ("a b.", TC(name="a b", text="a b.")), # names can contain spaces # Just enough for "name" and "description" ('a."b"', TC(name="a", description="b", text='a."b"')), ('a. "b"', TC(name="a", description="b", text='a. "b"')), @@ -68,6 +81,7 @@ def TC(**kwargs) -> TeilchenCreate | None: ("a. ##b", TC(name="a", description="", tags="#b", text="a. ##b")), ("a. #", TC(name="a", description="", tags="", text="a. #")), ("a. ##", TC(name="a", description="", tags="", text="a. ##")), + ("a. #tag with spaces", TC(name="a", tags="#tag", text="a. #tag with spaces")), # do we care about "number"? ], ) @@ -78,3 +92,66 @@ async def test_maketeilcheninput_can_create_desired_teilchen( actual = await make_teilchen_input(example_input) assert expected == actual + + +@pytest.fixture(name="engine") +def in_memory_db() -> Generator[Engine]: + """Creates an in-memory sqlite database + + Yields: + The engine used to create the database. + """ + from sqlmodel import SQLModel, create_engine + + engine = create_engine("sqlite:///:memory:") + + SQLModel.metadata.create_all(engine) + yield engine + + engine.dispose() + + +@pytest.fixture(name="db_teilchen") +def teilchen_in_db(engine: Engine) -> Generator[Teilchen]: + """Creates a new Teilchen and stores it in the database. + + Args: + engine: an instance of sqlalchemy.Engine which was used to create the database. + + Yields: + The Teilchen refreshed from the database. + """ + import uuid + + teilchen = Teilchen( + id=uuid.uuid7(), name="TT", description="Test Teilchen", number=1, tags="", text="" + ) + with Session(engine) as session: + session.add(teilchen) + session.commit() + session.refresh(teilchen) + + yield teilchen + + with Session(engine) as session: + session.delete(teilchen) + session.commit() + + +async def test_loadinitialdata_returns_expected_data(engine: Engine, db_teilchen: Teilchen): + all_data: Sequence[tuple] = await load_initial_data(engine) + assert len(all_data) == 1 + + fetched_data: tuple = all_data[0] + teilchen_data: dict[str, str | int] = { + "id": fetched_data[0], + "name": fetched_data[1], + "description": fetched_data[2], + "number": 1, + "tags": "", + "text": "", + } + + fetched_teilchen = Teilchen.model_validate(teilchen_data) + + assert fetched_teilchen == db_teilchen