diff --git a/swh/core/db/tests/conftest.py b/swh/core/db/tests/conftest.py index e4550a0..b1d42f4 100644 --- a/swh/core/db/tests/conftest.py +++ b/swh/core/db/tests/conftest.py @@ -1,57 +1,67 @@ # Copyright (C) 2019-2022 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information import os -import pathlib from click.testing import CliRunner from hypothesis import HealthCheck import pytest -from swh.core.db.db_utils import get_sql_for_package -from swh.core.utils import numfile_sortkey as sortkey +from swh.core.db.db_utils import import_swhmodule os.environ["LC_ALL"] = "C.UTF-8" # we use getattr here to keep mypy happy regardless hypothesis version function_scoped_fixture_check = ( [getattr(HealthCheck, "function_scoped_fixture")] if hasattr(HealthCheck, "function_scoped_fixture") else [] ) @pytest.fixture def cli_runner(): return CliRunner() @pytest.fixture() -def mock_package_sql(mocker, datadir): - """This bypasses the module manipulation to only returns the data test files. +def mock_import_swhmodule(mocker, datadir): + """This bypasses the module manipulation to make import_swhmodule return a mock + object suitable for data test files listing via get_sql_for_package. - For a given module `test.mod`, look for sql files in the directory `data/mod/*.sql`. + For a given module `test.`, return a MagicMock object with a __name__ + set to `` and __file__ pointing to `data//__init__.py`. + + The Mock object also defines a `get_datastore()` attribute on which the + `get_current_version()` exists and will return 42. Typical usage:: - def test_xxx(cli_runner, mock_package_sql): + def test_xxx(cli_runner, mock_import_swhmodule): conninfo = craft_conninfo(test_db, "new-db") module_name = "test.cli" - # the command below will use sql scripts from swh/core/db/tests/data/cli/*.sql + # the command below will use sql scripts from + # swh/core/db/tests/data/cli/sql/*.sql cli_runner.invoke(swhdb, ["init", module_name, "--dbname", conninfo]) + """ + mock = mocker.MagicMock - def get_sql_for_package_mock(modname, upgrade=False): + def import_swhmodule_mock(modname): if modname.startswith("test."): - sqldir = pathlib.Path(datadir) / modname.split(".", 1)[1] - if upgrade: - sqldir /= "upgrades" - return sorted(sqldir.glob("*.sql"), key=lambda x: sortkey(x.name)) - return get_sql_for_package(modname) - - mock_sql_files = mocker.patch( - "swh.core.db.db_utils.get_sql_for_package", get_sql_for_package_mock - ) - return mock_sql_files + dirname = modname.split(".", 1)[1] + + def get_datastore(*args, **kw): + return mock(get_current_version=lambda: 42) + + return mock( + __name__=modname, + __file__=os.path.join(datadir, dirname, "__init__.py"), + get_datastore=get_datastore, + ) + else: + return import_swhmodule(modname) + + return mocker.patch("swh.core.db.db_utils.import_swhmodule", import_swhmodule_mock) diff --git a/swh/core/db/tests/data/cli/0-superuser-init.sql b/swh/core/db/tests/data/cli/sql/0-superuser-init.sql similarity index 100% rename from swh/core/db/tests/data/cli/0-superuser-init.sql rename to swh/core/db/tests/data/cli/sql/0-superuser-init.sql diff --git a/swh/core/db/tests/data/cli/30-schema.sql b/swh/core/db/tests/data/cli/sql/30-schema.sql similarity index 100% rename from swh/core/db/tests/data/cli/30-schema.sql rename to swh/core/db/tests/data/cli/sql/30-schema.sql diff --git a/swh/core/db/tests/data/cli/40-funcs.sql b/swh/core/db/tests/data/cli/sql/40-funcs.sql similarity index 100% rename from swh/core/db/tests/data/cli/40-funcs.sql rename to swh/core/db/tests/data/cli/sql/40-funcs.sql diff --git a/swh/core/db/tests/data/cli/50-data.sql b/swh/core/db/tests/data/cli/sql/50-data.sql similarity index 100% rename from swh/core/db/tests/data/cli/50-data.sql rename to swh/core/db/tests/data/cli/sql/50-data.sql diff --git a/swh/core/db/tests/data/cli_new/0-superuser-init.sql b/swh/core/db/tests/data/cli_new/sql/0-superuser-init.sql similarity index 100% rename from swh/core/db/tests/data/cli_new/0-superuser-init.sql rename to swh/core/db/tests/data/cli_new/sql/0-superuser-init.sql diff --git a/swh/core/db/tests/data/cli_new/30-schema.sql b/swh/core/db/tests/data/cli_new/sql/30-schema.sql similarity index 100% rename from swh/core/db/tests/data/cli_new/30-schema.sql rename to swh/core/db/tests/data/cli_new/sql/30-schema.sql diff --git a/swh/core/db/tests/data/cli_new/40-funcs.sql b/swh/core/db/tests/data/cli_new/sql/40-funcs.sql similarity index 100% rename from swh/core/db/tests/data/cli_new/40-funcs.sql rename to swh/core/db/tests/data/cli_new/sql/40-funcs.sql diff --git a/swh/core/db/tests/data/cli_new/50-data.sql b/swh/core/db/tests/data/cli_new/sql/50-data.sql similarity index 100% rename from swh/core/db/tests/data/cli_new/50-data.sql rename to swh/core/db/tests/data/cli_new/sql/50-data.sql diff --git a/swh/core/db/tests/data/cli_new/upgrades/001.sql b/swh/core/db/tests/data/cli_new/sql/upgrades/001.sql similarity index 100% rename from swh/core/db/tests/data/cli_new/upgrades/001.sql rename to swh/core/db/tests/data/cli_new/sql/upgrades/001.sql diff --git a/swh/core/db/tests/data/cli_new/upgrades/002.sql b/swh/core/db/tests/data/cli_new/sql/upgrades/002.sql similarity index 100% rename from swh/core/db/tests/data/cli_new/upgrades/002.sql rename to swh/core/db/tests/data/cli_new/sql/upgrades/002.sql diff --git a/swh/core/db/tests/data/cli_new/upgrades/003.sql b/swh/core/db/tests/data/cli_new/sql/upgrades/003.sql similarity index 100% rename from swh/core/db/tests/data/cli_new/upgrades/003.sql rename to swh/core/db/tests/data/cli_new/sql/upgrades/003.sql diff --git a/swh/core/db/tests/data/cli_new/upgrades/004.sql b/swh/core/db/tests/data/cli_new/sql/upgrades/004.sql similarity index 100% rename from swh/core/db/tests/data/cli_new/upgrades/004.sql rename to swh/core/db/tests/data/cli_new/sql/upgrades/004.sql diff --git a/swh/core/db/tests/data/cli_new/upgrades/005.sql b/swh/core/db/tests/data/cli_new/sql/upgrades/005.sql similarity index 100% rename from swh/core/db/tests/data/cli_new/upgrades/005.sql rename to swh/core/db/tests/data/cli_new/sql/upgrades/005.sql diff --git a/swh/core/db/tests/data/cli_new/upgrades/006.sql b/swh/core/db/tests/data/cli_new/sql/upgrades/006.sql similarity index 100% rename from swh/core/db/tests/data/cli_new/upgrades/006.sql rename to swh/core/db/tests/data/cli_new/sql/upgrades/006.sql diff --git a/swh/core/db/tests/test_cli.py b/swh/core/db/tests/test_cli.py index bbea68c..3164ffa 100644 --- a/swh/core/db/tests/test_cli.py +++ b/swh/core/db/tests/test_cli.py @@ -1,356 +1,350 @@ # Copyright (C) 2019-2022 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information import copy +import os import traceback -from unittest.mock import MagicMock import pytest import yaml from swh.core.cli.db import db as swhdb from swh.core.db import BaseDb -from swh.core.db.db_utils import import_swhmodule, swh_db_module +from swh.core.db.db_utils import import_swhmodule, swh_db_module, swh_db_version from swh.core.tests.test_cli import assert_section_contains def test_cli_swh_help(swhmain, cli_runner): swhmain.add_command(swhdb) result = cli_runner.invoke(swhmain, ["-h"]) assert result.exit_code == 0 assert_section_contains( result.output, "Commands", "db Software Heritage database generic tools." ) help_db_snippets = ( ( "Usage", ( "Usage: swh db [OPTIONS] COMMAND [ARGS]...", "Software Heritage database generic tools.", ), ), ( "Commands", ( "create Create a database for the Software Heritage .", "init Initialize a database for the Software Heritage .", "init-admin Execute superuser-level initialization steps", ), ), ) def test_cli_swh_db_help(swhmain, cli_runner): swhmain.add_command(swhdb) result = cli_runner.invoke(swhmain, ["db", "-h"]) assert result.exit_code == 0 for section, snippets in help_db_snippets: for snippet in snippets: assert_section_contains(result.output, section, snippet) @pytest.fixture def swh_db_cli(cli_runner, monkeypatch, postgresql): """This initializes a cli_runner and sets the correct environment variable expected by the cli to run appropriately (when not specifying the --dbname flag) """ db_params = postgresql.get_dsn_parameters() monkeypatch.setenv("PGHOST", db_params["host"]) monkeypatch.setenv("PGUSER", db_params["user"]) monkeypatch.setenv("PGPORT", db_params["port"]) return cli_runner, db_params def craft_conninfo(test_db, dbname=None) -> str: """Craft conninfo string out of the test_db object. This also allows to override the dbname.""" db_params = test_db.get_dsn_parameters() if dbname: params = copy.deepcopy(db_params) params["dbname"] = dbname else: params = db_params return "postgresql://{user}@{host}:{port}/{dbname}".format(**params) -def test_cli_swh_db_create_and_init_db(cli_runner, postgresql, mock_package_sql): +def test_cli_swh_db_create_and_init_db(cli_runner, postgresql, mock_import_swhmodule): """Create a db then initializing it should be ok """ module_name = "test.cli" conninfo = craft_conninfo(postgresql, "new-db") # This creates the db and installs the necessary admin extensions result = cli_runner.invoke(swhdb, ["create", module_name, "--dbname", conninfo]) assert result.exit_code == 0, f"Unexpected output: {result.output}" # This initializes the schema and data result = cli_runner.invoke(swhdb, ["init", module_name, "--dbname", conninfo]) assert result.exit_code == 0, f"Unexpected output: {result.output}" # the origin value in the scripts uses a hash function (which implementation wise # uses a function from the pgcrypt extension, installed during db creation step) with BaseDb.connect(conninfo).cursor() as cur: cur.execute("select * from origin") origins = cur.fetchall() assert len(origins) == 1 def test_cli_swh_db_initialization_fail_without_creation_first( - cli_runner, postgresql, mock_package_sql + cli_runner, postgresql, mock_import_swhmodule ): """Init command on an inexisting db cannot work """ module_name = "test.cli" # it's mocked here conninfo = craft_conninfo(postgresql, "inexisting-db") result = cli_runner.invoke(swhdb, ["init", module_name, "--dbname", conninfo]) # Fails because we cannot connect to an inexisting db assert result.exit_code == 1, f"Unexpected output: {result.output}" def test_cli_swh_db_initialization_fail_without_extension( - cli_runner, postgresql, mock_package_sql + cli_runner, postgresql, mock_import_swhmodule ): """Init command cannot work without privileged extension. In this test, the schema needs privileged extension to work. """ module_name = "test.cli" # it's mocked here conninfo = craft_conninfo(postgresql) result = cli_runner.invoke(swhdb, ["init", module_name, "--dbname", conninfo]) # Fails as the function `public.digest` is not installed, init-admin calls is needed # first (the next tests show such behavior) assert result.exit_code == 1, f"Unexpected output: {result.output}" def test_cli_swh_db_initialization_works_with_flags( - cli_runner, postgresql, mock_package_sql + cli_runner, postgresql, mock_import_swhmodule ): """Init commands with carefully crafted libpq conninfo works """ module_name = "test.cli" # it's mocked here conninfo = craft_conninfo(postgresql) result = cli_runner.invoke(swhdb, ["init-admin", module_name, "--dbname", conninfo]) assert result.exit_code == 0, f"Unexpected output: {result.output}" result = cli_runner.invoke(swhdb, ["init", module_name, "--dbname", conninfo]) assert result.exit_code == 0, f"Unexpected output: {result.output}" # the origin values in the scripts uses a hash function (which implementation wise # uses a function from the pgcrypt extension, init-admin calls installs it) with BaseDb.connect(postgresql.dsn).cursor() as cur: cur.execute("select * from origin") origins = cur.fetchall() assert len(origins) == 1 -def test_cli_swh_db_initialization_with_env(swh_db_cli, mock_package_sql, postgresql): +def test_cli_swh_db_initialization_with_env( + swh_db_cli, mock_import_swhmodule, postgresql +): """Init commands with standard environment variables works """ module_name = "test.cli" # it's mocked here cli_runner, db_params = swh_db_cli result = cli_runner.invoke( swhdb, ["init-admin", module_name, "--dbname", db_params["dbname"]] ) assert result.exit_code == 0, f"Unexpected output: {result.output}" result = cli_runner.invoke( swhdb, ["init", module_name, "--dbname", db_params["dbname"]] ) assert result.exit_code == 0, f"Unexpected output: {result.output}" # the origin values in the scripts uses a hash function (which implementation wise # uses a function from the pgcrypt extension, init-admin calls installs it) with BaseDb.connect(postgresql.dsn).cursor() as cur: cur.execute("select * from origin") origins = cur.fetchall() assert len(origins) == 1 -def test_cli_swh_db_initialization_idempotent(swh_db_cli, mock_package_sql, postgresql): +def test_cli_swh_db_initialization_idempotent( + swh_db_cli, mock_import_swhmodule, postgresql +): """Multiple runs of the init commands are idempotent """ module_name = "test.cli" # mocked cli_runner, db_params = swh_db_cli result = cli_runner.invoke( swhdb, ["init-admin", module_name, "--dbname", db_params["dbname"]] ) assert result.exit_code == 0, f"Unexpected output: {result.output}" result = cli_runner.invoke( swhdb, ["init", module_name, "--dbname", db_params["dbname"]] ) assert result.exit_code == 0, f"Unexpected output: {result.output}" result = cli_runner.invoke( swhdb, ["init-admin", module_name, "--dbname", db_params["dbname"]] ) assert result.exit_code == 0, f"Unexpected output: {result.output}" result = cli_runner.invoke( swhdb, ["init", module_name, "--dbname", db_params["dbname"]] ) assert result.exit_code == 0, f"Unexpected output: {result.output}" # the origin values in the scripts uses a hash function (which implementation wise # uses a function from the pgcrypt extension, init-admin calls installs it) with BaseDb.connect(postgresql.dsn).cursor() as cur: cur.execute("select * from origin") origins = cur.fetchall() assert len(origins) == 1 def test_cli_swh_db_create_and_init_db_new_api( - cli_runner, postgresql, mock_package_sql, mocker, tmp_path + cli_runner, postgresql, mock_import_swhmodule, mocker, tmp_path ): """Create a db then initializing it should be ok for a "new style" datastore """ module_name = "test.cli_new" - def import_swhmodule_mock(modname): - if modname.startswith("test."): - - def get_datastore(cls, **kw): - # XXX probably not the best way of doing this... - return MagicMock(get_current_version=lambda: 42) - - return MagicMock(name=modname, get_datastore=get_datastore) - - return import_swhmodule(modname) - - mocker.patch("swh.core.db.db_utils.import_swhmodule", import_swhmodule_mock) conninfo = craft_conninfo(postgresql) # This initializes the schema and data cfgfile = tmp_path / "config.yml" cfgfile.write_text(yaml.dump({module_name: {"cls": "postgresql", "db": conninfo}})) result = cli_runner.invoke(swhdb, ["init-admin", module_name, "--dbname", conninfo]) assert result.exit_code == 0, f"Unexpected output: {result.output}" result = cli_runner.invoke(swhdb, ["-C", cfgfile, "init", module_name]) assert ( result.exit_code == 0 ), f"Unexpected output: {traceback.print_tb(result.exc_info[2])}" # the origin value in the scripts uses a hash function (which implementation wise # uses a function from the pgcrypt extension, installed during db creation step) with BaseDb.connect(conninfo).cursor() as cur: cur.execute("select * from origin") origins = cur.fetchall() assert len(origins) == 1 -def test_cli_swh_db_upgrade_new_api( - cli_runner, postgresql, mock_package_sql, mocker, tmp_path -): +def test_cli_swh_db_upgrade_new_api(cli_runner, postgresql, datadir, mocker, tmp_path): """Upgrade scenario for a "new style" datastore """ module_name = "test.cli_new" - from unittest.mock import MagicMock - - from swh.core.db.db_utils import import_swhmodule, swh_db_version - # the `current_version` variable is the version that will be returned by # any call to `get_current_version()` in this test session, thanks to the # local mocked version of import_swhmodule() below. current_version = 1 + # custom version of the mockup to make it easy to change the + # current_version returned by get_current_version() + # TODO: find a better solution for this... def import_swhmodule_mock(modname): if modname.startswith("test."): + dirname = modname.split(".", 1)[1] def get_datastore(cls, **kw): - # XXX probably not the best way of doing this... - return MagicMock(get_current_version=lambda: current_version) - - return MagicMock(name=modname, get_datastore=get_datastore) + return mocker.MagicMock(get_current_version=lambda: current_version) + + return mocker.MagicMock( + __name__=modname, + __file__=os.path.join(datadir, dirname, "__init__.py"), + name=modname, + get_datastore=get_datastore, + ) return import_swhmodule(modname) mocker.patch("swh.core.db.db_utils.import_swhmodule", import_swhmodule_mock) conninfo = craft_conninfo(postgresql) # This initializes the schema and data cfgfile = tmp_path / "config.yml" cfgfile.write_text(yaml.dump({module_name: {"cls": "postgresql", "db": conninfo}})) result = cli_runner.invoke(swhdb, ["init-admin", module_name, "--dbname", conninfo]) assert result.exit_code == 0, f"Unexpected output: {result.output}" result = cli_runner.invoke(swhdb, ["-C", cfgfile, "init", module_name]) assert ( result.exit_code == 0 ), f"Unexpected output: {traceback.print_tb(result.exc_info[2])}" assert swh_db_version(conninfo) == 1 # the upgrade should not do anything because the datastore does advertise # version 1 result = cli_runner.invoke(swhdb, ["-C", cfgfile, "upgrade", module_name]) assert swh_db_version(conninfo) == 1 # advertise current version as 3, a simple upgrade should get us there, but # no further current_version = 3 result = cli_runner.invoke(swhdb, ["-C", cfgfile, "upgrade", module_name]) assert swh_db_version(conninfo) == 3 # an attempt to go further should not do anything result = cli_runner.invoke( swhdb, ["-C", cfgfile, "upgrade", module_name, "--to-version", 5] ) assert swh_db_version(conninfo) == 3 # an attempt to go lower should not do anything result = cli_runner.invoke( swhdb, ["-C", cfgfile, "upgrade", module_name, "--to-version", 2] ) assert swh_db_version(conninfo) == 3 # advertise current version as 6, an upgrade with --to-version 4 should # stick to the given version 4 and no further current_version = 6 result = cli_runner.invoke( swhdb, ["-C", cfgfile, "upgrade", module_name, "--to-version", 4] ) assert swh_db_version(conninfo) == 4 assert "migration was not complete" in result.output # attempt to upgrade to a newer version than current code version fails result = cli_runner.invoke( swhdb, ["-C", cfgfile, "upgrade", module_name, "--to-version", current_version + 1], ) assert result.exit_code != 0 assert swh_db_version(conninfo) == 4 cnx = BaseDb.connect(conninfo) with cnx.transaction() as cur: cur.execute("drop table dbmodule") assert swh_db_module(conninfo) is None # db migration should recreate the missing dbmodule table result = cli_runner.invoke(swhdb, ["-C", cfgfile, "upgrade", module_name]) assert result.exit_code == 0 assert "Warning: the database does not have a dbmodule table." in result.output assert ( "Write the module information (test.cli_new) in the database? [Y/n]" in result.output ) assert swh_db_module(conninfo) == module_name diff --git a/swh/core/db/tests/test_db_utils.py b/swh/core/db/tests/test_db_utils.py index 4f0600e..7e2719d 100644 --- a/swh/core/db/tests/test_db_utils.py +++ b/swh/core/db/tests/test_db_utils.py @@ -1,174 +1,189 @@ # Copyright (C) 2022 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information from datetime import datetime, timedelta from os import path import pytest from swh.core.cli.db import db as swhdb from swh.core.db import BaseDb from swh.core.db.db_utils import ( get_database_info, + get_sql_for_package, now, swh_db_module, swh_db_upgrade, swh_db_version, swh_db_versions, swh_set_db_module, ) from .test_cli import craft_conninfo @pytest.mark.parametrize("module", ["test.cli", "test.cli_new"]) -def test_db_utils_versions(cli_runner, postgresql, mock_package_sql, module): +def test_get_sql_for_package(mock_import_swhmodule, module): + files = get_sql_for_package(module) + assert files + assert [f.name for f in files] == [ + "0-superuser-init.sql", + "30-schema.sql", + "40-funcs.sql", + "50-data.sql", + ] + + +@pytest.mark.parametrize("module", ["test.cli", "test.cli_new"]) +def test_db_utils_versions(cli_runner, postgresql, mock_import_swhmodule, module): """Check get_database_info, swh_db_versions and swh_db_module work ok This test checks db versions for both a db with "new style" set of sql init scripts (i.e. the dbversion table is not created in these scripts, but by the populate_database_for_package() function directly, via the 'swh db init' command) and an "old style" set (dbversion created in the scripts)S. """ conninfo = craft_conninfo(postgresql) result = cli_runner.invoke(swhdb, ["init-admin", module, "--dbname", conninfo]) assert result.exit_code == 0, f"Unexpected output: {result.output}" result = cli_runner.invoke( swhdb, ["init", module, "--dbname", conninfo, "--initial-version", 10] ) assert result.exit_code == 0, f"Unexpected output: {result.output}" # check the swh_db_module() function assert swh_db_module(conninfo) == module # the dbversion and dbmodule tables exists and are populated dbmodule, dbversion, dbflavor = get_database_info(conninfo) # check also the swh_db_versions() function versions = swh_db_versions(conninfo) assert dbmodule == module assert dbversion == 10 assert dbflavor is None # check also the swh_db_versions() function versions = swh_db_versions(conninfo) assert len(versions) == 1 assert versions[0][0] == 10 if module == "test.cli": assert versions[0][1] == datetime.fromisoformat( "2016-02-22T15:56:28.358587+00:00" ) assert versions[0][2] == "Work In Progress" else: # new scheme but with no datastore (so no version support from there) assert versions[0][2] == "DB initialization" # add a few versions in dbversion cnx = BaseDb.connect(conninfo) with cnx.transaction() as cur: cur.executemany( "insert into dbversion(version, release, description) values (%s, %s, %s)", [(i, now(), f"Upgrade to version {i}") for i in range(11, 15)], ) dbmodule, dbversion, dbflavor = get_database_info(conninfo) assert dbmodule == module assert dbversion == 14 assert dbflavor is None versions = swh_db_versions(conninfo) assert len(versions) == 5 for i, (version, ts, desc) in enumerate(versions): assert version == (14 - i) # these are in reverse order if version > 10: assert desc == f"Upgrade to version {version}" assert (now() - ts) < timedelta(seconds=1) @pytest.mark.parametrize("module", ["test.cli_new"]) -def test_db_utils_upgrade(cli_runner, postgresql, mock_package_sql, module, datadir): +def test_db_utils_upgrade( + cli_runner, postgresql, mock_import_swhmodule, module, datadir +): """Check swh_db_upgrade """ conninfo = craft_conninfo(postgresql) result = cli_runner.invoke(swhdb, ["init-admin", module, "--dbname", conninfo]) assert result.exit_code == 0, f"Unexpected output: {result.output}" result = cli_runner.invoke(swhdb, ["init", module, "--dbname", conninfo]) assert result.exit_code == 0, f"Unexpected output: {result.output}" assert swh_db_version(conninfo) == 1 new_version = swh_db_upgrade(conninfo, module) assert new_version == 6 assert swh_db_version(conninfo) == 6 versions = swh_db_versions(conninfo) # get rid of dates to ease checking versions = [(v[0], v[2]) for v in versions] assert versions[-1] == (1, "DB initialization") - sqlbasedir = path.join(datadir, module.split(".", 1)[1], "upgrades") + sqlbasedir = path.join(datadir, module.split(".", 1)[1], "sql", "upgrades") assert versions[1:-1] == [ (i, f"Upgraded to version {i} using {sqlbasedir}/{i:03d}.sql") for i in range(5, 1, -1) ] assert versions[0] == (6, "Updated version from upgrade script") cnx = BaseDb.connect(conninfo) with cnx.transaction() as cur: cur.execute("select url from origin where url like 'version%'") result = cur.fetchall() assert result == [("version%03d" % i,) for i in range(2, 7)] cur.execute( "select url from origin where url = 'this should never be executed'" ) result = cur.fetchall() assert not result @pytest.mark.parametrize("module", ["test.cli_new"]) def test_db_utils_swh_db_upgrade_sanity_checks( - cli_runner, postgresql, mock_package_sql, module, datadir + cli_runner, postgresql, mock_import_swhmodule, module, datadir ): """Check swh_db_upgrade """ conninfo = craft_conninfo(postgresql) result = cli_runner.invoke(swhdb, ["init-admin", module, "--dbname", conninfo]) assert result.exit_code == 0, f"Unexpected output: {result.output}" result = cli_runner.invoke(swhdb, ["init", module, "--dbname", conninfo]) assert result.exit_code == 0, f"Unexpected output: {result.output}" cnx = BaseDb.connect(conninfo) with cnx.transaction() as cur: cur.execute("drop table dbmodule") # try to upgrade with a unset module with pytest.raises(ValueError): swh_db_upgrade(conninfo, module) # check the dbmodule is unset assert swh_db_module(conninfo) is None # set the stored module to something else swh_set_db_module(conninfo, f"{module}2") assert swh_db_module(conninfo) == f"{module}2" # try to upgrade with a different module with pytest.raises(ValueError): swh_db_upgrade(conninfo, module) # revert to the proper module in the db swh_set_db_module(conninfo, module, force=True) assert swh_db_module(conninfo) == module # trying again is a noop swh_set_db_module(conninfo, module) assert swh_db_module(conninfo) == module # drop the dbversion table with cnx.transaction() as cur: cur.execute("drop table dbversion") # an upgrade should fail due to missing stored version with pytest.raises(ValueError): swh_db_upgrade(conninfo, module)