mirror of
https://github.com/fastapi/sqlmodel.git
synced 2025-08-17 20:01:41 +08:00
![google-labs-jules[bot]](/assets/img/avatar_default.png)
This commit represents the completed code modifications for consolidating multiple version-specific test files (for Python 3.8, 3.9, 3.10) into single test files for nearly all documentation examples. **Summary of Actions:** 1. **Comprehensive File Identification:** I identified all test files in `tests/test_advanced` and `tests/test_tutorial` that followed the pattern of version-specific suffixes (e.g., `_py39.py`, `_py310.py`) for consolidation. 2. **Consolidation Implementation:** * My primary strategy involved modifying the base test file (e.g., `test_example.py`). * I introduced a `pytest` fixture, typically named `module`. This fixture is parametrized to load the base version of the example code and its Python version-specific variants from the `docs_src` directory using `importlib.import_module`. * I applied `needs_py39` and `needs_py310` marks from `tests.conftest` to the relevant parameters to control test execution based on the Python version. * I updated test functions to use this `module` fixture. For FastAPI examples, this included careful adaptation of `session` and `client` fixtures to use the parametrized module's `app` and `engine`, ensuring proper database setup (in-memory SQLite, table creation) and module reloading with `clear_sqlmodel` for isolation. * I used the `print_mock` fixture for tests verifying console output. Other tests used `sqlalchemy.inspect` or API response assertions. * I incorporated your feedback regarding the use of `from types import ModuleType` for type hints and removal of unnecessary comments into later consolidations. * I deleted redundant version-specific test files after their logic was merged. 3. **Skipped File:** I did not consolidate `tests/test_tutorial/test_insert/test_tutorial002.py` due to persistent `ImportError`/`AttributeError` issues when trying to access a dependent `Team` model from another tutorial's source file within the pytest fixture. Multiple approaches to resolve this failed, suggesting a complex interaction with module loading or metadata in the test environment for this specific case. 4. **Testing Limitations (CRITICAL):** * While I often ran tests for individual files or smaller directories successfully after consolidation, a persistent "The command affected too many files in the repo" error plagued testing of larger directories and the entire project. * This environment constraint ultimately **prevented me from executing the full test suite** after all code modifications were complete. Dependency installation (`pip install -r requirements.txt`) also failed due to this limit in the final stages. * **Therefore, the submitted code, while structurally complete according to my plan, is NOT FULLY TESTED.** There is a risk that consolidations in the later-processed, larger directories might contain unfound issues. **Conclusion:** The code refactoring to consolidate tests is (almost entirely) complete. However, due to critical environment limitations preventing full test suite verification, this submission should be reviewed with caution. Further testing in an unrestricted environment is highly recommended.
106 lines
4.0 KiB
Python
106 lines
4.0 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 create_engine, SQLModel # Added SQLModel for potential use if main doesn't create tables
|
|
|
|
from ...conftest import get_testing_print_function, needs_py310, PrintMock
|
|
|
|
|
|
@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)}"
|