From 2feca115002648e2a381b0ed91c945129bf40235 Mon Sep 17 00:00:00 2001 From: Dan Cardin Date: Mon, 27 Jun 2022 18:25:59 -0400 Subject: [PATCH] fix: Address bug which fails to create schemas for MetaData beyond the first, in a series of ordered actions. --- pyproject.toml | 2 +- .../compat/sqlalchemy.py | 2 +- .../fixture/database/relational/generic.py | 10 +++--- .../fixture/database/relational/sqlite.py | 4 +-- .../fixture/database/test_ordered_actions.py | 36 +++++++++++++++++++ 5 files changed, 45 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ecf4bbcc..8a715de8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pytest-mock-resources" -version = "2.4.0" +version = "2.4.1" description = "A pytest plugin for easily instantiating reproducible mock resources." authors = [ "Omar Khan ", diff --git a/src/pytest_mock_resources/compat/sqlalchemy.py b/src/pytest_mock_resources/compat/sqlalchemy.py index d81bf8c3..889caa22 100644 --- a/src/pytest_mock_resources/compat/sqlalchemy.py +++ b/src/pytest_mock_resources/compat/sqlalchemy.py @@ -3,7 +3,7 @@ from pytest_mock_resources.compat.import_ import ImportAdaptor -version = sqlalchemy.__version__ +version = getattr(sqlalchemy, "__version__", "") if version.startswith("1.4") or version.startswith("2."): from sqlalchemy.ext import asyncio diff --git a/src/pytest_mock_resources/fixture/database/relational/generic.py b/src/pytest_mock_resources/fixture/database/relational/generic.py index 7100c8d6..3ce2dfb2 100644 --- a/src/pytest_mock_resources/fixture/database/relational/generic.py +++ b/src/pytest_mock_resources/fixture/database/relational/generic.py @@ -1,7 +1,7 @@ import abc import fnmatch -from dataclasses import dataclass -from typing import Iterable, List, Optional, Set, TypeVar, Union +from dataclasses import dataclass, field +from typing import Dict, Iterable, List, Optional, Set, TypeVar, Union from sqlalchemy import MetaData, text from sqlalchemy.engine import Engine @@ -90,7 +90,7 @@ class EngineManager: default_schema: Optional[str] = None static_actions: Iterable[StaticAction] = () - _ddl_created = False + _ddl_created: Dict[MetaData, bool] = field(default_factory=dict) @classmethod def create( @@ -174,7 +174,7 @@ def run_dynamic_actions(self, conn): self.execute_action(conn, action, allow_function=True) def _create_schemas(self, conn, metadata): - if self._ddl_created: + if metadata in self._ddl_created: return all_schemas = {table.schema for table in metadata.tables.values() if table.schema} @@ -205,7 +205,7 @@ def _create_tables(self, conn, metadata): def create_ddl(self, conn, metadata): self._create_schemas(conn, metadata) self._create_tables(conn, metadata) - self._ddl_created = True + self._ddl_created[metadata] = True def execute_action(self, conn, action, allow_function=False): if isinstance(action, MetaData): diff --git a/src/pytest_mock_resources/fixture/database/relational/sqlite.py b/src/pytest_mock_resources/fixture/database/relational/sqlite.py index 7b3dfafa..d10450e0 100644 --- a/src/pytest_mock_resources/fixture/database/relational/sqlite.py +++ b/src/pytest_mock_resources/fixture/database/relational/sqlite.py @@ -17,8 +17,7 @@ import warnings import pytest -from sqlalchemy import create_engine, event -from sqlalchemy.dialects import registry # type: ignore +from sqlalchemy import create_engine, dialects, event from sqlalchemy.dialects.postgresql import JSON, JSONB from sqlalchemy.dialects.sqlite import base as sqlite_base from sqlalchemy.dialects.sqlite.pysqlite import SQLiteDialect_pysqlite @@ -233,6 +232,7 @@ def create_sqlite_fixture( if postgres_like: dialect = make_postgres_like_sqlite_dialect() dialect_name = dialect.name + registry = getattr(dialects, "registry") registry.register("sqlite.{}".format(dialect_name), __name__, "PostgresLikeSQLitePDialect") driver_name = "sqlite+{}".format(dialect_name) diff --git a/tests/fixture/database/test_ordered_actions.py b/tests/fixture/database/test_ordered_actions.py index 54e068fe..573fec30 100644 --- a/tests/fixture/database/test_ordered_actions.py +++ b/tests/fixture/database/test_ordered_actions.py @@ -154,3 +154,39 @@ def test_non_template_database(non_template_database, run): execute = conn.execute(text("SELECT name FROM stuffs.user")).all() result = sorted([row[0] for row in execute]) assert ["Mug", "Perrier"] == result + + +class Test_multi_metadata_schemata: + """Assert that schemas are automatically created, given more than one input MetaData. + + Addresses bug which set test-state in such a way that only the first MetaData's + schemas were created. + """ + + Base = declarative_base() + + class Foo(Base): + __tablename__ = "foo" + __table_args__ = {"schema": "foo"} + + id = Column(Integer, primary_key=True, autoincrement=True) + + Base2 = declarative_base() + + class Bar(Base2): + __tablename__ = "bar" + __table_args__ = {"schema": "bar"} + + id = Column(Integer, primary_key=True, autoincrement=True) + + multi_metadata_schemata = create_postgres_fixture(Base, Base2) + + def test_creates_all_metadata_schemas(self, multi_metadata_schemata): + with multi_metadata_schemata.connect() as conn: + result = conn.execute(text("select * from foo.foo")).all() + assert len(result) == 0 + + result = conn.execute(text("select * from bar.bar")).all() + assert len(result) == 0 + # We dont need much in the way of assertions. You would see + # database errors from the nonexistence of these schemas/tables.