diff --git a/pyproject.toml b/pyproject.toml index f9c5d33..6356117 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "teilchensammler-cli" -version = "0.5.1" +version = "0.5.0" description = "Build up and maintain an inventory of electronics parts and tools." readme = "README.md" requires-python = ">=3.14,<4.0" diff --git a/src/teilchensammler_cli/database.py b/src/teilchensammler_cli/database.py index 2d9f888..b58456b 100644 --- a/src/teilchensammler_cli/database.py +++ b/src/teilchensammler_cli/database.py @@ -6,5 +6,5 @@ sqlite_url = os.environ.get("DATABASE_URL", "sqlite:///database.db") engine = create_engine(sqlite_url, echo=False) -def create_db_and_tables(engine): +def create_db_and_tables(): SQLModel.metadata.create_all(engine) diff --git a/src/teilchensammler_cli/main.py b/src/teilchensammler_cli/main.py index 9787807..42feda2 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, engine +from .database import create_db_and_tables from .models import add_to_database, load_initial_data, make_teilchen_input logging.basicConfig( @@ -26,7 +26,7 @@ TEILCHEN_DATA_HEADER = "pk Name Description Number Tags".split() class SammlerApp(App): async def on_mount(self) -> None: - create_db_and_tables(engine) + create_db_and_tables() self.push_screen(AddInventoryScreen()) @@ -68,7 +68,7 @@ class SearchBar(Static): event.input.value = "" - teilchen = await add_to_database(tc, engine) + teilchen = await add_to_database(tc) 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(engine)) + table.add_rows(await load_initial_data()) class AddInventoryScreen(Screen): diff --git a/src/teilchensammler_cli/models.py b/src/teilchensammler_cli/models.py index 4ed6afc..bca6c42 100644 --- a/src/teilchensammler_cli/models.py +++ b/src/teilchensammler_cli/models.py @@ -9,6 +9,7 @@ from sqlmodel import ( select, ) +from .database import engine logger = logging.getLogger(__name__) @@ -81,7 +82,8 @@ async def make_teilchen_input(text: str) -> TeilchenCreate | None: return teilchen -async def load_initial_data(engine) -> Sequence[Teilchen]: +async def load_initial_data() -> Sequence[Teilchen]: + from .database import engine with Session(engine) as session: statement = select( @@ -91,7 +93,7 @@ async def load_initial_data(engine) -> Sequence[Teilchen]: return all_teilchen -async def add_to_database(tc: TeilchenCreate, engine) -> Teilchen: +async def add_to_database(tc: TeilchenCreate) -> Teilchen: with Session(engine) as session: teilchen = Teilchen.model_validate(tc) session.add(teilchen) diff --git a/tests.py b/tests.py index 844823e..4eb3280 100644 --- a/tests.py +++ b/tests.py @@ -1,19 +1,6 @@ -from teilchensammler_cli.database import create_db_and_tables -from sqlalchemy.sql import text -import uuid -from typing import Generator - import pytest -from sqlalchemy import Engine -from sqlmodel import Session, SQLModel, create_engine -from teilchensammler_cli.models import ( - Teilchen, - TeilchenCreate, - add_to_database, - load_initial_data, - make_teilchen_input, -) +from teilchensammler_cli.models import TeilchenCreate, make_teilchen_input @pytest.mark.final # don't run while we are fiddling with the app @@ -23,7 +10,7 @@ def test_initial_layout(snap_compare): assert snap_compare(app, terminal_size=(130, 40)) -empty_teilchen_data = { +empty_teilchen = { "name": "", "description": "", "tags": "", @@ -45,7 +32,7 @@ def TC(**kwargs) -> TeilchenCreate | None: - an instance of `TeilchenCreate` """ if kwargs: - arguments = empty_teilchen_data | kwargs + arguments = empty_teilchen | kwargs return TeilchenCreate(**arguments) # ty:ignore[invalid-argument-type] @@ -59,15 +46,11 @@ 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"')), @@ -85,7 +68,6 @@ 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"? ], ) @@ -96,96 +78,3 @@ 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. - """ - - engine = create_engine("sqlite:///:memory:") - - yield engine - - engine.dispose() - - -@pytest.fixture(name="app_db") -def database_ready_to_use(engine: Engine) -> Generator[Engine]: - SQLModel.metadata.create_all(engine) - yield engine - - -@pytest.fixture(name="db_teilchen") -def teilchen_in_db(app_db: 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(app_db) as session: - session.add(teilchen) - session.commit() - session.refresh(teilchen) - - yield teilchen - - with Session(app_db) as session: - session.delete(teilchen) - session.commit() - - -async def test_loadinitialdata_returns_expected_data(app_db: Engine, db_teilchen: Teilchen): - all_data = await load_initial_data(app_db) - 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 - - -async def test_data_provided_to_addtodatabase_ends_up_in_database(app_db: Engine): - all_data = await load_initial_data(app_db) - assert len(all_data) == 0 - - teilchen = Teilchen( - id=uuid.uuid7(), name="test", description="test", tags="#test", number=1, text="test" - ) - - db_teilchen = await add_to_database(teilchen, app_db) - - assert teilchen == db_teilchen - - -async def test_created_database_contains_expected_tables(engine: Engine): - - statement = text("SELECT name from sqlite_schema WHERE type = 'table'") - with engine.connect() as connection: - results = connection.execute(statement).all() - assert len(results) == 0 - - create_db_and_tables(engine) - - results = connection.execute(statement).all() - assert len(results) == 1 diff --git a/uv.lock b/uv.lock index 4c6979b..0c0c8bc 100644 --- a/uv.lock +++ b/uv.lock @@ -1153,7 +1153,7 @@ wheels = [ [[package]] name = "teilchensammler-cli" -version = "0.5.1" +version = "0.5.0" source = { editable = "." } dependencies = [ { name = "ciso8601" },