From 4f108982173bb79bf1907b47510d64e6b1caf3c7 Mon Sep 17 00:00:00 2001 From: Fabrizio Romano Date: Wed, 10 Dec 2025 10:52:59 +0000 Subject: [PATCH 01/14] Remove sqlalchemy-diff dependency. --- pyproject.toml | 1 - tests/db_util.py | 185 +++++++++++++++++++++++++++++++++++++++++++++++ tests/util.py | 2 +- tox.toml | 1 - uv.lock | 16 +--- 5 files changed, 187 insertions(+), 18 deletions(-) create mode 100644 tests/db_util.py diff --git a/pyproject.toml b/pyproject.toml index 75bdf3c..e65c22d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,6 @@ maintainers = [ keywords = ["sqlalchemy", "diff", "compare"] dependencies = [ "sqlalchemy>=1.4,<3", - "sqlalchemy-utils>=0.40.0,!=0.42.0", ] [project.optional-dependencies] diff --git a/tests/db_util.py b/tests/db_util.py new file mode 100644 index 0000000..af1e42e --- /dev/null +++ b/tests/db_util.py @@ -0,0 +1,185 @@ +"""Database utility functions for PostgreSQL and SQLite. + +These functions replace sqlalchemy-utils functionality for test purposes. +""" + +import os +from urllib.parse import urlparse + +from sqlalchemy import create_engine, text + + +def _parse_database_uri(uri): + """Parse a database URI and return components.""" + parsed = urlparse(uri) + return { + "scheme": parsed.scheme, + "username": parsed.username, + "password": parsed.password, + "hostname": parsed.hostname, + "port": parsed.port, + "database": parsed.path.lstrip("/") if parsed.path else None, + "path": parsed.path, + } + + +def _get_postgres_admin_uri(uri): + """Get a PostgreSQL URI pointing to the 'postgres' database for admin operations.""" + parsed = _parse_database_uri(uri) + # Connect to 'postgres' database instead of the target database + admin_uri = ( + f"postgresql://{parsed['username']}:{parsed['password']}@" + f"{parsed['hostname']}:{parsed['port']}/postgres" + ) + return admin_uri + + +def _get_sqlite_file_path(uri): + """Extract the file path from a SQLite URI.""" + parsed = _parse_database_uri(uri) + path = parsed["path"] + + # Handle :memory: and empty path + if not path or path == "/:memory:": + return None + + # Remove leading slash + if path.startswith("/"): + path = path[1:] + + return path + + +def database_exists(uri): + """Check if a database exists. + + Args: + uri: Database URI (postgresql://... or sqlite:///...) + + Returns: + bool: True if database exists, False otherwise + """ + parsed = _parse_database_uri(uri) + scheme = parsed["scheme"] + + if scheme == "postgresql": + admin_uri = _get_postgres_admin_uri(uri) + target_db = parsed["database"] + + engine = create_engine(admin_uri, isolation_level="AUTOCOMMIT") + try: + with engine.connect() as conn: + result = conn.execute( + text( + "SELECT 1 FROM pg_database WHERE datname = :dbname" + ), + {"dbname": target_db} + ) + return result.fetchone() is not None + finally: + engine.dispose() + + elif scheme == "sqlite": + file_path = _get_sqlite_file_path(uri) + + # In-memory databases always "exist" (they're created on connection) + if file_path is None: + return True + + # For file-based SQLite, check if file exists + return os.path.exists(file_path) + + else: + raise ValueError(f"Unsupported database scheme: {scheme}") + + +def create_database(uri): + """Create a database. + + Args: + uri: Database URI (postgresql://... or sqlite:///...) + """ + parsed = _parse_database_uri(uri) + scheme = parsed["scheme"] + + if scheme == "postgresql": + admin_uri = _get_postgres_admin_uri(uri) + target_db = parsed["database"] + + engine = create_engine(admin_uri, isolation_level="AUTOCOMMIT") + try: + with engine.connect() as conn: + # Database should not exist since create_db() calls drop_db() first + conn.execute(text(f'CREATE DATABASE "{target_db}"')) + finally: + engine.dispose() + + elif scheme == "sqlite": + file_path = _get_sqlite_file_path(uri) + + # In-memory databases don't need creation + if file_path is None: + return + + # For file-based SQLite, ensure the directory exists + dir_path = os.path.dirname(file_path) + if dir_path and not os.path.exists(dir_path): + os.makedirs(dir_path, exist_ok=True) + + # Create an empty file to "create" the database + # SQLite will create the file on first connection, but we'll touch it here + if not os.path.exists(file_path): + with open(file_path, "a"): + pass + + else: + raise ValueError(f"Unsupported database scheme: {scheme}") + + +def drop_database(uri): + """Drop a database. + + Args: + uri: Database URI (postgresql://... or sqlite:///...) + """ + parsed = _parse_database_uri(uri) + scheme = parsed["scheme"] + + if scheme == "postgresql": + admin_uri = _get_postgres_admin_uri(uri) + target_db = parsed["database"] + + engine = create_engine(admin_uri, isolation_level="AUTOCOMMIT") + try: + with engine.connect() as conn: + # Terminate any existing connections to the database + conn.execute( + text( + """ + SELECT pg_terminate_backend(pg_stat_activity.pid) + FROM pg_stat_activity + WHERE pg_stat_activity.datname = :dbname + AND pid <> pg_backend_pid() + """ + ), + {"dbname": target_db} + ) + # Drop the database + conn.execute(text(f'DROP DATABASE IF EXISTS "{target_db}"')) + finally: + engine.dispose() + + elif scheme == "sqlite": + file_path = _get_sqlite_file_path(uri) + + # In-memory databases don't need dropping + if file_path is None: + return + + # For file-based SQLite, delete the file + if os.path.exists(file_path): + os.remove(file_path) + + else: + raise ValueError(f"Unsupported database scheme: {scheme}") + diff --git a/tests/util.py b/tests/util.py index 6820d0a..3136a87 100644 --- a/tests/util.py +++ b/tests/util.py @@ -3,7 +3,7 @@ from sqlalchemy import create_engine from sqlalchemy.engine import Engine from sqlalchemy.orm.decl_api import DeclarativeMeta -from sqlalchemy_utils import create_database, database_exists, drop_database +from tests.db_util import create_database, database_exists, drop_database from tests import assert_items_equal diff --git a/tox.toml b/tox.toml index 48757b1..9627a7a 100644 --- a/tox.toml +++ b/tox.toml @@ -36,7 +36,6 @@ description = "Run unit tests with Python {basepython} and sqlalchemy V1.4" skip_install = true deps = [ "sqlalchemy~=1.4", - "sqlalchemy-utils>=0.40.0,!=0.42.0", "pytest~=9.0.1", "pytest-cov~=7.0.0", "psycopg2-binary", diff --git a/uv.lock b/uv.lock index c952457..22c77c7 100644 --- a/uv.lock +++ b/uv.lock @@ -515,7 +515,7 @@ name = "exceptiongroup" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } wheels = [ @@ -2019,7 +2019,6 @@ version = "1.0.4" source = { editable = "." } dependencies = [ { name = "sqlalchemy" }, - { name = "sqlalchemy-utils" }, ] [package.optional-dependencies] @@ -2062,23 +2061,10 @@ requires-dist = [ { name = "ruff", marker = "extra == 'lint'", specifier = "~=0.14.1" }, { name = "sqlalchemy", specifier = ">=1.4,<3" }, { name = "sqlalchemy-diff", extras = ["dev", "lint", "test"], marker = "extra == 'all'" }, - { name = "sqlalchemy-utils", specifier = ">=0.40.0,!=0.42.0" }, { name = "ty", marker = "extra == 'lint'" }, ] provides-extras = ["dev", "lint", "test", "all"] -[[package]] -name = "sqlalchemy-utils" -version = "0.41.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sqlalchemy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4d/bf/abfd5474cdd89ddd36dbbde9c6efba16bfa7f5448913eba946fed14729da/SQLAlchemy-Utils-0.41.2.tar.gz", hash = "sha256:bc599c8c3b3319e53ce6c5c3c471120bd325d0071fb6f38a10e924e3d07b9990", size = 138017, upload-time = "2024-03-24T15:17:28.196Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/f0/dc4757b83ac1ab853cf222df8535ed73973e0c203d983982ba7b8bc60508/SQLAlchemy_Utils-0.41.2-py3-none-any.whl", hash = "sha256:85cf3842da2bf060760f955f8467b87983fb2e30f1764fd0e24a48307dc8ec6e", size = 93083, upload-time = "2024-03-24T15:17:24.533Z" }, -] - [[package]] name = "stack-data" version = "0.6.3" From c5ee8cbcec2bdbe282763bbaf9a035124768f907 Mon Sep 17 00:00:00 2001 From: Fabrizio Romano Date: Wed, 10 Dec 2025 10:55:20 +0000 Subject: [PATCH 02/14] Use pathlib. --- tests/db_util.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/tests/db_util.py b/tests/db_util.py index af1e42e..58c1931 100644 --- a/tests/db_util.py +++ b/tests/db_util.py @@ -3,7 +3,7 @@ These functions replace sqlalchemy-utils functionality for test purposes. """ -import os +from pathlib import Path from urllib.parse import urlparse from sqlalchemy import create_engine, text @@ -35,7 +35,7 @@ def _get_postgres_admin_uri(uri): def _get_sqlite_file_path(uri): - """Extract the file path from a SQLite URI.""" + """Extract the file path from a SQLite URI as a Path object.""" parsed = _parse_database_uri(uri) path = parsed["path"] @@ -47,7 +47,7 @@ def _get_sqlite_file_path(uri): if path.startswith("/"): path = path[1:] - return path + return Path(path) def database_exists(uri): @@ -87,7 +87,7 @@ def database_exists(uri): return True # For file-based SQLite, check if file exists - return os.path.exists(file_path) + return file_path.exists() else: raise ValueError(f"Unsupported database scheme: {scheme}") @@ -122,15 +122,13 @@ def create_database(uri): return # For file-based SQLite, ensure the directory exists - dir_path = os.path.dirname(file_path) - if dir_path and not os.path.exists(dir_path): - os.makedirs(dir_path, exist_ok=True) + if file_path.parent: + file_path.parent.mkdir(parents=True, exist_ok=True) # Create an empty file to "create" the database # SQLite will create the file on first connection, but we'll touch it here - if not os.path.exists(file_path): - with open(file_path, "a"): - pass + if not file_path.exists(): + file_path.touch() else: raise ValueError(f"Unsupported database scheme: {scheme}") @@ -177,8 +175,8 @@ def drop_database(uri): return # For file-based SQLite, delete the file - if os.path.exists(file_path): - os.remove(file_path) + if file_path.exists(): + file_path.unlink() else: raise ValueError(f"Unsupported database scheme: {scheme}") From 42154050f7c288ea398486acd372c0441f5dd9b8 Mon Sep 17 00:00:00 2001 From: Fabrizio Romano Date: Wed, 10 Dec 2025 11:06:00 +0000 Subject: [PATCH 03/14] Add comprehensive tests for db_util functions and improve URI handling - Introduced a new test suite for the db_util module, covering functions such as _get_postgres_admin_uri, _get_sqlite_file_path, create_database, and database_exists. - Enhanced _get_postgres_admin_uri to handle cases where the port is not specified. - Updated _get_sqlite_file_path to return None for empty paths and improved handling of edge cases. --- tests/db_util.py | 9 +- tests/test_db_util.py | 501 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 508 insertions(+), 2 deletions(-) create mode 100644 tests/test_db_util.py diff --git a/tests/db_util.py b/tests/db_util.py index 58c1931..2fc89c8 100644 --- a/tests/db_util.py +++ b/tests/db_util.py @@ -27,9 +27,10 @@ def _get_postgres_admin_uri(uri): """Get a PostgreSQL URI pointing to the 'postgres' database for admin operations.""" parsed = _parse_database_uri(uri) # Connect to 'postgres' database instead of the target database + port_part = f":{parsed['port']}" if parsed['port'] is not None else "" admin_uri = ( f"postgresql://{parsed['username']}:{parsed['password']}@" - f"{parsed['hostname']}:{parsed['port']}/postgres" + f"{parsed['hostname']}{port_part}/postgres" ) return admin_uri @@ -40,13 +41,17 @@ def _get_sqlite_file_path(uri): path = parsed["path"] # Handle :memory: and empty path - if not path or path == "/:memory:": + if not path or path == "/:memory:" or path == "/": return None # Remove leading slash if path.startswith("/"): path = path[1:] + # Handle empty string after stripping + if not path: + return None + return Path(path) diff --git a/tests/test_db_util.py b/tests/test_db_util.py new file mode 100644 index 0000000..dba7bf1 --- /dev/null +++ b/tests/test_db_util.py @@ -0,0 +1,501 @@ +"""Comprehensive test suite for tests.db_util module.""" + +import uuid +from pathlib import Path + +import pytest +from sqlalchemy.exc import ProgrammingError + +from tests.db_util import ( + _get_postgres_admin_uri, + _get_sqlite_file_path, + _parse_database_uri, + create_database, + database_exists, + drop_database, +) + + +class TestParseDatabaseUri: + """Test the _parse_database_uri helper function.""" + + def test_parse_postgresql_uri(self): + """Test parsing a PostgreSQL URI.""" + uri = "postgresql://user:pass@localhost:5432/mydb" + result = _parse_database_uri(uri) + + assert result["scheme"] == "postgresql" + assert result["username"] == "user" + assert result["password"] == "pass" + assert result["hostname"] == "localhost" + assert result["port"] == 5432 + assert result["database"] == "mydb" + assert result["path"] == "/mydb" + + def test_parse_postgresql_uri_no_port(self): + """Test parsing a PostgreSQL URI without port.""" + uri = "postgresql://user:pass@localhost/mydb" + result = _parse_database_uri(uri) + + assert result["scheme"] == "postgresql" + assert result["username"] == "user" + assert result["password"] == "pass" + assert result["hostname"] == "localhost" + assert result["port"] is None + assert result["database"] == "mydb" + + def test_parse_sqlite_memory_uri(self): + """Test parsing a SQLite in-memory URI.""" + uri = "sqlite:///:memory:" + result = _parse_database_uri(uri) + + assert result["scheme"] == "sqlite" + assert result["username"] is None + assert result["password"] is None + assert result["hostname"] is None + assert result["port"] is None + assert result["database"] == ":memory:" # lstrip("/") removes leading slash + assert result["path"] == "/:memory:" + + def test_parse_sqlite_file_uri(self): + """Test parsing a SQLite file URI.""" + uri = "sqlite:///path/to/database.db" + result = _parse_database_uri(uri) + + assert result["scheme"] == "sqlite" + assert result["path"] == "/path/to/database.db" + assert result["database"] == "path/to/database.db" + + +class TestGetPostgresAdminUri: + """Test the _get_postgres_admin_uri helper function.""" + + def test_get_postgres_admin_uri(self): + """Test generating admin URI for PostgreSQL.""" + uri = "postgresql://user:pass@localhost:5432/mydb" + admin_uri = _get_postgres_admin_uri(uri) + + assert admin_uri == "postgresql://user:pass@localhost:5432/postgres" + + def test_get_postgres_admin_uri_no_port(self): + """Test generating admin URI without port.""" + uri = "postgresql://user:pass@localhost/mydb" + admin_uri = _get_postgres_admin_uri(uri) + + assert admin_uri == "postgresql://user:pass@localhost/postgres" + + +class TestGetSqliteFilePath: + """Test the _get_sqlite_file_path helper function.""" + + def test_get_sqlite_file_path_memory(self): + """Test extracting path from in-memory SQLite URI.""" + uri = "sqlite:///:memory:" + result = _get_sqlite_file_path(uri) + + assert result is None + + def test_get_sqlite_file_path_empty(self): + """Test extracting path from empty SQLite URI.""" + uri = "sqlite:///" + result = _get_sqlite_file_path(uri) + + assert result is None + + def test_get_sqlite_file_path_absolute(self): + """Test extracting absolute path from SQLite URI.""" + uri = "sqlite:///path/to/database.db" + result = _get_sqlite_file_path(uri) + + assert isinstance(result, Path) + assert result == Path("path/to/database.db") + + def test_get_sqlite_file_path_relative(self): + """Test extracting relative path from SQLite URI.""" + uri = "sqlite:///database.db" + result = _get_sqlite_file_path(uri) + + assert isinstance(result, Path) + assert result == Path("database.db") + + +class TestDatabaseExists: + """Test the database_exists function.""" + + @pytest.fixture + def unique_db_name(self): + """Generate a unique database name for testing.""" + return f"test_db_exists_{uuid.uuid4().hex[:8]}" + + @pytest.fixture + def postgres_uri(self, make_postgres_uri, unique_db_name): + """Create a PostgreSQL URI for testing.""" + return make_postgres_uri(unique_db_name) + + def test_database_exists_postgresql_nonexistent(self, postgres_uri): + """Test checking non-existent PostgreSQL database.""" + assert database_exists(postgres_uri) is False + + def test_database_exists_postgresql_existent(self, postgres_uri): + """Test checking existing PostgreSQL database.""" + # Create the database first + create_database(postgres_uri) + try: + assert database_exists(postgres_uri) is True + finally: + drop_database(postgres_uri) + + def test_database_exists_sqlite_memory(self): + """Test checking in-memory SQLite database (always exists).""" + uri = "sqlite:///:memory:" + assert database_exists(uri) is True + + def test_database_exists_sqlite_file_nonexistent(self, tmp_path): + """Test checking non-existent SQLite file database.""" + db_file = tmp_path / "nonexistent.db" + uri = f"sqlite:///{db_file}" + assert database_exists(uri) is False + + def test_database_exists_sqlite_file_existent(self, tmp_path): + """Test checking existing SQLite file database.""" + db_file = tmp_path / "existing.db" + db_file.touch() + uri = f"sqlite:///{db_file}" + assert database_exists(uri) is True + + def test_database_exists_unsupported_scheme(self): + """Test that unsupported schemes raise ValueError.""" + uri = "mysql://user:pass@localhost/db" + with pytest.raises(ValueError, match="Unsupported database scheme: mysql"): + database_exists(uri) + + +class TestCreateDatabase: + """Test the create_database function.""" + + @pytest.fixture + def unique_db_name(self): + """Generate a unique database name for testing.""" + return f"test_db_create_{uuid.uuid4().hex[:8]}" + + @pytest.fixture + def postgres_uri(self, make_postgres_uri, unique_db_name): + """Create a PostgreSQL URI for testing.""" + return make_postgres_uri(unique_db_name) + + def test_create_database_postgresql(self, postgres_uri): + """Test creating a PostgreSQL database.""" + # Ensure it doesn't exist first + if database_exists(postgres_uri): + drop_database(postgres_uri) + + create_database(postgres_uri) + try: + assert database_exists(postgres_uri) is True + finally: + drop_database(postgres_uri) + + def test_create_database_postgresql_already_exists(self, postgres_uri): + """Test creating a PostgreSQL database that already exists.""" + # Create the database first + if not database_exists(postgres_uri): + create_database(postgres_uri) + + # PostgreSQL will raise a ProgrammingError when trying to create existing database + with pytest.raises(ProgrammingError): + create_database(postgres_uri) + + # Cleanup + drop_database(postgres_uri) + + def test_create_database_sqlite_memory(self): + """Test creating in-memory SQLite database (no-op).""" + uri = "sqlite:///:memory:" + # Should not raise an error + create_database(uri) + assert database_exists(uri) is True + + def test_create_database_sqlite_file(self, tmp_path): + """Test creating a SQLite file database.""" + db_file = tmp_path / "new_database.db" + uri = f"sqlite:///{db_file}" + + assert not db_file.exists() + create_database(uri) + assert db_file.exists() + assert database_exists(uri) is True + + # Cleanup + db_file.unlink() + + def test_create_database_sqlite_file_with_subdirectory(self, tmp_path): + """Test creating a SQLite file database in a subdirectory.""" + subdir = tmp_path / "subdir" + db_file = subdir / "database.db" + uri = f"sqlite:///{db_file}" + + assert not subdir.exists() + assert not db_file.exists() + + create_database(uri) + + assert subdir.exists() + assert db_file.exists() + assert database_exists(uri) is True + + # Cleanup + db_file.unlink() + subdir.rmdir() + + def test_create_database_sqlite_file_already_exists(self, tmp_path): + """Test creating a SQLite file database that already exists.""" + db_file = tmp_path / "existing.db" + db_file.touch() + uri = f"sqlite:///{db_file}" + + # Should not raise an error, just touch the file + create_database(uri) + assert db_file.exists() + + # Cleanup + db_file.unlink() + + def test_create_database_unsupported_scheme(self): + """Test that unsupported schemes raise ValueError.""" + uri = "mysql://user:pass@localhost/db" + with pytest.raises(ValueError, match="Unsupported database scheme: mysql"): + create_database(uri) + + +class TestDropDatabase: + """Test the drop_database function.""" + + @pytest.fixture + def unique_db_name(self): + """Generate a unique database name for testing.""" + return f"test_db_drop_{uuid.uuid4().hex[:8]}" + + @pytest.fixture + def postgres_uri(self, make_postgres_uri, unique_db_name): + """Create a PostgreSQL URI for testing.""" + return make_postgres_uri(unique_db_name) + + def test_drop_database_postgresql_existent(self, postgres_uri): + """Test dropping an existing PostgreSQL database.""" + # Create the database first + if not database_exists(postgres_uri): + create_database(postgres_uri) + + assert database_exists(postgres_uri) is True + drop_database(postgres_uri) + assert database_exists(postgres_uri) is False + + def test_drop_database_postgresql_nonexistent(self, postgres_uri): + """Test dropping a non-existent PostgreSQL database.""" + # Ensure it doesn't exist + if database_exists(postgres_uri): + drop_database(postgres_uri) + + # Should not raise an error (uses IF EXISTS) + drop_database(postgres_uri) + assert database_exists(postgres_uri) is False + + def test_drop_database_postgresql_with_connections(self, postgres_uri): + """Test dropping a PostgreSQL database that has active connections.""" + # Create the database + if not database_exists(postgres_uri): + create_database(postgres_uri) + + # Create a connection to the database + from sqlalchemy import create_engine + + engine = create_engine(postgres_uri) + conn = engine.connect() + + try: + # Drop should terminate connections and succeed + drop_database(postgres_uri) + assert database_exists(postgres_uri) is False + finally: + conn.close() + engine.dispose() + + def test_drop_database_sqlite_memory(self): + """Test dropping in-memory SQLite database (no-op).""" + uri = "sqlite:///:memory:" + # Should not raise an error + drop_database(uri) + assert database_exists(uri) is True # Still exists (in-memory) + + def test_drop_database_sqlite_file_existent(self, tmp_path): + """Test dropping an existing SQLite file database.""" + db_file = tmp_path / "to_drop.db" + db_file.touch() + uri = f"sqlite:///{db_file}" + + assert db_file.exists() + drop_database(uri) + assert not db_file.exists() + assert database_exists(uri) is False + + def test_drop_database_sqlite_file_nonexistent(self, tmp_path): + """Test dropping a non-existent SQLite file database.""" + db_file = tmp_path / "nonexistent.db" + uri = f"sqlite:///{db_file}" + + assert not db_file.exists() + # Should not raise an error + drop_database(uri) + assert not db_file.exists() + + def test_drop_database_unsupported_scheme(self): + """Test that unsupported schemes raise ValueError.""" + uri = "mysql://user:pass@localhost/db" + with pytest.raises(ValueError, match="Unsupported database scheme: mysql"): + drop_database(uri) + + +class TestDatabaseLifecycle: + """Test complete database lifecycle operations.""" + + @pytest.fixture + def unique_db_name(self): + """Generate a unique database name for testing.""" + return f"test_db_lifecycle_{uuid.uuid4().hex[:8]}" + + @pytest.fixture + def postgres_uri(self, make_postgres_uri, unique_db_name): + """Create a PostgreSQL URI for testing.""" + return make_postgres_uri(unique_db_name) + + def test_postgresql_lifecycle(self, postgres_uri): + """Test complete PostgreSQL database lifecycle.""" + # Start: database should not exist + assert database_exists(postgres_uri) is False + + # Create database + create_database(postgres_uri) + assert database_exists(postgres_uri) is True + + # Drop database + drop_database(postgres_uri) + assert database_exists(postgres_uri) is False + + # Create again + create_database(postgres_uri) + assert database_exists(postgres_uri) is True + + # Final cleanup + drop_database(postgres_uri) + assert database_exists(postgres_uri) is False + + def test_sqlite_file_lifecycle(self, tmp_path): + """Test complete SQLite file database lifecycle.""" + db_file = tmp_path / "lifecycle.db" + uri = f"sqlite:///{db_file}" + + # Start: database should not exist + assert database_exists(uri) is False + + # Create database + create_database(uri) + assert database_exists(uri) is True + assert db_file.exists() + + # Drop database + drop_database(uri) + assert database_exists(uri) is False + assert not db_file.exists() + + # Create again + create_database(uri) + assert database_exists(uri) is True + assert db_file.exists() + + # Final cleanup + drop_database(uri) + assert database_exists(uri) is False + assert not db_file.exists() + + def test_sqlite_memory_lifecycle(self): + """Test SQLite in-memory database lifecycle.""" + uri = "sqlite:///:memory:" + + # In-memory always exists + assert database_exists(uri) is True + + # Create is a no-op + create_database(uri) + assert database_exists(uri) is True + + # Drop is a no-op + drop_database(uri) + assert database_exists(uri) is True + + +class TestDatabaseOperationsEdgeCases: + """Test edge cases and error conditions.""" + + @pytest.fixture + def unique_db_name(self): + """Generate a unique database name for testing.""" + return f"test_db_edge_{uuid.uuid4().hex[:8]}" + + @pytest.fixture + def postgres_uri(self, make_postgres_uri, unique_db_name): + """Create a PostgreSQL URI for testing.""" + return make_postgres_uri(unique_db_name) + + def test_postgresql_database_name_with_special_chars(self, make_postgres_uri): + """Test PostgreSQL database with special characters in name.""" + # PostgreSQL allows quoted identifiers, but we should test our quoting + db_name = f"test_db_{uuid.uuid4().hex[:8]}" + uri = make_postgres_uri(db_name) + + create_database(uri) + try: + assert database_exists(uri) is True + finally: + drop_database(uri) + + def test_sqlite_file_path_with_spaces(self, tmp_path): + """Test SQLite file path with spaces.""" + db_file = tmp_path / "database with spaces.db" + uri = f"sqlite:///{db_file}" + + create_database(uri) + assert db_file.exists() + assert database_exists(uri) is True + + drop_database(uri) + assert not db_file.exists() + + def test_sqlite_file_path_unicode(self, tmp_path): + """Test SQLite file path with unicode characters.""" + db_file = tmp_path / "数据库.db" + uri = f"sqlite:///{db_file}" + + create_database(uri) + assert db_file.exists() + assert database_exists(uri) is True + + drop_database(uri) + assert not db_file.exists() + + def test_multiple_operations_sequence(self, postgres_uri): + """Test multiple create/drop operations in sequence.""" + # Create and drop multiple times + for _ in range(3): + create_database(postgres_uri) + assert database_exists(postgres_uri) is True + drop_database(postgres_uri) + assert database_exists(postgres_uri) is False + + def test_sqlite_empty_path(self): + """Test SQLite URI with empty path.""" + uri = "sqlite:///" + # Should be treated as in-memory + assert database_exists(uri) is True + create_database(uri) # Should be no-op + drop_database(uri) # Should be no-op + From 1bf9f816156ba6508788b25e226c59ae736fcfec Mon Sep 17 00:00:00 2001 From: Fabrizio Romano Date: Wed, 10 Dec 2025 11:10:20 +0000 Subject: [PATCH 04/14] Refactor database existence assertions in tests for clarity - Simplified assertions in the test suite for database existence by removing explicit comparisons to True/False, enhancing readability. - Updated tests for PostgreSQL and SQLite to use more concise assertion styles. --- tests/test_db_util.py | 68 +++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/tests/test_db_util.py b/tests/test_db_util.py index dba7bf1..5959caf 100644 --- a/tests/test_db_util.py +++ b/tests/test_db_util.py @@ -134,34 +134,34 @@ def postgres_uri(self, make_postgres_uri, unique_db_name): def test_database_exists_postgresql_nonexistent(self, postgres_uri): """Test checking non-existent PostgreSQL database.""" - assert database_exists(postgres_uri) is False + assert not database_exists(postgres_uri) def test_database_exists_postgresql_existent(self, postgres_uri): """Test checking existing PostgreSQL database.""" # Create the database first create_database(postgres_uri) try: - assert database_exists(postgres_uri) is True + assert database_exists(postgres_uri) finally: drop_database(postgres_uri) def test_database_exists_sqlite_memory(self): """Test checking in-memory SQLite database (always exists).""" uri = "sqlite:///:memory:" - assert database_exists(uri) is True + assert database_exists(uri) def test_database_exists_sqlite_file_nonexistent(self, tmp_path): """Test checking non-existent SQLite file database.""" db_file = tmp_path / "nonexistent.db" uri = f"sqlite:///{db_file}" - assert database_exists(uri) is False + assert not database_exists(uri) def test_database_exists_sqlite_file_existent(self, tmp_path): """Test checking existing SQLite file database.""" db_file = tmp_path / "existing.db" db_file.touch() uri = f"sqlite:///{db_file}" - assert database_exists(uri) is True + assert database_exists(uri) def test_database_exists_unsupported_scheme(self): """Test that unsupported schemes raise ValueError.""" @@ -191,7 +191,7 @@ def test_create_database_postgresql(self, postgres_uri): create_database(postgres_uri) try: - assert database_exists(postgres_uri) is True + assert database_exists(postgres_uri) finally: drop_database(postgres_uri) @@ -213,7 +213,7 @@ def test_create_database_sqlite_memory(self): uri = "sqlite:///:memory:" # Should not raise an error create_database(uri) - assert database_exists(uri) is True + assert database_exists(uri) def test_create_database_sqlite_file(self, tmp_path): """Test creating a SQLite file database.""" @@ -223,7 +223,7 @@ def test_create_database_sqlite_file(self, tmp_path): assert not db_file.exists() create_database(uri) assert db_file.exists() - assert database_exists(uri) is True + assert database_exists(uri) # Cleanup db_file.unlink() @@ -241,7 +241,7 @@ def test_create_database_sqlite_file_with_subdirectory(self, tmp_path): assert subdir.exists() assert db_file.exists() - assert database_exists(uri) is True + assert database_exists(uri) # Cleanup db_file.unlink() @@ -286,9 +286,9 @@ def test_drop_database_postgresql_existent(self, postgres_uri): if not database_exists(postgres_uri): create_database(postgres_uri) - assert database_exists(postgres_uri) is True + assert database_exists(postgres_uri) drop_database(postgres_uri) - assert database_exists(postgres_uri) is False + assert not database_exists(postgres_uri) def test_drop_database_postgresql_nonexistent(self, postgres_uri): """Test dropping a non-existent PostgreSQL database.""" @@ -298,7 +298,7 @@ def test_drop_database_postgresql_nonexistent(self, postgres_uri): # Should not raise an error (uses IF EXISTS) drop_database(postgres_uri) - assert database_exists(postgres_uri) is False + assert not database_exists(postgres_uri) def test_drop_database_postgresql_with_connections(self, postgres_uri): """Test dropping a PostgreSQL database that has active connections.""" @@ -315,7 +315,7 @@ def test_drop_database_postgresql_with_connections(self, postgres_uri): try: # Drop should terminate connections and succeed drop_database(postgres_uri) - assert database_exists(postgres_uri) is False + assert not database_exists(postgres_uri) finally: conn.close() engine.dispose() @@ -325,7 +325,7 @@ def test_drop_database_sqlite_memory(self): uri = "sqlite:///:memory:" # Should not raise an error drop_database(uri) - assert database_exists(uri) is True # Still exists (in-memory) + assert database_exists(uri) # Still exists (in-memory) def test_drop_database_sqlite_file_existent(self, tmp_path): """Test dropping an existing SQLite file database.""" @@ -336,7 +336,7 @@ def test_drop_database_sqlite_file_existent(self, tmp_path): assert db_file.exists() drop_database(uri) assert not db_file.exists() - assert database_exists(uri) is False + assert not database_exists(uri) def test_drop_database_sqlite_file_nonexistent(self, tmp_path): """Test dropping a non-existent SQLite file database.""" @@ -371,23 +371,23 @@ def postgres_uri(self, make_postgres_uri, unique_db_name): def test_postgresql_lifecycle(self, postgres_uri): """Test complete PostgreSQL database lifecycle.""" # Start: database should not exist - assert database_exists(postgres_uri) is False + assert not database_exists(postgres_uri) # Create database create_database(postgres_uri) - assert database_exists(postgres_uri) is True + assert database_exists(postgres_uri) # Drop database drop_database(postgres_uri) - assert database_exists(postgres_uri) is False + assert not database_exists(postgres_uri) # Create again create_database(postgres_uri) - assert database_exists(postgres_uri) is True + assert database_exists(postgres_uri) # Final cleanup drop_database(postgres_uri) - assert database_exists(postgres_uri) is False + assert not database_exists(postgres_uri) def test_sqlite_file_lifecycle(self, tmp_path): """Test complete SQLite file database lifecycle.""" @@ -395,26 +395,26 @@ def test_sqlite_file_lifecycle(self, tmp_path): uri = f"sqlite:///{db_file}" # Start: database should not exist - assert database_exists(uri) is False + assert not database_exists(uri) # Create database create_database(uri) - assert database_exists(uri) is True + assert database_exists(uri) assert db_file.exists() # Drop database drop_database(uri) - assert database_exists(uri) is False + assert not database_exists(uri) assert not db_file.exists() # Create again create_database(uri) - assert database_exists(uri) is True + assert database_exists(uri) assert db_file.exists() # Final cleanup drop_database(uri) - assert database_exists(uri) is False + assert not database_exists(uri) assert not db_file.exists() def test_sqlite_memory_lifecycle(self): @@ -422,15 +422,15 @@ def test_sqlite_memory_lifecycle(self): uri = "sqlite:///:memory:" # In-memory always exists - assert database_exists(uri) is True + assert database_exists(uri) # Create is a no-op create_database(uri) - assert database_exists(uri) is True + assert database_exists(uri) # Drop is a no-op drop_database(uri) - assert database_exists(uri) is True + assert database_exists(uri) class TestDatabaseOperationsEdgeCases: @@ -454,7 +454,7 @@ def test_postgresql_database_name_with_special_chars(self, make_postgres_uri): create_database(uri) try: - assert database_exists(uri) is True + assert database_exists(uri) finally: drop_database(uri) @@ -465,7 +465,7 @@ def test_sqlite_file_path_with_spaces(self, tmp_path): create_database(uri) assert db_file.exists() - assert database_exists(uri) is True + assert database_exists(uri) drop_database(uri) assert not db_file.exists() @@ -477,7 +477,7 @@ def test_sqlite_file_path_unicode(self, tmp_path): create_database(uri) assert db_file.exists() - assert database_exists(uri) is True + assert database_exists(uri) drop_database(uri) assert not db_file.exists() @@ -487,15 +487,15 @@ def test_multiple_operations_sequence(self, postgres_uri): # Create and drop multiple times for _ in range(3): create_database(postgres_uri) - assert database_exists(postgres_uri) is True + assert database_exists(postgres_uri) drop_database(postgres_uri) - assert database_exists(postgres_uri) is False + assert not database_exists(postgres_uri) def test_sqlite_empty_path(self): """Test SQLite URI with empty path.""" uri = "sqlite:///" # Should be treated as in-memory - assert database_exists(uri) is True + assert database_exists(uri) create_database(uri) # Should be no-op drop_database(uri) # Should be no-op From 8ed267ebfa8503b3dae7b679368b1e577180de5c Mon Sep 17 00:00:00 2001 From: Fabrizio Romano Date: Wed, 10 Dec 2025 11:16:20 +0000 Subject: [PATCH 05/14] Enhance database existence checks in SQLite tests - Added assertions to verify the existence of the in-memory SQLite database after creation. - Improved clarity of the test by ensuring checks are performed before and after the database creation. --- tests/test_db_util.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_db_util.py b/tests/test_db_util.py index 5959caf..6f30f89 100644 --- a/tests/test_db_util.py +++ b/tests/test_db_util.py @@ -212,7 +212,10 @@ def test_create_database_sqlite_memory(self): """Test creating in-memory SQLite database (no-op).""" uri = "sqlite:///:memory:" # Should not raise an error + assert not database_exists(uri) + create_database(uri) + assert database_exists(uri) def test_create_database_sqlite_file(self, tmp_path): From 9e6a3e6e3fe9c899173d43dd5156685e02412dba Mon Sep 17 00:00:00 2001 From: Fabrizio Romano Date: Wed, 10 Dec 2025 11:21:17 +0000 Subject: [PATCH 06/14] Refactor test_db_util.py to streamline imports and connection setup - Removed redundant import of create_engine from within the test method. - Consolidated import statements for improved readability and organization. --- tests/test_db_util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_db_util.py b/tests/test_db_util.py index 6f30f89..9dd1355 100644 --- a/tests/test_db_util.py +++ b/tests/test_db_util.py @@ -4,6 +4,7 @@ from pathlib import Path import pytest +from sqlalchemy import create_engine from sqlalchemy.exc import ProgrammingError from tests.db_util import ( @@ -310,8 +311,6 @@ def test_drop_database_postgresql_with_connections(self, postgres_uri): create_database(postgres_uri) # Create a connection to the database - from sqlalchemy import create_engine - engine = create_engine(postgres_uri) conn = engine.connect() From 8bd8b41446759caf42414b69bbdf8c3c6db0b579 Mon Sep 17 00:00:00 2001 From: Fabrizio Romano Date: Wed, 10 Dec 2025 11:22:43 +0000 Subject: [PATCH 07/14] Update in-memory SQLite database test to assert existence after creation - Adjusted the test for creating an in-memory SQLite database to assert that it exists after the creation call, reflecting the behavior of in-memory databases. - Improved clarity by adding comments to explain the assertions and their expected outcomes. --- tests/test_db_util.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_db_util.py b/tests/test_db_util.py index 9dd1355..18ca36d 100644 --- a/tests/test_db_util.py +++ b/tests/test_db_util.py @@ -212,11 +212,11 @@ def test_create_database_postgresql_already_exists(self, postgres_uri): def test_create_database_sqlite_memory(self): """Test creating in-memory SQLite database (no-op).""" uri = "sqlite:///:memory:" + # In-memory databases always exist (they're created on connection) + assert database_exists(uri) # Should not raise an error - assert not database_exists(uri) - create_database(uri) - + # Still exists after create (no-op for in-memory) assert database_exists(uri) def test_create_database_sqlite_file(self, tmp_path): From e3712a79d403e2e2f6ce5e85645ff31d19396c86 Mon Sep 17 00:00:00 2001 From: Fabrizio Romano Date: Wed, 10 Dec 2025 11:23:58 +0000 Subject: [PATCH 08/14] Formatting. --- tests/db_util.py | 10 +++------- tests/test_db_util.py | 1 - tests/util.py | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/tests/db_util.py b/tests/db_util.py index 2fc89c8..4dbc293 100644 --- a/tests/db_util.py +++ b/tests/db_util.py @@ -27,7 +27,7 @@ def _get_postgres_admin_uri(uri): """Get a PostgreSQL URI pointing to the 'postgres' database for admin operations.""" parsed = _parse_database_uri(uri) # Connect to 'postgres' database instead of the target database - port_part = f":{parsed['port']}" if parsed['port'] is not None else "" + port_part = f":{parsed['port']}" if parsed["port"] is not None else "" admin_uri = ( f"postgresql://{parsed['username']}:{parsed['password']}@" f"{parsed['hostname']}{port_part}/postgres" @@ -75,10 +75,7 @@ def database_exists(uri): try: with engine.connect() as conn: result = conn.execute( - text( - "SELECT 1 FROM pg_database WHERE datname = :dbname" - ), - {"dbname": target_db} + text("SELECT 1 FROM pg_database WHERE datname = :dbname"), {"dbname": target_db} ) return result.fetchone() is not None finally: @@ -165,7 +162,7 @@ def drop_database(uri): AND pid <> pg_backend_pid() """ ), - {"dbname": target_db} + {"dbname": target_db}, ) # Drop the database conn.execute(text(f'DROP DATABASE IF EXISTS "{target_db}"')) @@ -185,4 +182,3 @@ def drop_database(uri): else: raise ValueError(f"Unsupported database scheme: {scheme}") - diff --git a/tests/test_db_util.py b/tests/test_db_util.py index 18ca36d..0b26680 100644 --- a/tests/test_db_util.py +++ b/tests/test_db_util.py @@ -500,4 +500,3 @@ def test_sqlite_empty_path(self): assert database_exists(uri) create_database(uri) # Should be no-op drop_database(uri) # Should be no-op - diff --git a/tests/util.py b/tests/util.py index 3136a87..3260a2c 100644 --- a/tests/util.py +++ b/tests/util.py @@ -3,9 +3,9 @@ from sqlalchemy import create_engine from sqlalchemy.engine import Engine from sqlalchemy.orm.decl_api import DeclarativeMeta -from tests.db_util import create_database, database_exists, drop_database from tests import assert_items_equal +from tests.db_util import create_database, database_exists, drop_database def create_db(uri): From 1c1b2cac136b5e62cee2a225d6c9498ff4f1d637 Mon Sep 17 00:00:00 2001 From: Fabrizio Romano Date: Wed, 10 Dec 2025 11:30:04 +0000 Subject: [PATCH 09/14] Update version. --- README.md | 1 - tests/db_util.py | 5 ----- 2 files changed, 6 deletions(-) diff --git a/README.md b/README.md index 558d462..a3ce480 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ A tool for comparing database schemas using SQLAlchemy. - Python 3.10 or higher (supports 3.10, 3.11, 3.12, 3.13, 3.14) - SQLAlchemy >= 1.4 -- sqlalchemy-utils ~= 0.41.2 ## Authors diff --git a/tests/db_util.py b/tests/db_util.py index 4dbc293..8b9f399 100644 --- a/tests/db_util.py +++ b/tests/db_util.py @@ -1,8 +1,3 @@ -"""Database utility functions for PostgreSQL and SQLite. - -These functions replace sqlalchemy-utils functionality for test purposes. -""" - from pathlib import Path from urllib.parse import urlparse From 3fe120fd6fc8cc6a29d425944611a06c9805af11 Mon Sep 17 00:00:00 2001 From: Fabrizio Romano Date: Wed, 10 Dec 2025 11:30:45 +0000 Subject: [PATCH 10/14] Bump version to 1.1.0. --- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- uv.lock | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c7e879..2112c84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [1.1.0] + +10/12/2025 + +- Remove dependency on sqlalchemy-utils. +- Add our own database utility functions for test purposes. +- Add comprehensive test suite for database utility functions. + + ## [1.0.4] 18/11/2025 diff --git a/pyproject.toml b/pyproject.toml index e65c22d..21c9a51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sqlalchemy-diff" -version = "1.0.4" +version = "1.1.0" authors = [ { name = "Fabrizio Romano", email = "gianchub@gmail.com" }, { name = "Mark McArdle", email = "m.mc4rdle@gmail.com" }, diff --git a/uv.lock b/uv.lock index 22c77c7..a1ee063 100644 --- a/uv.lock +++ b/uv.lock @@ -2015,7 +2015,7 @@ wheels = [ [[package]] name = "sqlalchemy-diff" -version = "1.0.4" +version = "1.1.0" source = { editable = "." } dependencies = [ { name = "sqlalchemy" }, From 61cefac6efa39350374eab6e1fefa95c0d049db0 Mon Sep 17 00:00:00 2001 From: Fabrizio Romano Date: Wed, 10 Dec 2025 12:07:09 +0000 Subject: [PATCH 11/14] Fix type hints. --- src/sqlalchemydiff/inspection/base.py | 3 ++- src/sqlalchemydiff/inspection/inspectors.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sqlalchemydiff/inspection/base.py b/src/sqlalchemydiff/inspection/base.py index c8af00e..e8182a3 100644 --- a/src/sqlalchemydiff/inspection/base.py +++ b/src/sqlalchemydiff/inspection/base.py @@ -1,5 +1,6 @@ import abc import inspect as stdlib_inspect +from typing import Any from sqlalchemy import inspect from sqlalchemy.engine import Engine @@ -59,7 +60,7 @@ def __init__(self, one_alias: str = "one", two_alias: str = "two"): @abc.abstractmethod def inspect( self, engine: Engine, ignore_specs: list[IgnoreSpecType] | None = None - ) -> dict: ... # pragma: no cover + ) -> Any: ... # pragma: no cover @abc.abstractmethod def diff(self, one: dict, two: dict) -> dict: ... # pragma: no cover diff --git a/src/sqlalchemydiff/inspection/inspectors.py b/src/sqlalchemydiff/inspection/inspectors.py index f803735..20c231a 100644 --- a/src/sqlalchemydiff/inspection/inspectors.py +++ b/src/sqlalchemydiff/inspection/inspectors.py @@ -1,4 +1,4 @@ -from typing import cast +from typing import Any, cast from sqlalchemy.engine import Engine @@ -144,7 +144,7 @@ def diff(self, one: dict, two: dict) -> dict: def _is_supported(self, inspector: Inspector) -> bool: return hasattr(inspector, "get_foreign_keys") - def _get_fk_identifier(self, fk: dict) -> dict: + def _get_fk_identifier(self, fk: Any) -> Any: if not fk["name"]: fk["name"] = f"_unnamed_fk_{fk['referred_table']}_{'_'.join(fk['constrained_columns'])}" return fk From 053026e974fc544e8c7cb74a2a9595040be1423a Mon Sep 17 00:00:00 2001 From: Fabrizio Romano Date: Wed, 10 Dec 2025 13:39:30 +0000 Subject: [PATCH 12/14] Remove unnecessary module docstring from test_db_util.py for improved clarity. --- tests/test_db_util.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_db_util.py b/tests/test_db_util.py index 0b26680..15c891e 100644 --- a/tests/test_db_util.py +++ b/tests/test_db_util.py @@ -1,5 +1,3 @@ -"""Comprehensive test suite for tests.db_util module.""" - import uuid from pathlib import Path From 4d21329cca5f003d35f31752dd5c3e37997a3053 Mon Sep 17 00:00:00 2001 From: Fabrizio Romano Date: Wed, 10 Dec 2025 13:42:46 +0000 Subject: [PATCH 13/14] Remove unnecessary cleanup. --- tests/test_db_util.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/test_db_util.py b/tests/test_db_util.py index 15c891e..d27cb31 100644 --- a/tests/test_db_util.py +++ b/tests/test_db_util.py @@ -227,9 +227,6 @@ def test_create_database_sqlite_file(self, tmp_path): assert db_file.exists() assert database_exists(uri) - # Cleanup - db_file.unlink() - def test_create_database_sqlite_file_with_subdirectory(self, tmp_path): """Test creating a SQLite file database in a subdirectory.""" subdir = tmp_path / "subdir" @@ -245,10 +242,6 @@ def test_create_database_sqlite_file_with_subdirectory(self, tmp_path): assert db_file.exists() assert database_exists(uri) - # Cleanup - db_file.unlink() - subdir.rmdir() - def test_create_database_sqlite_file_already_exists(self, tmp_path): """Test creating a SQLite file database that already exists.""" db_file = tmp_path / "existing.db" @@ -259,9 +252,6 @@ def test_create_database_sqlite_file_already_exists(self, tmp_path): create_database(uri) assert db_file.exists() - # Cleanup - db_file.unlink() - def test_create_database_unsupported_scheme(self): """Test that unsupported schemes raise ValueError.""" uri = "mysql://user:pass@localhost/db" From 00eb8c71ce0812aff2b23befdd3250015f26a397 Mon Sep 17 00:00:00 2001 From: Fabrizio Romano Date: Wed, 10 Dec 2025 15:20:53 +0000 Subject: [PATCH 14/14] Simplify _get_postgres_admin_url --- tests/db_util.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/db_util.py b/tests/db_util.py index 8b9f399..9c05e5e 100644 --- a/tests/db_util.py +++ b/tests/db_util.py @@ -20,14 +20,8 @@ def _parse_database_uri(uri): def _get_postgres_admin_uri(uri): """Get a PostgreSQL URI pointing to the 'postgres' database for admin operations.""" - parsed = _parse_database_uri(uri) - # Connect to 'postgres' database instead of the target database - port_part = f":{parsed['port']}" if parsed["port"] is not None else "" - admin_uri = ( - f"postgresql://{parsed['username']}:{parsed['password']}@" - f"{parsed['hostname']}{port_part}/postgres" - ) - return admin_uri + parsed = urlparse(uri) + return parsed._replace(path="postgres").geturl() def _get_sqlite_file_path(uri):