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, ) @pytest.mark.final # don't run while we are fiddling with the app def test_initial_layout(snap_compare): from teilchensammler_cli.main import app assert snap_compare(app, terminal_size=(130, 40)) empty_teilchen_data = { "name": "", "description": "", "tags": "", "text": "", "number": 1, } def TC(**kwargs) -> TeilchenCreate | None: """TC = TeilchenCreate Create Teilchen with desired attributes. Args: input: mapping that must be well-formed enough to actually create a Teilchen Returns: - `None` on empty input - an instance of `TeilchenCreate` """ if kwargs: arguments = empty_teilchen_data | kwargs return TeilchenCreate(**arguments) # ty:ignore[invalid-argument-type] @pytest.mark.parametrize( "example_input,expected", [ # Not enough data ("", TC()), (".", TC()), ("..", TC()), (".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"')), ('a. ""', TC(name="a", description="", text='a. ""')), ('. ""', TC()), ('. "b"', TC()), # Just enough for "name" and "description" and "tags" ('a. "b" #c', TC(name="a", description="b", tags="#c", text='a. "b" #c')), ('a. "b #d" #c', TC(name="a", description="b #d", tags="#c #d", text='a. "b #d" #c')), # swap order in input, tag result is stable ('a. "b #c" #d', TC(name="a", description="b #c", tags="#c #d", text='a. "b #c" #d')), # Just enough for "name" and "tags" ("a. #d", TC(name="a", description="", tags="#d", text="a. #d")), ("a. #d#b", TC(name="a", description="", tags="#b #d", text="a. #d#b")), ("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"? ], ) async def test_maketeilcheninput_can_create_desired_teilchen( example_input: str, expected: TeilchenCreate | None ) -> None: 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