Files
2025-06-20 07:22:43 +00:00

120 lines
4.1 KiB
Python

import importlib
import sys
import types
from typing import Any
from unittest.mock import patch
import pytest
from sqlalchemy import inspect
from sqlalchemy.engine.reflection import Inspector
from sqlmodel import ( # Added SQLModel for potential use if main doesn't create tables
create_engine,
)
from ...conftest import PrintMock, get_testing_print_function, needs_py310
@pytest.fixture(
name="module",
params=[
"tutorial001",
pytest.param("tutorial001_py310", marks=needs_py310),
],
)
def get_module(
request: pytest.FixtureRequest, clear_sqlmodel: Any
): # clear_sqlmodel ensures fresh DB state
module_name = request.param
full_module_name = f"docs_src.tutorial.indexes.{module_name}"
if full_module_name in sys.modules:
mod = importlib.reload(sys.modules[full_module_name])
else:
mod = importlib.import_module(full_module_name)
# These tests usually define engine in their main() or globally.
# We'll ensure it's set up for the test a standard way.
mod.sqlite_url = "sqlite://"
mod.engine = create_engine(
mod.sqlite_url
) # connect_args not typically in these non-FastAPI examples
# Ensure tables are created. Some tutorials do it in main, others expect it externally.
# If mod.main() is expected to create tables, this might be redundant but safe.
# If Hero model is defined globally, SQLModel.metadata.create_all(mod.engine) can be used.
if hasattr(mod, "Hero") and hasattr(mod.Hero, "metadata"):
mod.Hero.metadata.create_all(mod.engine)
elif hasattr(mod, "SQLModel") and hasattr(
mod.SQLModel, "metadata"
): # Fallback if Hero specific metadata not found
mod.SQLModel.metadata.create_all(mod.engine)
return mod
def test_tutorial(print_mock: PrintMock, module: types.ModuleType):
# The engine is now set up by the fixture.
# clear_sqlmodel is handled by the fixture too.
# If main() also creates engine and tables, ensure it doesn't conflict.
# For these print-based tests, main() usually contains the core logic to be tested.
with patch("builtins.print", new=get_testing_print_function(print_mock.calls)):
module.main()
assert print_mock.calls == [
[{"secret_name": "Dive Wilson", "age": None, "id": 1, "name": "Deadpond"}]
]
insp: Inspector = inspect(module.engine)
# Ensure table name is correctly retrieved from the possibly reloaded module
table_name = str(module.Hero.__tablename__)
indexes = insp.get_indexes(table_name)
expected_indexes = [
{
"name": "ix_hero_name",
"dialect_options": {},
"column_names": ["name"],
"unique": 0,
},
{
"name": "ix_hero_age",
"dialect_options": {},
"column_names": ["age"],
"unique": 0,
},
]
# Convert list of dicts to list of tuples of items for easier comparison if order is not guaranteed
# For now, direct comparison with pop should work if the number of indexes is small and fixed.
found_indexes_simplified = []
for index in indexes:
found_indexes_simplified.append(
{
"name": index["name"],
"column_names": sorted(index["column_names"]), # Sort for consistency
"unique": index["unique"],
# Not including dialect_options as it can vary or be empty
}
)
expected_indexes_simplified = []
for index in expected_indexes:
expected_indexes_simplified.append(
{
"name": index["name"],
"column_names": sorted(index["column_names"]),
"unique": index["unique"],
}
)
for expected_index in expected_indexes_simplified:
assert expected_index in found_indexes_simplified, (
f"Expected index {expected_index['name']} not found or mismatch."
)
assert len(found_indexes_simplified) == len(expected_indexes_simplified), (
f"Mismatch in number of indexes. Found: {len(found_indexes_simplified)}, Expected: {len(expected_indexes_simplified)}"
)