diff --git a/documentation/changelog.rst b/documentation/changelog.rst index 02c1e5eb58..7b1e1e272c 100644 --- a/documentation/changelog.rst +++ b/documentation/changelog.rst @@ -33,6 +33,7 @@ Bugfixes * KPIs on the asset page counted one day more than the selected time range [see `PR #2434 `_] * KPIs on the asset page now total the values the chart beside them draws, counting each event under the day it starts in: a sensor reported by several sources counted only one of them, and a revised value was counted on top of the value it revised [see `PR #2434 `_] * The time range sent when loading an asset's KPIs was off by the viewer's UTC offset, so KPIs could cover the wrong days [see `PR #2435 `_] +* Avoid crashing on startup when the database is stamped with an Alembic revision unknown to this FlexMeasures checkout [see `PR #2465 `_] diff --git a/flexmeasures/data/tests/test_utils.py b/flexmeasures/data/tests/test_utils.py index 751d6f6a10..8825c16221 100644 --- a/flexmeasures/data/tests/test_utils.py +++ b/flexmeasures/data/tests/test_utils.py @@ -2,6 +2,7 @@ import logging +from alembic.script.revision import ResolutionError from sqlalchemy.exc import OperationalError, ProgrammingError from flexmeasures.data import db, register_at @@ -54,12 +55,25 @@ def iterate_revisions(self, *args, **kwargs): return (_DummyRevision(revision) for revision in self._revisions) +class _DummyRevisionMapWithUnknownRevision: + def iterate_revisions(self, *args, **kwargs): + def revisions(): + raise ResolutionError("No such revision or branch 'unknown-a'", "unknown-a") + yield + + return revisions() + + class _DummyScriptDirectoryWithRevisionMap(_DummyScriptDirectory): def __init__(self, heads: tuple[str, ...], revisions: tuple[str, ...]): super().__init__(heads) self.revision_map = _DummyRevisionMap(revisions) +class _DummyScriptDirectoryWithUnknownRevisionMap(_DummyScriptDirectory): + revision_map = _DummyRevisionMapWithUnknownRevision() + + def test_schema_mismatch_log_record_is_deduplicated( app, clean_redis, monkeypatch, caplog ): @@ -237,3 +251,19 @@ def test_database_schema_has_revision_false_when_revision_is_not_in_current_hist ) assert database_schema_has_revision(app, "required-a") is False + + +def test_database_schema_has_revision_false_when_current_revision_is_unknown( + app, monkeypatch +): + monkeypatch.setattr(db.engine, "connect", lambda: _DummyConnection()) + monkeypatch.setattr( + "flexmeasures.data.utils.MigrationContext.configure", + lambda connection: _DummyMigrationContext(("unknown-a",)), + ) + monkeypatch.setattr( + "flexmeasures.data.utils.ScriptDirectory.from_config", + lambda config: _DummyScriptDirectoryWithUnknownRevisionMap(("head-a",)), + ) + + assert database_schema_has_revision(app, "required-a") is False diff --git a/flexmeasures/data/utils.py b/flexmeasures/data/utils.py index b684c5d560..f4a84ff2e9 100644 --- a/flexmeasures/data/utils.py +++ b/flexmeasures/data/utils.py @@ -112,10 +112,10 @@ def database_schema_has_revision(app, required_revision: str) -> bool: inclusive=True, assert_relative_length=False, ) + if any(revision.revision == required_revision for revision in revisions): + return True except RevisionError: continue - if any(revision.revision == required_revision for revision in revisions): - return True return False