def test_validate_schema(threedi_db): """Validate a correct schema version""" schema = ModelSchema(threedi_db) with mock.patch.object(schema, "get_version", return_value=constants.MIN_SCHEMA_VERSION): assert schema.validate_schema()
def test_get_version_alembic(in_memory_sqlite, alembic_version_table): """Get the version of a sqlite with an alembic version table""" with in_memory_sqlite.get_engine().connect() as connection: connection.execute( alembic_version_table.insert().values(version_num="0201")) schema_checker = ModelSchema(in_memory_sqlite) migration_id = schema_checker.get_version() assert migration_id == 201
def test_get_version_south(in_memory_sqlite, south_migration_table): """Get the version of a sqlite with a South version table""" with in_memory_sqlite.get_engine().connect() as connection: for v in (42, 43): connection.execute(south_migration_table.insert().values(id=v)) schema_checker = ModelSchema(in_memory_sqlite) migration_id = schema_checker.get_version() assert migration_id == 43
def test_upgrade_south_not_latest_errors(in_memory_sqlite): """Upgrading a database that is not at the latest south migration will error""" schema = ModelSchema(in_memory_sqlite) with mock.patch.object(schema, "get_version", return_value=constants.LATEST_SOUTH_MIGRATION_ID - 1): with pytest.raises(errors.MigrationMissingError): schema.upgrade(backup=False)
def test_upgrade_without_backup(threedi_db): """Upgrading with backup=True will proceed on the database itself""" schema = ModelSchema(threedi_db) with mock.patch("threedi_modelchecker.schema._upgrade_database", side_effect=RuntimeError) as upgrade, mock.patch.object( schema, "get_version", return_value=199): with pytest.raises(RuntimeError): schema.upgrade(backup=False) (db, ), kwargs = upgrade.call_args assert db is threedi_db
def test_upgrade_with_backup(threedi_db): """Upgrading with backup=True will proceed on a copy of the database""" if threedi_db.db_type != "spatialite": pytest.skip() schema = ModelSchema(threedi_db) with mock.patch("threedi_modelchecker.schema._upgrade_database", side_effect=RuntimeError) as upgrade, mock.patch.object( schema, "get_version", return_value=199): with pytest.raises(RuntimeError): schema.upgrade(backup=True) (db, ), kwargs = upgrade.call_args assert db is not threedi_db
def migrate(ctx, revision): """Migrate the threedi model schematisation to the latest version.""" schema = ModelSchema(ctx.obj["db"]) click.echo("The current schema revision is: %s" % schema.get_version()) click.echo("Running alembic upgrade script...") schema.upgrade(revision=revision) click.echo("The migrated schema revision is: %s" % schema.get_version())
def test_validate_schema_missing_migration(threedi_db, version): """Validate a too low schema version""" schema = ModelSchema(threedi_db) with mock.patch.object(schema, "get_version", return_value=version): with pytest.raises(errors.MigrationMissingError): schema.validate_schema()
def test_get_version_empty_alembic(in_memory_sqlite, alembic_version_table): """Get the version of a sqlite with an empty alembic version table""" schema_checker = ModelSchema(in_memory_sqlite) migration_id = schema_checker.get_version() assert migration_id is None
def test_get_version_no_tables(in_memory_sqlite): """Get the version of a sqlite with no version tables""" schema_checker = ModelSchema(in_memory_sqlite) migration_id = schema_checker.get_version() assert migration_id is None
def test_full_upgrade_oldest(oldest_sqlite): """Upgrade an empty database to the latest version""" schema = ModelSchema(oldest_sqlite) schema.upgrade(backup=False) assert schema.get_version() == get_schema_version() assert oldest_sqlite.get_engine().has_table("v2_connection_nodes")
def test_validate_schema_too_high_migration(threedi_db, version): """Validate a too high schema version""" schema = ModelSchema(threedi_db) with mock.patch.object(schema, "get_version", return_value=version): with pytest.warns(UserWarning): schema.validate_schema()
def check_schematisation(self): """Run schematisation checker.""" try: from sqlalchemy.exc import OperationalError from threedi_modelchecker.threedi_database import ThreediDatabase from threedi_modelchecker.model_checks import ThreediModelChecker from threedi_modelchecker.schema import ModelSchema from threedi_modelchecker import errors except ImportError: raise db_settings = {"db_path": self.schematisation_sqlite} threedi_db = ThreediDatabase(db_settings) schema = ModelSchema(threedi_db) try: schema.validate_schema() except errors.MigrationMissingError: warn_and_ask_msg = ( "The selected spatialite cannot be used because its database schema version is out of date. " "Would you like to migrate your spatialite to the current schema version?" ) do_migration = self.communication.ask(None, "Missing migration", warn_and_ask_msg) if not do_migration: self.communication.bar_warn("Schematisation checks skipped!") return wip_revision = self.current_local_schematisation.wip_revision backup_filepath = wip_revision.backup_sqlite() schema.upgrade(backup=False, upgrade_spatialite_version=True) shutil.rmtree(os.path.dirname(backup_filepath)) except errors.UpgradeFailedError: error_msg = ( "There are errors in the spatialite. Please re-open this file in QGIS 3.16, run the model checker and " "fix error messages. Then attempt to upgrade again. For questions please contact the servicedesk." ) self.communication.show_error(error_msg, self) return except Exception as e: error_msg = f"{e}" self.communication.show_error(error_msg, self) return model_checker = None try: model_checker = ThreediModelChecker(threedi_db) model_checker.db.check_connection() except OperationalError as exc: error_msg = ( f"Failed to start a connection with the database.\n" f"Something went wrong trying to connect to the database, " f"please check the connection settings: {exc.args[0]}") self.communication.show_error(error_msg, self) return except errors.MigrationMissingError: error_msg = ( "The selected 3Di model does not have the latest migration.\n" "The selected 3Di model does not have the latest migration," "please migrate your model to the latest version.") self.communication.show_error(error_msg, self) return except errors.MigrationTooHighError: error_msg = ( "The selected 3Di model has a higher migration than expected.\n" "The 3Di model has a higher migration than expected, " "do you have the latest version of ThreediToolbox?") self.communication.show_error(error_msg, self) return except errors.MigrationNameError: warn_msg = ( "Unexpected migration name, but migration id is matching.\n" "We are gonna continue for now and hope for the best.") self.communication.bar_warn(warn_msg) session = model_checker.db.get_session() total_checks = len(model_checker.config.checks) self.pbar_check_spatialite.setMaximum(total_checks) self.pbar_check_spatialite.setValue(0) results_rows = [] for i, check in enumerate( model_checker.checks(level=LogLevels.INFO.value), start=1): for result_row in check.get_invalid(session): results_rows.append([ check.level.name, check.error_code, result_row.id, check.table.name, check.column.name, getattr(result_row, check.column.name), check.description(), ]) self.pbar_check_spatialite.setValue(i) if results_rows: for result_row in results_rows: level = result_row[0].upper() self.schematisation_checker_logger.log_result_row( result_row, level) self.communication.bar_info("Finished schematisation checks.") self.pbar_check_spatialite.setValue(total_checks) self.pbar_check_spatialite.hide() self.lbl_check_spatialite.show()