udata 12.0.2.dev15__py3-none-any.whl → 13.0.1.dev21__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of udata might be problematic. Click here for more details.
- udata/api/__init__.py +1 -0
- udata/api_fields.py +10 -4
- udata/app.py +11 -10
- udata/auth/__init__.py +9 -10
- udata/auth/mails.py +137 -45
- udata/auth/views.py +5 -12
- udata/commands/__init__.py +2 -3
- udata/commands/info.py +1 -3
- udata/commands/tests/test_fixtures.py +6 -3
- udata/core/access_type/api.py +18 -0
- udata/core/access_type/constants.py +98 -0
- udata/core/access_type/models.py +44 -0
- udata/core/activity/models.py +1 -1
- udata/core/badges/models.py +1 -1
- udata/core/badges/tasks.py +35 -1
- udata/core/badges/tests/test_commands.py +2 -4
- udata/core/badges/tests/test_model.py +2 -2
- udata/core/badges/tests/test_tasks.py +55 -0
- udata/core/constants.py +1 -0
- udata/core/contact_point/models.py +8 -0
- udata/core/dataservices/api.py +3 -3
- udata/core/dataservices/apiv2.py +3 -1
- udata/core/dataservices/constants.py +0 -29
- udata/core/dataservices/models.py +44 -44
- udata/core/dataservices/rdf.py +2 -1
- udata/core/dataservices/search.py +5 -9
- udata/core/dataservices/tasks.py +33 -0
- udata/core/dataset/api_fields.py +11 -0
- udata/core/dataset/apiv2.py +11 -0
- udata/core/dataset/constants.py +0 -1
- udata/core/dataset/forms.py +29 -0
- udata/core/dataset/models.py +16 -4
- udata/core/dataset/rdf.py +2 -1
- udata/core/dataset/search.py +2 -2
- udata/core/dataset/tasks.py +86 -8
- udata/core/discussions/mails.py +63 -0
- udata/core/discussions/tasks.py +4 -18
- udata/core/metrics/__init__.py +0 -6
- udata/core/organization/api.py +3 -1
- udata/core/organization/mails.py +144 -0
- udata/core/organization/models.py +2 -1
- udata/core/organization/search.py +1 -1
- udata/core/organization/tasks.py +21 -49
- udata/core/pages/tests/test_api.py +0 -2
- udata/core/reuse/api.py +27 -1
- udata/core/reuse/mails.py +21 -0
- udata/core/reuse/models.py +10 -1
- udata/core/reuse/search.py +1 -1
- udata/core/reuse/tasks.py +2 -3
- udata/core/site/models.py +2 -6
- udata/core/spatial/tests/test_api.py +17 -20
- udata/core/spatial/tests/test_models.py +3 -3
- udata/core/user/mails.py +54 -0
- udata/core/user/models.py +2 -3
- udata/core/user/tasks.py +8 -23
- udata/core/user/tests/test_user_model.py +2 -6
- udata/entrypoints.py +0 -5
- udata/features/identicon/tests/test_backends.py +3 -13
- udata/forms/fields.py +3 -3
- udata/forms/widgets.py +2 -2
- udata/frontend/__init__.py +3 -32
- udata/harvest/actions.py +4 -9
- udata/harvest/api.py +5 -14
- udata/harvest/backends/__init__.py +20 -11
- udata/harvest/backends/base.py +2 -2
- udata/harvest/backends/ckan/harvesters.py +2 -1
- udata/harvest/backends/dcat.py +3 -0
- udata/harvest/backends/maaf.py +1 -0
- udata/harvest/commands.py +6 -4
- udata/harvest/forms.py +9 -6
- udata/harvest/tasks.py +3 -5
- udata/harvest/tests/ckan/test_ckan_backend.py +300 -337
- udata/harvest/tests/ckan/test_ckan_backend_errors.py +94 -99
- udata/harvest/tests/ckan/test_ckan_backend_filters.py +128 -122
- udata/harvest/tests/ckan/test_dkan_backend.py +39 -51
- udata/harvest/tests/dcat/datara--5a26b0f6-0ccf-46ad-ac58-734054b91977.rdf.xml +255 -0
- udata/harvest/tests/dcat/datara--f40c3860-7236-4b30-a141-23b8ae33f7b2.rdf.xml +289 -0
- udata/harvest/tests/factories.py +1 -1
- udata/harvest/tests/test_actions.py +11 -9
- udata/harvest/tests/test_api.py +4 -5
- udata/harvest/tests/test_base_backend.py +5 -4
- udata/harvest/tests/test_dcat_backend.py +50 -19
- udata/harvest/tests/test_models.py +2 -4
- udata/harvest/tests/test_notifications.py +2 -4
- udata/harvest/tests/test_tasks.py +2 -3
- udata/mail.py +90 -53
- udata/migrations/2025-01-05-dataservices-fields-changes.py +8 -14
- udata/migrations/2025-10-21-remove-ckan-harvest-modified-at.py +28 -0
- udata/migrations/2025-10-29-harvesters-sources-integrity.py +27 -0
- udata/mongo/taglist_field.py +3 -3
- udata/rdf.py +32 -15
- udata/sentry.py +3 -4
- udata/settings.py +7 -2
- udata/tags.py +5 -5
- udata/tasks.py +3 -3
- udata/templates/mail/message.html +65 -0
- udata/templates/mail/message.txt +16 -0
- udata/tests/__init__.py +40 -58
- udata/tests/api/__init__.py +87 -2
- udata/tests/api/test_activities_api.py +17 -23
- udata/tests/api/test_auth_api.py +2 -4
- udata/tests/api/test_contact_points.py +48 -54
- udata/tests/api/test_dataservices_api.py +57 -37
- udata/tests/api/test_datasets_api.py +146 -49
- udata/tests/api/test_me_api.py +4 -6
- udata/tests/api/test_organizations_api.py +19 -38
- udata/tests/api/test_reports_api.py +0 -4
- udata/tests/api/test_reuses_api.py +92 -19
- udata/tests/api/test_security_api.py +124 -0
- udata/tests/api/test_swagger.py +2 -3
- udata/tests/api/test_tags_api.py +6 -7
- udata/tests/api/test_transfer_api.py +0 -2
- udata/tests/api/test_user_api.py +8 -10
- udata/tests/apiv2/test_datasets.py +0 -4
- udata/tests/apiv2/test_me_api.py +0 -2
- udata/tests/apiv2/test_organizations.py +0 -2
- udata/tests/apiv2/test_swagger.py +2 -3
- udata/tests/apiv2/test_topics.py +0 -2
- udata/tests/cli/test_cli_base.py +14 -12
- udata/tests/cli/test_db_cli.py +51 -54
- udata/tests/contact_point/test_contact_point_models.py +2 -2
- udata/tests/dataservice/test_csv_adapter.py +2 -5
- udata/tests/dataservice/test_dataservice_rdf.py +8 -6
- udata/tests/dataservice/test_dataservice_tasks.py +36 -38
- udata/tests/dataset/test_csv_adapter.py +2 -5
- udata/tests/dataset/test_dataset_actions.py +2 -4
- udata/tests/dataset/test_dataset_commands.py +2 -4
- udata/tests/dataset/test_dataset_events.py +3 -3
- udata/tests/dataset/test_dataset_model.py +6 -7
- udata/tests/dataset/test_dataset_rdf.py +201 -12
- udata/tests/dataset/test_dataset_recommendations.py +2 -2
- udata/tests/dataset/test_dataset_tasks.py +66 -68
- udata/tests/dataset/test_resource_preview.py +39 -48
- udata/tests/dataset/test_transport_tasks.py +2 -2
- udata/tests/features/territories/__init__.py +0 -6
- udata/tests/features/territories/test_territories_api.py +25 -24
- udata/tests/forms/test_current_user_field.py +2 -2
- udata/tests/forms/test_dict_field.py +2 -4
- udata/tests/forms/test_extras_fields.py +2 -3
- udata/tests/forms/test_image_field.py +2 -2
- udata/tests/forms/test_model_field.py +2 -4
- udata/tests/forms/test_publish_as_field.py +2 -4
- udata/tests/forms/test_user_forms.py +26 -29
- udata/tests/frontend/test_auth.py +2 -3
- udata/tests/frontend/test_csv.py +5 -6
- udata/tests/frontend/test_error_handlers.py +2 -3
- udata/tests/frontend/test_hooks.py +5 -7
- udata/tests/frontend/test_markdown.py +3 -4
- udata/tests/helpers.py +2 -7
- udata/tests/metrics/test_metrics.py +52 -48
- udata/tests/metrics/test_tasks.py +154 -150
- udata/tests/organization/test_csv_adapter.py +2 -5
- udata/tests/organization/test_notifications.py +2 -4
- udata/tests/organization/test_organization_model.py +3 -4
- udata/tests/organization/test_organization_rdf.py +2 -8
- udata/tests/plugin.py +6 -110
- udata/tests/reuse/test_reuse_model.py +3 -4
- udata/tests/site/test_site_api.py +0 -2
- udata/tests/site/test_site_csv_exports.py +0 -2
- udata/tests/site/test_site_metrics.py +2 -4
- udata/tests/site/test_site_model.py +2 -2
- udata/tests/site/test_site_rdf.py +4 -7
- udata/tests/test_activity.py +3 -3
- udata/tests/test_api_fields.py +6 -9
- udata/tests/test_cors.py +0 -2
- udata/tests/test_dcat_commands.py +2 -3
- udata/tests/test_discussions.py +2 -7
- udata/tests/test_mail.py +150 -114
- udata/tests/test_migrations.py +413 -419
- udata/tests/test_model.py +10 -11
- udata/tests/test_notifications.py +2 -3
- udata/tests/test_owned.py +3 -3
- udata/tests/test_rdf.py +19 -15
- udata/tests/test_routing.py +5 -5
- udata/tests/test_storages.py +6 -5
- udata/tests/test_tags.py +2 -4
- udata/tests/test_topics.py +2 -4
- udata/tests/test_transfer.py +4 -5
- udata/tests/topic/test_topic_tasks.py +25 -27
- udata/tests/user/test_user_rdf.py +2 -8
- udata/tests/user/test_user_tasks.py +3 -5
- udata/tests/workers/test_jobs_commands.py +2 -2
- udata/tests/workers/test_tasks_routing.py +27 -27
- udata/translations/ar/LC_MESSAGES/udata.mo +0 -0
- udata/translations/ar/LC_MESSAGES/udata.po +369 -435
- udata/translations/de/LC_MESSAGES/udata.mo +0 -0
- udata/translations/de/LC_MESSAGES/udata.po +371 -437
- udata/translations/es/LC_MESSAGES/udata.mo +0 -0
- udata/translations/es/LC_MESSAGES/udata.po +369 -435
- udata/translations/fr/LC_MESSAGES/udata.mo +0 -0
- udata/translations/fr/LC_MESSAGES/udata.po +381 -447
- udata/translations/it/LC_MESSAGES/udata.mo +0 -0
- udata/translations/it/LC_MESSAGES/udata.po +371 -437
- udata/translations/pt/LC_MESSAGES/udata.mo +0 -0
- udata/translations/pt/LC_MESSAGES/udata.po +371 -437
- udata/translations/sr/LC_MESSAGES/udata.mo +0 -0
- udata/translations/sr/LC_MESSAGES/udata.po +372 -438
- udata/translations/udata.pot +379 -440
- udata/utils.py +14 -2
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/METADATA +1 -2
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/RECORD +205 -242
- udata/templates/mail/account_deleted.html +0 -5
- udata/templates/mail/account_deleted.txt +0 -6
- udata/templates/mail/account_inactivity.html +0 -40
- udata/templates/mail/account_inactivity.txt +0 -31
- udata/templates/mail/badge_added_association.html +0 -33
- udata/templates/mail/badge_added_association.txt +0 -11
- udata/templates/mail/badge_added_certified.html +0 -33
- udata/templates/mail/badge_added_certified.txt +0 -11
- udata/templates/mail/badge_added_company.html +0 -33
- udata/templates/mail/badge_added_company.txt +0 -11
- udata/templates/mail/badge_added_local_authority.html +0 -33
- udata/templates/mail/badge_added_local_authority.txt +0 -11
- udata/templates/mail/badge_added_public_service.html +0 -33
- udata/templates/mail/badge_added_public_service.txt +0 -11
- udata/templates/mail/discussion_closed.html +0 -47
- udata/templates/mail/discussion_closed.txt +0 -16
- udata/templates/mail/inactive_account_deleted.html +0 -5
- udata/templates/mail/inactive_account_deleted.txt +0 -6
- udata/templates/mail/membership_refused.html +0 -20
- udata/templates/mail/membership_refused.txt +0 -11
- udata/templates/mail/membership_request.html +0 -46
- udata/templates/mail/membership_request.txt +0 -12
- udata/templates/mail/new_discussion.html +0 -44
- udata/templates/mail/new_discussion.txt +0 -15
- udata/templates/mail/new_discussion_comment.html +0 -45
- udata/templates/mail/new_discussion_comment.txt +0 -16
- udata/templates/mail/new_member.html +0 -27
- udata/templates/mail/new_member.txt +0 -11
- udata/templates/mail/new_reuse.html +0 -37
- udata/templates/mail/new_reuse.txt +0 -9
- udata/templates/mail/test.html +0 -6
- udata/templates/mail/test.txt +0 -6
- udata/templates/mail/user_mail_card.html +0 -26
- udata/templates/security/email/base.html +0 -105
- udata/templates/security/email/base.txt +0 -6
- udata/templates/security/email/button.html +0 -3
- udata/templates/security/email/change_notice.html +0 -22
- udata/templates/security/email/change_notice.txt +0 -8
- udata/templates/security/email/confirmation_instructions.html +0 -20
- udata/templates/security/email/confirmation_instructions.txt +0 -7
- udata/templates/security/email/login_instructions.html +0 -19
- udata/templates/security/email/login_instructions.txt +0 -7
- udata/templates/security/email/reset_instructions.html +0 -24
- udata/templates/security/email/reset_instructions.txt +0 -9
- udata/templates/security/email/reset_notice.html +0 -11
- udata/templates/security/email/reset_notice.txt +0 -4
- udata/templates/security/email/welcome.html +0 -24
- udata/templates/security/email/welcome.txt +0 -9
- udata/templates/security/email/welcome_existing.html +0 -32
- udata/templates/security/email/welcome_existing.txt +0 -14
- udata/terms.md +0 -6
- udata/tests/frontend/__init__.py +0 -23
- udata/tests/metrics/conftest.py +0 -15
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/WHEEL +0 -0
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/entry_points.txt +0 -0
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/licenses/LICENSE +0 -0
- {udata-12.0.2.dev15.dist-info → udata-13.0.1.dev21.dist-info}/top_level.txt +0 -0
udata/tests/test_migrations.py
CHANGED
|
@@ -3,8 +3,10 @@ from datetime import datetime
|
|
|
3
3
|
from textwrap import dedent
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
|
+
from mongoengine.connection import get_db
|
|
6
7
|
|
|
7
8
|
from udata import migrations
|
|
9
|
+
from udata.tests.api import PytestOnlyDBTestCase
|
|
8
10
|
from udata.tests.helpers import assert_equal_dates
|
|
9
11
|
|
|
10
12
|
|
|
@@ -80,447 +82,439 @@ def mock(app, tmpdir, mocker):
|
|
|
80
82
|
yield m
|
|
81
83
|
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
(
|
|
87
|
-
("udata", "test"),
|
|
88
|
-
("udata:test.py", None),
|
|
89
|
-
("udata:test.py", None),
|
|
90
|
-
],
|
|
91
|
-
)
|
|
92
|
-
def test_get_migration(args):
|
|
93
|
-
migration = migrations.get(*args)
|
|
94
|
-
|
|
95
|
-
assert isinstance(migration, migrations.Migration)
|
|
96
|
-
assert migration.plugin == "udata"
|
|
97
|
-
assert migration.filename == "test.py"
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def test_list_available_migrations(mock):
|
|
101
|
-
mock.add_migration("udata", "01_core_migration.py")
|
|
102
|
-
mock.add_migration("test", "02_test_migration.py")
|
|
103
|
-
mock.add_migration("other", "03_other_migration.py")
|
|
104
|
-
# Should not list `__*.py` files
|
|
105
|
-
mock.add_migration("test", "__private.py")
|
|
106
|
-
# Should not list migrations for disabled plugin
|
|
107
|
-
mock.add_migration("disabled", "should_not_be_there.py", enable=False)
|
|
108
|
-
# Should not fail on plugins without migrations dir
|
|
109
|
-
mock.ensure_plugin("nomigrations")
|
|
110
|
-
|
|
111
|
-
availables = migrations.list_available()
|
|
112
|
-
|
|
113
|
-
assert len(availables) == 3
|
|
114
|
-
assert availables == [
|
|
115
|
-
migrations.Migration(p, f)
|
|
116
|
-
for p, f in (
|
|
117
|
-
("udata", "01_core_migration.py"),
|
|
118
|
-
("test", "02_test_migration.py"),
|
|
119
|
-
("other", "03_other_migration.py"),
|
|
120
|
-
)
|
|
121
|
-
]
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def test_get_record(db):
|
|
125
|
-
inserted = {
|
|
126
|
-
"plugin": "test",
|
|
127
|
-
"filename": "filename.py",
|
|
128
|
-
"ops": [
|
|
129
|
-
{
|
|
130
|
-
"date": datetime.utcnow(),
|
|
131
|
-
"type": "migrate",
|
|
132
|
-
"script": "script",
|
|
133
|
-
"output": "output",
|
|
134
|
-
"success": True,
|
|
135
|
-
}
|
|
136
|
-
],
|
|
137
|
-
}
|
|
138
|
-
db.migrations.insert_one(inserted)
|
|
139
|
-
|
|
140
|
-
record = migrations.get("test", "filename.py").record
|
|
141
|
-
|
|
142
|
-
assert record["plugin"] == inserted["plugin"]
|
|
143
|
-
assert record["filename"] == inserted["filename"]
|
|
144
|
-
|
|
145
|
-
op = record["ops"][0]
|
|
146
|
-
assert op["script"] == inserted["ops"][0]["script"]
|
|
147
|
-
assert op["output"] == inserted["ops"][0]["output"]
|
|
148
|
-
assert op["type"] == inserted["ops"][0]["type"]
|
|
149
|
-
assert op["success"]
|
|
150
|
-
assert_equal_dates(op["date"], inserted["ops"][0]["date"])
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def test_migration_execute(mock, db):
|
|
154
|
-
mock.add_migration(
|
|
155
|
-
"udata",
|
|
156
|
-
"migration.py",
|
|
157
|
-
"""\
|
|
158
|
-
import logging
|
|
159
|
-
|
|
160
|
-
log = logging.getLogger(__name__)
|
|
85
|
+
class MigrationsTest(PytestOnlyDBTestCase):
|
|
86
|
+
@pytest.fixture
|
|
87
|
+
def db(self):
|
|
88
|
+
return get_db()
|
|
161
89
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
inserted = db.test.find_one()
|
|
171
|
-
assert inserted is not None
|
|
172
|
-
assert inserted["key"] == "value"
|
|
173
|
-
assert output == [["info", "test"]]
|
|
174
|
-
|
|
175
|
-
assert db.migrations.count_documents({}) == 1
|
|
176
|
-
record = db.migrations.find_one()
|
|
177
|
-
assert record["plugin"] == "udata"
|
|
178
|
-
assert record["filename"] == "migration.py"
|
|
179
|
-
assert len(record["ops"]) == 1
|
|
180
|
-
|
|
181
|
-
op = record["ops"][0]
|
|
182
|
-
assert op["type"] == "migrate"
|
|
183
|
-
assert op["output"] == [["info", "test"]]
|
|
184
|
-
assert op["state"] == {}
|
|
185
|
-
assert isinstance(op["date"], datetime)
|
|
186
|
-
assert op["success"]
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
def test_migration_add_record(mock, db):
|
|
190
|
-
mock.add_migration(
|
|
191
|
-
"test",
|
|
192
|
-
"filename.py",
|
|
193
|
-
"""\
|
|
194
|
-
# whatever
|
|
195
|
-
|
|
196
|
-
def migrate():
|
|
197
|
-
pass
|
|
198
|
-
""",
|
|
90
|
+
@pytest.mark.parametrize(
|
|
91
|
+
"args",
|
|
92
|
+
[
|
|
93
|
+
("udata", "test.py"),
|
|
94
|
+
("udata", "test"),
|
|
95
|
+
("udata:test.py", None),
|
|
96
|
+
("udata:test.py", None),
|
|
97
|
+
],
|
|
199
98
|
)
|
|
99
|
+
def test_get_migration(self, args):
|
|
100
|
+
migration = migrations.get(*args)
|
|
101
|
+
|
|
102
|
+
assert isinstance(migration, migrations.Migration)
|
|
103
|
+
assert migration.plugin == "udata"
|
|
104
|
+
assert migration.filename == "test.py"
|
|
105
|
+
|
|
106
|
+
def test_list_available_migrations(self, mock):
|
|
107
|
+
mock.add_migration("udata", "01_core_migration.py")
|
|
108
|
+
mock.add_migration("test", "02_test_migration.py")
|
|
109
|
+
mock.add_migration("other", "03_other_migration.py")
|
|
110
|
+
# Should not list `__*.py` files
|
|
111
|
+
mock.add_migration("test", "__private.py")
|
|
112
|
+
# Should not list migrations for disabled plugin
|
|
113
|
+
mock.add_migration("disabled", "should_not_be_there.py", enable=False)
|
|
114
|
+
# Should not fail on plugins without migrations dir
|
|
115
|
+
mock.ensure_plugin("nomigrations")
|
|
116
|
+
|
|
117
|
+
availables = migrations.list_available()
|
|
118
|
+
|
|
119
|
+
assert len(availables) == 3
|
|
120
|
+
assert availables == [
|
|
121
|
+
migrations.Migration(p, f)
|
|
122
|
+
for p, f in (
|
|
123
|
+
("udata", "01_core_migration.py"),
|
|
124
|
+
("test", "02_test_migration.py"),
|
|
125
|
+
("other", "03_other_migration.py"),
|
|
126
|
+
)
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
def test_get_record(self, db):
|
|
130
|
+
inserted = {
|
|
131
|
+
"plugin": "test",
|
|
132
|
+
"filename": "filename.py",
|
|
133
|
+
"ops": [
|
|
134
|
+
{
|
|
135
|
+
"date": datetime.utcnow(),
|
|
136
|
+
"type": "migrate",
|
|
137
|
+
"script": "script",
|
|
138
|
+
"output": "output",
|
|
139
|
+
"success": True,
|
|
140
|
+
}
|
|
141
|
+
],
|
|
142
|
+
}
|
|
143
|
+
db.migrations.insert_one(inserted)
|
|
144
|
+
|
|
145
|
+
record = migrations.get("test", "filename.py").record
|
|
146
|
+
|
|
147
|
+
assert record["plugin"] == inserted["plugin"]
|
|
148
|
+
assert record["filename"] == inserted["filename"]
|
|
149
|
+
|
|
150
|
+
op = record["ops"][0]
|
|
151
|
+
assert op["script"] == inserted["ops"][0]["script"]
|
|
152
|
+
assert op["output"] == inserted["ops"][0]["output"]
|
|
153
|
+
assert op["type"] == inserted["ops"][0]["type"]
|
|
154
|
+
assert op["success"]
|
|
155
|
+
assert_equal_dates(op["date"], inserted["ops"][0]["date"])
|
|
156
|
+
|
|
157
|
+
def test_migration_execute(self, mock, db):
|
|
158
|
+
mock.add_migration(
|
|
159
|
+
"udata",
|
|
160
|
+
"migration.py",
|
|
161
|
+
"""\
|
|
162
|
+
import logging
|
|
163
|
+
|
|
164
|
+
log = logging.getLogger(__name__)
|
|
165
|
+
|
|
166
|
+
def migrate(db):
|
|
167
|
+
db.test.insert_one({'key': 'value'})
|
|
168
|
+
log.info('test')
|
|
169
|
+
""",
|
|
170
|
+
)
|
|
200
171
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
172
|
+
output = migrations.get("udata", "migration.py").execute()
|
|
173
|
+
|
|
174
|
+
inserted = db.test.find_one()
|
|
175
|
+
assert inserted is not None
|
|
176
|
+
assert inserted["key"] == "value"
|
|
177
|
+
assert output == [["info", "test"]]
|
|
178
|
+
|
|
179
|
+
assert db.migrations.count_documents({}) == 1
|
|
180
|
+
record = db.migrations.find_one()
|
|
181
|
+
assert record["plugin"] == "udata"
|
|
182
|
+
assert record["filename"] == "migration.py"
|
|
183
|
+
assert len(record["ops"]) == 1
|
|
184
|
+
|
|
185
|
+
op = record["ops"][0]
|
|
186
|
+
assert op["type"] == "migrate"
|
|
187
|
+
assert op["output"] == [["info", "test"]]
|
|
188
|
+
assert op["state"] == {}
|
|
189
|
+
assert isinstance(op["date"], datetime)
|
|
190
|
+
assert op["success"]
|
|
191
|
+
|
|
192
|
+
def test_migration_add_record(self, mock, db):
|
|
193
|
+
mock.add_migration(
|
|
194
|
+
"test",
|
|
195
|
+
"filename.py",
|
|
196
|
+
"""\
|
|
197
|
+
# whatever
|
|
198
|
+
|
|
199
|
+
def migrate():
|
|
200
|
+
pass
|
|
201
|
+
""",
|
|
202
|
+
)
|
|
217
203
|
|
|
218
|
-
|
|
219
|
-
mock.add_migration(
|
|
220
|
-
"test",
|
|
221
|
-
"filename.py",
|
|
222
|
-
"""\
|
|
223
|
-
# whatever
|
|
204
|
+
expected_output = [["info", "Recorded only"]]
|
|
224
205
|
|
|
225
|
-
|
|
226
|
-
pass
|
|
227
|
-
""",
|
|
228
|
-
)
|
|
206
|
+
output = migrations.get("test", "filename.py").execute(recordonly=True)
|
|
229
207
|
|
|
230
|
-
|
|
208
|
+
assert output == expected_output
|
|
231
209
|
|
|
232
|
-
|
|
210
|
+
migration = db.migrations.find_one()
|
|
211
|
+
assert migration["plugin"] == "test"
|
|
212
|
+
assert migration["filename"] == "filename.py"
|
|
233
213
|
|
|
234
|
-
|
|
214
|
+
op = migration["ops"][0]
|
|
215
|
+
assert op["script"].startswith("# whatever\n")
|
|
216
|
+
assert op["output"] == expected_output
|
|
217
|
+
assert op["type"] == "migrate"
|
|
218
|
+
assert op["success"]
|
|
235
219
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
220
|
+
def test_record_migration(self, mock, db):
|
|
221
|
+
mock.add_migration(
|
|
222
|
+
"test",
|
|
223
|
+
"filename.py",
|
|
224
|
+
"""\
|
|
225
|
+
# whatever
|
|
239
226
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
assert op["success"]
|
|
227
|
+
def migrate():
|
|
228
|
+
pass
|
|
229
|
+
""",
|
|
230
|
+
)
|
|
245
231
|
|
|
232
|
+
expected_output = [["info", "Recorded only"]]
|
|
246
233
|
|
|
247
|
-
|
|
248
|
-
with pytest.raises(migrations.MigrationError):
|
|
249
|
-
migrations.get("test", "filename.py").execute()
|
|
250
|
-
assert db.migrations.find_one() is None
|
|
234
|
+
output = migrations.get("test", "filename.py").execute(recordonly=True)
|
|
251
235
|
|
|
236
|
+
assert output == expected_output
|
|
252
237
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
migrations.get("test", "filename.py").execute()
|
|
257
|
-
assert db.migrations.find_one() is None
|
|
238
|
+
migration = db.migrations.find_one()
|
|
239
|
+
assert migration["plugin"] == "test"
|
|
240
|
+
assert migration["filename"] == "filename.py"
|
|
258
241
|
|
|
242
|
+
op = migration["ops"][0]
|
|
243
|
+
assert op["script"].startswith("# whatever\n")
|
|
244
|
+
assert op["output"] == expected_output
|
|
245
|
+
assert op["type"] == "migrate"
|
|
246
|
+
assert op["success"]
|
|
259
247
|
|
|
260
|
-
def
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
"""\
|
|
265
|
-
import logging
|
|
248
|
+
def test_execute_missing_plugin(self, db):
|
|
249
|
+
with pytest.raises(migrations.MigrationError):
|
|
250
|
+
migrations.get("test", "filename.py").execute()
|
|
251
|
+
assert db.migrations.find_one() is None
|
|
266
252
|
|
|
267
|
-
|
|
253
|
+
def test_execute_missing_migration(self, db, mock):
|
|
254
|
+
mock.ensure_plugin("test")
|
|
255
|
+
with pytest.raises(migrations.MigrationError):
|
|
256
|
+
migrations.get("test", "filename.py").execute()
|
|
257
|
+
assert db.migrations.find_one() is None
|
|
268
258
|
|
|
269
|
-
def
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
259
|
+
def test_execute_migration_error(self, mock, db):
|
|
260
|
+
mock.add_migration(
|
|
261
|
+
"udata",
|
|
262
|
+
"migration.py",
|
|
263
|
+
"""\
|
|
264
|
+
import logging
|
|
275
265
|
|
|
276
|
-
|
|
277
|
-
migrations.get("udata", "migration.py").execute()
|
|
278
|
-
|
|
279
|
-
exc = excinfo.value
|
|
280
|
-
assert isinstance(exc, migrations.MigrationError)
|
|
281
|
-
assert isinstance(exc.exc, ValueError)
|
|
282
|
-
assert exc.msg == "Error while executing migration"
|
|
283
|
-
assert exc.output == [["info", "test"]]
|
|
284
|
-
|
|
285
|
-
# Without rollback DB is left as it is
|
|
286
|
-
assert db.test.count_documents({}) == 1
|
|
287
|
-
inserted = db.test.find_one()
|
|
288
|
-
assert inserted is not None
|
|
289
|
-
assert inserted["key"] == "value"
|
|
290
|
-
|
|
291
|
-
# Failed migration is recorded
|
|
292
|
-
assert db.migrations.count_documents({}) == 1
|
|
293
|
-
record = db.migrations.find_one()
|
|
294
|
-
assert record["plugin"] == "udata"
|
|
295
|
-
assert record["filename"] == "migration.py"
|
|
296
|
-
assert len(record["ops"]) == 1
|
|
297
|
-
|
|
298
|
-
op = record["ops"][0]
|
|
299
|
-
assert op["type"] == "migrate"
|
|
300
|
-
assert op["output"] == [["info", "test"]]
|
|
301
|
-
assert op["state"] == {}
|
|
302
|
-
assert isinstance(op["date"], datetime)
|
|
303
|
-
assert not op["success"]
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
def test_execute_migration_error_with_rollback(mock, db):
|
|
307
|
-
mock.add_migration(
|
|
308
|
-
"udata",
|
|
309
|
-
"migration.py",
|
|
310
|
-
"""\
|
|
311
|
-
def migrate(db):
|
|
312
|
-
db.test.insert_one({'key': 'value'})
|
|
313
|
-
raise ValueError('error')
|
|
314
|
-
|
|
315
|
-
def rollback(db):
|
|
316
|
-
db.rollback_test.insert_one({'key': 'value'})
|
|
317
|
-
""",
|
|
318
|
-
)
|
|
266
|
+
log = logging.getLogger(__name__)
|
|
319
267
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
assert exc.msg == "Error while executing migration, rollback has been applied"
|
|
327
|
-
|
|
328
|
-
assert isinstance(exc.migrate_exc, migrations.MigrationError)
|
|
329
|
-
assert isinstance(exc.migrate_exc.exc, ValueError)
|
|
330
|
-
assert exc.migrate_exc.msg == "Error while executing migration"
|
|
331
|
-
|
|
332
|
-
# Migrate value is inserted
|
|
333
|
-
assert db.test.count_documents({}) == 1
|
|
334
|
-
# Rollback should be executed
|
|
335
|
-
assert db.rollback_test.count_documents({}) == 1
|
|
336
|
-
|
|
337
|
-
# DB is rollbacked if possible
|
|
338
|
-
assert db.migrations.count_documents({}) == 1
|
|
339
|
-
record = db.migrations.find_one()
|
|
340
|
-
assert record["plugin"] == "udata"
|
|
341
|
-
assert record["filename"] == "migration.py"
|
|
342
|
-
# Both failed migration and rollback are recorded
|
|
343
|
-
assert len(record["ops"]) == 2
|
|
344
|
-
|
|
345
|
-
# First the migration
|
|
346
|
-
op = record["ops"][0]
|
|
347
|
-
assert op["type"] == "migrate"
|
|
348
|
-
assert op["output"] == []
|
|
349
|
-
assert op["state"] == {}
|
|
350
|
-
assert isinstance(op["date"], datetime)
|
|
351
|
-
assert not op["success"]
|
|
352
|
-
|
|
353
|
-
# Then the rollback
|
|
354
|
-
op = record["ops"][1]
|
|
355
|
-
assert op["type"] == "rollback"
|
|
356
|
-
assert op["output"] == []
|
|
357
|
-
assert op["state"] == {}
|
|
358
|
-
assert isinstance(op["date"], datetime)
|
|
359
|
-
assert op["success"]
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
def test_execute_migration_error_with_state_rollback(mock, db):
|
|
363
|
-
mock.add_migration(
|
|
364
|
-
"udata",
|
|
365
|
-
"migration.py",
|
|
366
|
-
"""\
|
|
367
|
-
def migrate(db):
|
|
368
|
-
db.test.insert_one({'key': 'first'})
|
|
369
|
-
db._state['first'] = True
|
|
370
|
-
raise ValueError('error')
|
|
371
|
-
db.test.insert_two({'key': 'second'})
|
|
372
|
-
db._state['second'] = True
|
|
373
|
-
|
|
374
|
-
def rollback(db):
|
|
375
|
-
if db._state.get('first', False):
|
|
376
|
-
db.rollback_test.insert_one({'key': 'first'})
|
|
377
|
-
if db._state.get('second', False):
|
|
378
|
-
db.rollback_test.insert_one({'key': 'second'})
|
|
379
|
-
""",
|
|
380
|
-
)
|
|
268
|
+
def migrate(db):
|
|
269
|
+
db.test.insert_one({'key': 'value'})
|
|
270
|
+
log.info('test')
|
|
271
|
+
raise ValueError('error')
|
|
272
|
+
""",
|
|
273
|
+
)
|
|
381
274
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
def test_execute_migration_error_with_rollback_error(mock, db):
|
|
425
|
-
mock.add_migration(
|
|
426
|
-
"udata",
|
|
427
|
-
"migration.py",
|
|
428
|
-
"""\
|
|
429
|
-
def migrate(db):
|
|
430
|
-
db.test.insert_one({'key': 'value'})
|
|
431
|
-
raise ValueError('error')
|
|
432
|
-
|
|
433
|
-
def rollback(db):
|
|
434
|
-
db.rollback_test.insert_one({'key': 'value'})
|
|
435
|
-
raise ValueError('error')
|
|
436
|
-
""",
|
|
437
|
-
)
|
|
275
|
+
with pytest.raises(migrations.MigrationError) as excinfo:
|
|
276
|
+
migrations.get("udata", "migration.py").execute()
|
|
277
|
+
|
|
278
|
+
exc = excinfo.value
|
|
279
|
+
assert isinstance(exc, migrations.MigrationError)
|
|
280
|
+
assert isinstance(exc.exc, ValueError)
|
|
281
|
+
assert exc.msg == "Error while executing migration"
|
|
282
|
+
assert exc.output == [["info", "test"]]
|
|
283
|
+
|
|
284
|
+
# Without rollback DB is left as it is
|
|
285
|
+
assert db.test.count_documents({}) == 1
|
|
286
|
+
inserted = db.test.find_one()
|
|
287
|
+
assert inserted is not None
|
|
288
|
+
assert inserted["key"] == "value"
|
|
289
|
+
|
|
290
|
+
# Failed migration is recorded
|
|
291
|
+
assert db.migrations.count_documents({}) == 1
|
|
292
|
+
record = db.migrations.find_one()
|
|
293
|
+
assert record["plugin"] == "udata"
|
|
294
|
+
assert record["filename"] == "migration.py"
|
|
295
|
+
assert len(record["ops"]) == 1
|
|
296
|
+
|
|
297
|
+
op = record["ops"][0]
|
|
298
|
+
assert op["type"] == "migrate"
|
|
299
|
+
assert op["output"] == [["info", "test"]]
|
|
300
|
+
assert op["state"] == {}
|
|
301
|
+
assert isinstance(op["date"], datetime)
|
|
302
|
+
assert not op["success"]
|
|
303
|
+
|
|
304
|
+
def test_execute_migration_error_with_rollback(self, mock, db):
|
|
305
|
+
mock.add_migration(
|
|
306
|
+
"udata",
|
|
307
|
+
"migration.py",
|
|
308
|
+
"""\
|
|
309
|
+
def migrate(db):
|
|
310
|
+
db.test.insert_one({'key': 'value'})
|
|
311
|
+
raise ValueError('error')
|
|
312
|
+
|
|
313
|
+
def rollback(db):
|
|
314
|
+
db.rollback_test.insert_one({'key': 'value'})
|
|
315
|
+
""",
|
|
316
|
+
)
|
|
438
317
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
318
|
+
with pytest.raises(migrations.MigrationError) as excinfo:
|
|
319
|
+
migrations.get("udata", "migration.py").execute()
|
|
320
|
+
|
|
321
|
+
exc = excinfo.value
|
|
322
|
+
assert isinstance(exc, migrations.RollbackError)
|
|
323
|
+
assert exc.exc is None
|
|
324
|
+
assert exc.msg == "Error while executing migration, rollback has been applied"
|
|
325
|
+
|
|
326
|
+
assert isinstance(exc.migrate_exc, migrations.MigrationError)
|
|
327
|
+
assert isinstance(exc.migrate_exc.exc, ValueError)
|
|
328
|
+
assert exc.migrate_exc.msg == "Error while executing migration"
|
|
329
|
+
|
|
330
|
+
# Migrate value is inserted
|
|
331
|
+
assert db.test.count_documents({}) == 1
|
|
332
|
+
# Rollback should be executed
|
|
333
|
+
assert db.rollback_test.count_documents({}) == 1
|
|
334
|
+
|
|
335
|
+
# DB is rollbacked if possible
|
|
336
|
+
assert db.migrations.count_documents({}) == 1
|
|
337
|
+
record = db.migrations.find_one()
|
|
338
|
+
assert record["plugin"] == "udata"
|
|
339
|
+
assert record["filename"] == "migration.py"
|
|
340
|
+
# Both failed migration and rollback are recorded
|
|
341
|
+
assert len(record["ops"]) == 2
|
|
342
|
+
|
|
343
|
+
# First the migration
|
|
344
|
+
op = record["ops"][0]
|
|
345
|
+
assert op["type"] == "migrate"
|
|
346
|
+
assert op["output"] == []
|
|
347
|
+
assert op["state"] == {}
|
|
348
|
+
assert isinstance(op["date"], datetime)
|
|
349
|
+
assert not op["success"]
|
|
350
|
+
|
|
351
|
+
# Then the rollback
|
|
352
|
+
op = record["ops"][1]
|
|
353
|
+
assert op["type"] == "rollback"
|
|
354
|
+
assert op["output"] == []
|
|
355
|
+
assert op["state"] == {}
|
|
356
|
+
assert isinstance(op["date"], datetime)
|
|
357
|
+
assert op["success"]
|
|
358
|
+
|
|
359
|
+
def test_execute_migration_error_with_state_rollback(self, mock, db):
|
|
360
|
+
mock.add_migration(
|
|
361
|
+
"udata",
|
|
362
|
+
"migration.py",
|
|
363
|
+
"""\
|
|
364
|
+
def migrate(db):
|
|
365
|
+
db.test.insert_one({'key': 'first'})
|
|
366
|
+
db._state['first'] = True
|
|
367
|
+
raise ValueError('error')
|
|
368
|
+
db.test.insert_two({'key': 'second'})
|
|
369
|
+
db._state['second'] = True
|
|
370
|
+
|
|
371
|
+
def rollback(db):
|
|
372
|
+
if db._state.get('first', False):
|
|
373
|
+
db.rollback_test.insert_one({'key': 'first'})
|
|
374
|
+
if db._state.get('second', False):
|
|
375
|
+
db.rollback_test.insert_one({'key': 'second'})
|
|
376
|
+
""",
|
|
377
|
+
)
|
|
495
378
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
"
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
]
|
|
517
|
-
|
|
518
|
-
|
|
379
|
+
with pytest.raises(migrations.MigrationError) as excinfo:
|
|
380
|
+
migrations.get("udata", "migration.py").execute()
|
|
381
|
+
|
|
382
|
+
exc = excinfo.value
|
|
383
|
+
assert isinstance(exc, migrations.RollbackError)
|
|
384
|
+
assert exc.exc is None
|
|
385
|
+
assert exc.msg == "Error while executing migration, rollback has been applied"
|
|
386
|
+
|
|
387
|
+
assert isinstance(exc.migrate_exc, migrations.MigrationError)
|
|
388
|
+
assert isinstance(exc.migrate_exc.exc, ValueError)
|
|
389
|
+
assert exc.migrate_exc.msg == "Error while executing migration"
|
|
390
|
+
|
|
391
|
+
# Only the first value is inserted
|
|
392
|
+
assert db.test.count_documents({}) == 1
|
|
393
|
+
# Only the first rollback operation is executed
|
|
394
|
+
assert db.rollback_test.count_documents({}) == 1
|
|
395
|
+
|
|
396
|
+
# DB is rollbacked if possible
|
|
397
|
+
assert db.migrations.count_documents({}) == 1
|
|
398
|
+
record = db.migrations.find_one()
|
|
399
|
+
assert record["plugin"] == "udata"
|
|
400
|
+
assert record["filename"] == "migration.py"
|
|
401
|
+
# Both failed migration and rollback are recorded
|
|
402
|
+
assert len(record["ops"]) == 2
|
|
403
|
+
|
|
404
|
+
# First the migration
|
|
405
|
+
op = record["ops"][0]
|
|
406
|
+
assert op["type"] == "migrate"
|
|
407
|
+
assert op["output"] == []
|
|
408
|
+
assert op["state"] == {"first": True}
|
|
409
|
+
assert isinstance(op["date"], datetime)
|
|
410
|
+
assert not op["success"]
|
|
411
|
+
|
|
412
|
+
# Then the rollback
|
|
413
|
+
op = record["ops"][1]
|
|
414
|
+
assert op["type"] == "rollback"
|
|
415
|
+
assert op["output"] == []
|
|
416
|
+
assert op["state"] == {"first": True}
|
|
417
|
+
assert isinstance(op["date"], datetime)
|
|
418
|
+
assert op["success"]
|
|
419
|
+
|
|
420
|
+
def test_execute_migration_error_with_rollback_error(self, mock, db):
|
|
421
|
+
mock.add_migration(
|
|
422
|
+
"udata",
|
|
423
|
+
"migration.py",
|
|
424
|
+
"""\
|
|
425
|
+
def migrate(db):
|
|
426
|
+
db.test.insert_one({'key': 'value'})
|
|
427
|
+
raise ValueError('error')
|
|
428
|
+
|
|
429
|
+
def rollback(db):
|
|
430
|
+
db.rollback_test.insert_one({'key': 'value'})
|
|
431
|
+
raise ValueError('error')
|
|
432
|
+
""",
|
|
433
|
+
)
|
|
519
434
|
|
|
520
|
-
|
|
435
|
+
with pytest.raises(migrations.MigrationError) as excinfo:
|
|
436
|
+
migrations.get("udata", "migration.py").execute()
|
|
437
|
+
|
|
438
|
+
exc = excinfo.value
|
|
439
|
+
assert isinstance(exc, migrations.RollbackError)
|
|
440
|
+
assert isinstance(exc.exc, ValueError)
|
|
441
|
+
assert exc.msg == "Error while executing migration rollback"
|
|
442
|
+
|
|
443
|
+
assert isinstance(exc.migrate_exc, migrations.MigrationError)
|
|
444
|
+
assert isinstance(exc.migrate_exc.exc, ValueError)
|
|
445
|
+
assert exc.migrate_exc.msg == "Error while executing migration"
|
|
446
|
+
|
|
447
|
+
# Migrate value is inserted
|
|
448
|
+
assert db.test.count_documents({}) == 1
|
|
449
|
+
# Rollback should not be recorded
|
|
450
|
+
assert db.rollback_test.count_documents({}) == 1
|
|
451
|
+
|
|
452
|
+
# DB is rollbacked if possible
|
|
453
|
+
assert db.migrations.count_documents({}) == 1
|
|
454
|
+
record = db.migrations.find_one()
|
|
455
|
+
assert record["plugin"] == "udata"
|
|
456
|
+
assert record["filename"] == "migration.py"
|
|
457
|
+
# Both failed migration and rollback are recorded
|
|
458
|
+
assert len(record["ops"]) == 2
|
|
459
|
+
|
|
460
|
+
# First the migration
|
|
461
|
+
op = record["ops"][0]
|
|
462
|
+
assert op["type"] == "migrate"
|
|
463
|
+
assert op["output"] == []
|
|
464
|
+
assert op["state"] == {}
|
|
465
|
+
assert isinstance(op["date"], datetime)
|
|
466
|
+
assert not op["success"]
|
|
467
|
+
|
|
468
|
+
# Then the rollback
|
|
469
|
+
op = record["ops"][1]
|
|
470
|
+
assert op["type"] == "rollback"
|
|
471
|
+
assert op["output"] == []
|
|
472
|
+
assert op["state"] == {}
|
|
473
|
+
assert isinstance(op["date"], datetime)
|
|
474
|
+
assert not op["success"]
|
|
475
|
+
|
|
476
|
+
def test_execute_migration_dry_run(self, mock, db):
|
|
477
|
+
mock.add_migration(
|
|
478
|
+
"udata",
|
|
479
|
+
"migration.py",
|
|
480
|
+
"""\
|
|
481
|
+
import logging
|
|
482
|
+
|
|
483
|
+
log = logging.getLogger(__name__)
|
|
484
|
+
|
|
485
|
+
def migrate(db):
|
|
486
|
+
db.test.insert_one({'key': 'value'})
|
|
487
|
+
log.info('test')
|
|
488
|
+
""",
|
|
489
|
+
)
|
|
521
490
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
491
|
+
output = migrations.get("udata", "migration.py").execute(dryrun=True)
|
|
492
|
+
|
|
493
|
+
assert output == []
|
|
494
|
+
assert db.test.find_one() is None
|
|
495
|
+
assert db.migrations.count_documents({}) == 0
|
|
496
|
+
|
|
497
|
+
def test_unrecord_migration(self, db):
|
|
498
|
+
inserted = {
|
|
499
|
+
"plugin": "test",
|
|
500
|
+
"filename": "filename.py",
|
|
501
|
+
"ops": [
|
|
502
|
+
{
|
|
503
|
+
"date": datetime.utcnow(),
|
|
504
|
+
"type": "migrate",
|
|
505
|
+
"script": "script",
|
|
506
|
+
"output": "output",
|
|
507
|
+
"state": {},
|
|
508
|
+
"success": True,
|
|
509
|
+
}
|
|
510
|
+
],
|
|
511
|
+
}
|
|
512
|
+
db.migrations.insert_one(inserted)
|
|
513
|
+
|
|
514
|
+
migration = migrations.get("test", "filename.py")
|
|
515
|
+
|
|
516
|
+
# Remove the migration record, return True
|
|
517
|
+
assert migration.unrecord()
|
|
518
|
+
assert db.migrations.find_one() is None
|
|
519
|
+
# Already removed, return False
|
|
520
|
+
assert not migration.unrecord()
|