"""Utilities for working with Art Relations DB in tests.""" from collections.abc import Callable, Generator, Sequence from contextlib import contextmanager from functools import wraps from typing import Any from unittest import mock from sqlalchemy import text from sqlalchemy.orm import DeclarativeBase from video.connectors import mysql def seed_models(models: Sequence[DeclarativeBase]) -> None: """Save the given model(s) to the DB. Args: models (list): list of model instances to save. """ with mysql.ar_db_session(turn_off_foreign_key_constraint=True) as session: for model in models: session.add(model) session.flush() # detach the objects from this session so tests can interrogate them for model in models: session.expunge(model) def test_schema(function: Callable[..., Any]) -> Callable[..., Any]: """Test schema. Decorator that creates the test DB schema before a function call and tears the schema down after the function call has finished. This just creates the schema and does not seed data. Individual test cases can use factories to seed data as needed. Args: function (func): function to be called after creating the test schema. Returns: function: The decorated function. """ @wraps(function) def call_function_within_db_context(*args: Any, **kwargs: Any) -> Any: try: function_return = function(*args, **kwargs) finally: with mysql.ar_db_session(turn_off_foreign_key_constraint=True) as session: for table_name in mysql.ArModel.metadata.tables: session.execute(text(f"TRUNCATE TABLE `{table_name}`")) return function_return return call_function_within_db_context def mock_db_session(mocker: Any) -> Any: """Create a mock database session. Also mock the ar_db_session context manager to use the mock session. """ mock_session = mock.Mock(query=mock.Mock()) @contextmanager def fake_session_manager( turn_off_foreign_key_constraint: bool = True, ) -> Generator[Any, None, None]: yield mock_session mocker.patch.object(mysql, "ar_db_session", fake_session_manager) return mock_session