-
-
Notifications
You must be signed in to change notification settings - Fork 16
Moved tests to using testcontainers #232
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1629117
f90cbf1
467b614
078fc07
bd2c356
a642e66
cac21e8
64c6067
f08d49d
29b09c7
2e8b265
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import contextlib | ||
| from collections.abc import Iterator | ||
|
|
||
| import pytest | ||
| from dotenv import load_dotenv | ||
| from fastapi.testclient import TestClient | ||
| from sqlalchemy import Connection, Engine | ||
|
|
||
| from database.setup import expdb_database, user_database | ||
| from main import create_api | ||
| from routers.dependencies import expdb_connection, userdb_connection | ||
|
|
||
| load_dotenv() | ||
|
|
||
|
|
||
| @contextlib.contextmanager | ||
| def automatic_rollback(engine: Engine) -> Iterator[Connection]: | ||
| with engine.connect() as connection: | ||
| transaction = connection.begin() | ||
| yield connection | ||
| if transaction.is_active: | ||
| transaction.rollback() | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def expdb_test() -> Connection: | ||
| with automatic_rollback(expdb_database()) as connection: | ||
| yield connection | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def user_test() -> Connection: | ||
| with automatic_rollback(user_database()) as connection: | ||
| yield connection | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def py_api(expdb_test: Connection, user_test: Connection) -> TestClient: | ||
| app = create_api() | ||
| # We use the lambda definitions because fixtures may not be called directly. | ||
| app.dependency_overrides[expdb_connection] = lambda: expdb_test | ||
| app.dependency_overrides[userdb_connection] = lambda: user_test | ||
| return TestClient(app) |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,99 @@ | ||||||||||||||||||||||||||||||||||
| import contextlib | ||||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||||
| import re | ||||||||||||||||||||||||||||||||||
| from collections.abc import Generator, Iterator | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import pytest | ||||||||||||||||||||||||||||||||||
| import sqlalchemy | ||||||||||||||||||||||||||||||||||
| from dotenv import load_dotenv | ||||||||||||||||||||||||||||||||||
| from fastapi.testclient import TestClient | ||||||||||||||||||||||||||||||||||
| from sqlalchemy import Connection, Engine | ||||||||||||||||||||||||||||||||||
| from testcontainers.mysql import LogMessageWaitStrategy, MySqlContainer | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| from main import create_api | ||||||||||||||||||||||||||||||||||
| from routers.dependencies import expdb_connection, userdb_connection | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| load_dotenv() | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @pytest.fixture(scope="session", autouse=True) | ||||||||||||||||||||||||||||||||||
| def override_testcontainers_connect() -> None: | ||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||
| Override MySqlContainer._connect once per test session. | ||||||||||||||||||||||||||||||||||
| Applied automatically everywhere. | ||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| def _connect(self: MySqlContainer) -> None: | ||||||||||||||||||||||||||||||||||
| wait_strategy = LogMessageWaitStrategy( | ||||||||||||||||||||||||||||||||||
| re.compile( | ||||||||||||||||||||||||||||||||||
| r".*: ready for connections", | ||||||||||||||||||||||||||||||||||
| flags=re.DOTALL | re.MULTILINE, | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| wait_strategy.wait_until_ready(self) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| MySqlContainer._connect = _connect # noqa: SLF001 | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @pytest.fixture(scope="session") | ||||||||||||||||||||||||||||||||||
| def mysql_container() -> MySqlContainer: | ||||||||||||||||||||||||||||||||||
| container = MySqlContainer( | ||||||||||||||||||||||||||||||||||
| os.environ.get( | ||||||||||||||||||||||||||||||||||
| "OPENML_DATABASES_OPENML_URL", | ||||||||||||||||||||||||||||||||||
| "openml/test-database:v0.1.20260204", | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| username=os.environ.get("OPENML_DATABASES_OPENML_USERNAME", ""), | ||||||||||||||||||||||||||||||||||
| password=os.environ.get("OPENML_DATABASES_OPENML_PASSWORD", ""), | ||||||||||||||||||||||||||||||||||
| dbname="openml_expdb", | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| container.start() | ||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||
| yield container | ||||||||||||||||||||||||||||||||||
| finally: | ||||||||||||||||||||||||||||||||||
| container.stop() | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @pytest.fixture | ||||||||||||||||||||||||||||||||||
| def expdb_test(mysql_container: MySqlContainer) -> Connection: | ||||||||||||||||||||||||||||||||||
| url = mysql_container.get_connection_url().replace("mysql://", "mysql+pymysql://") | ||||||||||||||||||||||||||||||||||
| engine = sqlalchemy.create_engine(url) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| with engine.begin() as connection: # This starts a transaction | ||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||
| yield connection | ||||||||||||||||||||||||||||||||||
| finally: | ||||||||||||||||||||||||||||||||||
| connection.rollback() # Rollback ALL test changes | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+57
to
+66
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Proposed fix `@pytest.fixture`
-def expdb_test(mysql_container: MySqlContainer) -> Connection:
+def expdb_test(mysql_container: MySqlContainer) -> Iterator[Connection]:
url = mysql_container.get_connection_url().replace("mysql://", "mysql+pymysql://")
engine = sqlalchemy.create_engine(url)
-
- with engine.begin() as connection: # This starts a transaction
- try:
- yield connection
- finally:
- connection.rollback() # Rollback ALL test changes
+ with automatic_rollback(engine) as connection:
+ yield connection📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @contextlib.contextmanager | ||||||||||||||||||||||||||||||||||
| def automatic_rollback(engine: Engine) -> Iterator[Connection]: | ||||||||||||||||||||||||||||||||||
| with engine.connect() as connection: | ||||||||||||||||||||||||||||||||||
| transaction = connection.begin() | ||||||||||||||||||||||||||||||||||
| yield connection | ||||||||||||||||||||||||||||||||||
| if transaction.is_active: | ||||||||||||||||||||||||||||||||||
| transaction.rollback() | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @pytest.fixture | ||||||||||||||||||||||||||||||||||
| def py_api(expdb_test: Connection, user_test: Connection) -> Generator[TestClient, None, None]: | ||||||||||||||||||||||||||||||||||
| app = create_api() | ||||||||||||||||||||||||||||||||||
| # We use the lambda definitions because fixtures may not be called directly. | ||||||||||||||||||||||||||||||||||
| app.dependency_overrides[expdb_connection] = lambda: expdb_test | ||||||||||||||||||||||||||||||||||
| app.dependency_overrides[userdb_connection] = lambda: user_test | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| client = TestClient(app) | ||||||||||||||||||||||||||||||||||
| yield client | ||||||||||||||||||||||||||||||||||
| client.close() | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @pytest.fixture | ||||||||||||||||||||||||||||||||||
| def user_test(mysql_container: MySqlContainer) -> Connection: | ||||||||||||||||||||||||||||||||||
| """Get a connection to the user database using the testcontainer.""" | ||||||||||||||||||||||||||||||||||
| url = mysql_container.get_connection_url() | ||||||||||||||||||||||||||||||||||
| url = url.replace("mysql://", "mysql+pymysql://") | ||||||||||||||||||||||||||||||||||
| url = url.replace("openml_expdb", "openml") | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| engine = sqlalchemy.create_engine(url) | ||||||||||||||||||||||||||||||||||
| with automatic_rollback(engine) as connection: | ||||||||||||||||||||||||||||||||||
| yield connection | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Plugin name mismatch —
tests.fixtures_containershould betests.fixtures_testcontainer.Line 33 imports
"tests.fixtures_container"but the actual file istests/fixtures_testcontainer.py. This will raise a module-not-found error whenever--use-test-containeris passed.Proposed fix
🤖 Prompt for AI Agents