Initial commit

This commit is contained in:
2024-08-27 20:33:44 +02:00
commit 1f1832267d
14794 changed files with 1599592 additions and 0 deletions

View File

@@ -0,0 +1,265 @@
import datetime
from django.test import TestCase
from django.utils import timezone
from wagtail.blocks import StreamValue
from wagtail.blocks.migrations import migrate_operation
from wagtail.blocks.migrations.operations import (
RenameStreamChildrenOperation,
RenameStructChildrenOperation,
)
from wagtail.blocks.migrations.utils import (
InvalidBlockDefError,
apply_changes_to_raw_data,
)
from wagtail.signal_handlers import disable_reference_index_auto_update
from wagtail.test.streamfield_migrations import factories, models
from wagtail.test.streamfield_migrations.testutils import MigrationTestMixin
class TestExceptionRaisedInRawData(TestCase):
"""Directly test whether an exception is raised by apply_changes_to_raw_data for invalid defs.
This would happen in a situation where the user gives a block path which contains a block name
which is not present in the block definition in the project state at which the migration is
applied. (There should also be a block in the stream data with the said name for this to happen)
"""
def setUp(self):
raw_data = factories.SampleModelFactory(
content__0__char1__value="Char Block 1",
content__1="nestedstruct",
).content.raw_data
raw_data.extend(
[
{
"type": "invalid_name1",
"id": "0001",
"value": {"char1": "foo", "char2": "foo"},
},
{
"type": "invalid_name1",
"id": "0002",
"value": {"char1": "foo", "char2": "foo"},
},
]
)
raw_data[1]["value"]["invalid_name2"] = [
{"type": "char1", "value": "foo", "id": "0003"}
]
self.raw_data = raw_data
def test_rename_invalid_stream_child(self):
"""Test whether Exception is raised in when recursing through stream block data"""
with self.assertRaisesMessage(
InvalidBlockDefError, "No current block def named invalid_name1"
):
apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="invalid_name1",
operation=RenameStructChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
def test_rename_invalid_struct_child(self):
"""Test whether Exception is raised in when recursing through struct block data"""
with self.assertRaisesMessage(
InvalidBlockDefError, "No current block def named invalid_name2"
):
apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedstruct.invalid_name2",
operation=RenameStreamChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
class BadDataMigrationTestCase(TestCase, MigrationTestMixin):
model = models.SamplePage
default_operation_and_block_path = [
(
RenameStructChildrenOperation(old_name="char1", new_name="renamed1"),
"invalid_name1",
)
]
app_name = "streamfield_migration_tests"
def create_instance(self):
instance = factories.SamplePageFactory(
content__0__char1__value="Char Block 1",
content__1="nestedstruct",
)
self.instance = instance
def append_invalid_instance_data(self):
raw_data = self.instance.content.raw_data
raw_data.extend(
[
{
"type": "invalid_name1",
"id": "0001",
"value": {"char1": "foo", "char2": "foo"},
},
{
"type": "invalid_name1",
"id": "0002",
"value": {"char1": "foo", "char2": "foo"},
},
]
)
stream_block = self.instance.content.stream_block
self.instance.content = StreamValue(
stream_block=stream_block, stream_data=raw_data, is_lazy=True
)
self.instance.save()
def create_invalid_revision(self, delta):
self.append_invalid_instance_data()
invalid_revision = self.create_revision(delta)
# remove the invalid data from the instance
raw_data = self.instance.content.raw_data
raw_data = raw_data[:2]
stream_block = self.instance.content.stream_block
self.instance.content = StreamValue(
stream_block=stream_block, stream_data=raw_data, is_lazy=True
)
self.instance.save()
return invalid_revision.id, invalid_revision.created_at
def create_revision(self, delta):
revision = self.instance.save_revision()
revision.created_at = timezone.now() - datetime.timedelta(days=(delta))
revision.save()
return revision
class TestExceptionRaisedForInstance(BadDataMigrationTestCase):
"""Exception should always be raised when applying migration if it occurs while migrating the
instance data"""
def setUp(self):
with disable_reference_index_auto_update():
self.create_instance()
self.append_invalid_instance_data()
def test_migrate(self):
with self.assertRaisesMessage(
InvalidBlockDefError,
"Invalid block def in {} object ({})".format(
self.instance.__class__.__name__, self.instance.id
),
):
self.apply_migration(
revisions_from=timezone.now() + datetime.timedelta(days=2),
)
class TestExceptionRaisedForLatestRevision(BadDataMigrationTestCase):
"""Exception should always be raised when applying migration if it occurs while migrating the
latest revision data"""
def setUp(self):
with disable_reference_index_auto_update():
self.create_instance()
for i in range(4):
self.create_revision(5 - i)
(
self.invalid_revision_id,
self.invalid_revision_created_at,
) = self.create_invalid_revision(0)
def test_migrate(self):
with self.assertRaisesMessage(
InvalidBlockDefError,
"Invalid block def in {} object ({}) for revision id ({}) created at {}".format(
self.instance.__class__.__name__,
self.instance.id,
self.invalid_revision_id,
self.invalid_revision_created_at,
),
):
self.apply_migration(revisions_from=None)
class TestExceptionRaisedForLiveRevision(BadDataMigrationTestCase):
"""Exception should always be raised when applying migration if it occurs while migrating the
live revision data"""
def setUp(self):
with disable_reference_index_auto_update():
self.create_instance()
(
self.invalid_revision_id,
self.invalid_revision_created_at,
) = self.create_invalid_revision(5)
self.instance.live_revision_id = self.invalid_revision_id
self.instance.save()
for i in range(1, 5):
self.create_revision(5 - i)
def test_migrate(self):
with self.assertRaisesMessage(
InvalidBlockDefError,
"Invalid block def in {} object ({}) for revision id ({}) created at {}".format(
self.instance.__class__.__name__,
self.instance.id,
self.invalid_revision_id,
self.invalid_revision_created_at,
),
):
self.apply_migration(revisions_from=None)
class TestExceptionIgnoredForOtherRevisions(BadDataMigrationTestCase):
"""Exception should not be be raised when applying migration if it occurs while migrating
revision data which is not of a live or latest revision. Instead an exception should be logged"""
model = models.SamplePage
def setUp(self):
with disable_reference_index_auto_update():
self.create_instance()
(
self.invalid_revision_id,
self.invalid_revision_created_at,
) = self.create_invalid_revision(5)
for i in range(1, 5):
self.create_revision(5 - i)
def test_migrate(self):
with self.assertLogs(level="ERROR") as cm:
self.apply_migration(revisions_from=None)
self.assertEqual(
cm.output[0].splitlines()[0],
"ERROR:{}:Invalid block def in {} object ({}) for revision id ({}) created at {}".format(
migrate_operation.__name__,
self.instance.__class__.__name__,
self.instance.id,
self.invalid_revision_id,
self.invalid_revision_created_at,
),
)
self.assertEqual(
cm.output[0].splitlines()[-1],
"{}: No current block def named invalid_name1".format(
InvalidBlockDefError.__module__
+ "."
+ InvalidBlockDefError.__name__
),
)

View File

@@ -0,0 +1,59 @@
from django.test import TestCase
from wagtail.blocks.migrations.operations import (
RemoveStreamChildrenOperation,
RenameStreamChildrenOperation,
)
from wagtail.test.streamfield_migrations import models
from wagtail.test.streamfield_migrations.testutils import MigrationTestMixin
class MigrationNameTest(TestCase, MigrationTestMixin):
model = models.SamplePage
app_name = "wagtail_streamfield_migration_toolkit_test"
def test_rename(self):
operations_and_block_path = [
(
RenameStreamChildrenOperation(old_name="char1", new_name="renamed1"),
"",
)
]
migration = self.init_migration(
operations_and_block_path=operations_and_block_path
)
suggested_name = migration.suggest_name()
self.assertEqual(suggested_name, "rename_char1_to_renamed1")
def test_remove(self):
operations_and_block_path = [
(
RemoveStreamChildrenOperation(name="char1"),
"",
)
]
migration = self.init_migration(
operations_and_block_path=operations_and_block_path
)
suggested_name = migration.suggest_name()
self.assertEqual(suggested_name, "remove_char1")
def test_multiple(self):
operations_and_block_path = [
(
RenameStreamChildrenOperation(old_name="char1", new_name="renamed1"),
"",
),
(
RemoveStreamChildrenOperation(name="char1"),
"simplestruct",
),
]
migration = self.init_migration(
operations_and_block_path=operations_and_block_path
)
suggested_name = migration.suggest_name()
self.assertEqual(suggested_name, "rename_char1_to_renamed1_remove_char1")

View File

@@ -0,0 +1,252 @@
import datetime
import json
from django.db import connection
from django.db.models import F, JSONField, TextField
from django.db.models.functions import Cast
from django.test import TestCase
from django.utils import timezone
from wagtail.blocks.migrations.operations import RenameStreamChildrenOperation
from wagtail.test.streamfield_migrations import factories, models
from wagtail.test.streamfield_migrations.testutils import MigrationTestMixin
# TODO test multiple operations in one go
class BaseMigrationTest(TestCase, MigrationTestMixin):
factory = None
has_revisions = False
default_operation_and_block_path = [
(
RenameStreamChildrenOperation(old_name="char1", new_name="renamed1"),
"",
)
]
app_name = None
def _get_test_instances(self):
return [
self.factory(
content__0__char1="Test char 1",
content__1__char1="Test char 2",
content__2__char2="Test char 3",
content__3__char2="Test char 4",
),
self.factory(
content__0__char1="Test char 1",
content__1__char1="Test char 2",
content__2__char2="Test char 3",
),
self.factory(
content__0__char2="Test char 1",
content__1__char2="Test char 2",
content__2__char2="Test char 3",
),
]
def setUp(self):
instances = self._get_test_instances()
self.original_raw_data = {}
self.original_revisions = {}
for instance in instances:
self.original_raw_data[instance.id] = instance.content.raw_data
if self.has_revisions:
for i in range(5):
revision = instance.save_revision()
revision.created_at = timezone.now() - datetime.timedelta(
days=(5 - i)
)
revision.save()
if i == 1:
instance.live_revision = revision
instance.save()
self.original_revisions[instance.id] = list(
instance.revisions.all().order_by("id")
)
def assertBlocksRenamed(self, old_content, new_content, is_altered=True):
for old_block, new_block in zip(old_content, new_content):
self.assertEqual(old_block["id"], new_block["id"])
if is_altered and old_block["type"] == "char1":
self.assertEqual(new_block["type"], "renamed1")
else:
self.assertEqual(old_block["type"], new_block["type"])
def _test_migrate_stream_data(self):
"""Test whether the stream data of the model instances have been updated properly
Apply the migration and then query the raw data of the updated instances. Compare with
original raw data and check whether all relevant `char1` blocks have been renamed and
whether ids and other block types are intact.
"""
self.apply_migration()
instances = self.model.objects.all().annotate(
raw_content=Cast(F("content"), JSONField())
)
for instance in instances:
prev_content = self.original_raw_data[instance.id]
self.assertBlocksRenamed(
old_content=prev_content, new_content=instance.raw_content
)
# TODO test multiple operations applied in one migration
def _test_migrate_revisions(self):
"""Test whether all revisions have been updated properly
Applying migration with `revisions_from=None`, so all revisions should be updated.
"""
self.apply_migration()
instances = self.model.objects.all()
for instance in instances:
old_revisions = self.original_revisions[instance.id]
for old_revision, new_revision in zip(
old_revisions, instance.revisions.all().order_by("id")
):
old_content = json.loads(old_revision.content["content"])
new_content = json.loads(new_revision.content["content"])
self.assertBlocksRenamed(
old_content=old_content, new_content=new_content
)
def _test_always_migrate_live_and_latest_revisions(self):
"""Test whether latest and live revisions are always updated
Applying migration with `revisions_from` set to a date in the future, so there should be
no revisions which are made after the date. Only the live and latest revisions should
update in this case.
"""
revisions_from = timezone.now() + datetime.timedelta(days=2)
self.apply_migration(revisions_from=revisions_from)
instances = self.model.objects.all()
for instance in instances:
old_revisions = self.original_revisions[instance.id]
for old_revision, new_revision in zip(
old_revisions, instance.revisions.all().order_by("id")
):
is_latest_or_live = old_revision.id == instance.live_revision_id or (
old_revision.id == instance.latest_revision_id
)
old_content = json.loads(old_revision.content["content"])
new_content = json.loads(new_revision.content["content"])
self.assertBlocksRenamed(
old_content=old_content,
new_content=new_content,
is_altered=is_latest_or_live,
)
def _test_migrate_revisions_from_date(self):
"""Test whether revisions from a given date onwards are updated
Applying migration with `revisions_from` set to a date between the created date of the first
and last revision, so only the revisions after the date and the live and latest revision
should be updated.
"""
revisions_from = timezone.now() - datetime.timedelta(days=2)
self.apply_migration(revisions_from=revisions_from)
instances = self.model.objects.all()
for instance in instances:
old_revisions = self.original_revisions[instance.id]
for old_revision, new_revision in zip(
old_revisions, instance.revisions.all().order_by("id")
):
is_latest_or_live = old_revision.id == instance.live_revision_id or (
old_revision.id == instance.latest_revision_id
)
is_after_revisions_from = old_revision.created_at > revisions_from
is_altered = is_latest_or_live or is_after_revisions_from
old_content = json.loads(old_revision.content["content"])
new_content = json.loads(new_revision.content["content"])
self.assertBlocksRenamed(
old_content=old_content,
new_content=new_content,
is_altered=is_altered,
)
class TestNonPageModelWithoutRevisions(BaseMigrationTest):
model = models.SampleModel
factory = factories.SampleModelFactory
has_revisions = False
app_name = "streamfield_migration_tests"
def test_migrate_stream_data(self):
self._test_migrate_stream_data()
class TestPage(BaseMigrationTest):
model = models.SamplePage
factory = factories.SamplePageFactory
has_revisions = True
app_name = "streamfield_migration_tests"
def test_migrate_stream_data(self):
self._test_migrate_stream_data()
def test_migrate_revisions(self):
self._test_migrate_revisions()
def test_always_migrate_live_and_latest_revisions(self):
self._test_always_migrate_live_and_latest_revisions()
def test_migrate_revisions_from_date(self):
self._test_migrate_revisions_from_date()
class TestNullStreamField(BaseMigrationTest):
"""
Migrations are processed if the underlying JSON is null.
This might occur if we're operating on a StreamField that was added to a model that
had existing records.
"""
model = models.SamplePage
factory = factories.SamplePageFactory
has_revisions = True
app_name = "streamfield_migration_tests"
def _get_test_instances(self):
return self.factory.create_batch(1, content=None)
def setUp(self):
super().setUp()
# Bypass StreamField/StreamBlock processing that cast a None stream field value
# to the empty StreamValue, and set the underlying JSON to null.
with connection.cursor() as cursor:
cursor.execute(f"UPDATE {self.model._meta.db_table} SET content = 'null'")
def assert_null_content(self):
"""
The raw JSON of all instances for this test is null.
"""
instances = self.model.objects.all().annotate(
raw_content=Cast(F("content"), TextField())
)
for instance in instances:
with self.subTest(instance=instance):
self.assertEqual(instance.raw_content, "null")
def test_migrate_stream_data(self):
self.assert_null_content()
self.apply_migration()
self.assert_null_content()

View File

@@ -0,0 +1,783 @@
from django.test import TestCase
from wagtail.blocks.migrations.operations import (
RemoveStreamChildrenOperation,
RemoveStructChildrenOperation,
RenameStreamChildrenOperation,
RenameStructChildrenOperation,
)
from wagtail.blocks.migrations.utils import apply_changes_to_raw_data
from wagtail.test.streamfield_migrations import factories, models
class FieldStructStreamChildBlockTest(TestCase):
"""Tests involving changes to children of a StreamBlock nested inside a StructBlock
We use `nestedstruct.simplestream` blocks here.
"""
def setUp(self):
raw_data = factories.SampleModelFactory(
content__0__char1__value="Char Block 1",
content__1="nestedstruct",
content__1__nestedstruct__list1__0__value="a",
content__1__nestedstruct__stream1__0__char1__value="Char Block 1",
content__1__nestedstruct__stream1__1__char2__value="Char Block 2",
content__1__nestedstruct__stream1__2__char1__value="Char Block 1",
content__2="nestedstruct",
content__2__nestedstruct__list1__0__value="a",
content__2__nestedstruct__stream1__0__char1__value="Char Block 1",
content__3="simplestream",
content__3__simplestream__0__char1__value="Char Block 1",
content__3__simplestream__1__char2__value="Char Block 2",
).content.raw_data
self.raw_data = raw_data
def test_blocks_and_data_not_operated_on_intact(self):
"""Test whether other blocks and data not passed to an operation are intact.
We are checking whether the parts of the data which are not passed to an operation are
intact. Since the recursion process depends just on the block path and block structure,
this check is independent of the operation used. We will use a rename operation for now.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedstruct.stream1",
operation=RenameStreamChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(altered_raw_data[0], self.raw_data[0])
self.assertEqual(altered_raw_data[3], self.raw_data[3])
self.assertEqual(altered_raw_data[1]["id"], self.raw_data[1]["id"])
self.assertEqual(altered_raw_data[2]["id"], self.raw_data[2]["id"])
self.assertEqual(altered_raw_data[1]["type"], self.raw_data[1]["type"])
self.assertEqual(altered_raw_data[2]["type"], self.raw_data[2]["type"])
for key in self.raw_data[1]["value"].keys():
self.assertIn(key, altered_raw_data[1]["value"])
for key in self.raw_data[1]["value"].keys():
self.assertIn(key, altered_raw_data[2]["value"])
self.assertEqual(
altered_raw_data[1]["value"]["char1"], self.raw_data[1]["value"]["char1"]
)
self.assertEqual(
altered_raw_data[2]["value"]["char1"], self.raw_data[2]["value"]["char1"]
)
self.assertEqual(
altered_raw_data[1]["value"]["struct1"],
self.raw_data[1]["value"]["struct1"],
)
self.assertEqual(
altered_raw_data[2]["value"]["struct1"],
self.raw_data[2]["value"]["struct1"],
)
self.assertEqual(
altered_raw_data[1]["value"]["list1"], self.raw_data[1]["value"]["list1"]
)
self.assertEqual(
altered_raw_data[2]["value"]["list1"], self.raw_data[2]["value"]["list1"]
)
def test_rename(self):
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedstruct.stream1",
operation=RenameStreamChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(altered_raw_data[1]["value"]["stream1"][0]["type"], "renamed1")
self.assertEqual(altered_raw_data[1]["value"]["stream1"][2]["type"], "renamed1")
self.assertEqual(altered_raw_data[2]["value"]["stream1"][0]["type"], "renamed1")
self.assertEqual(
altered_raw_data[1]["value"]["stream1"][0]["id"],
self.raw_data[1]["value"]["stream1"][0]["id"],
)
self.assertEqual(
altered_raw_data[1]["value"]["stream1"][2]["id"],
self.raw_data[1]["value"]["stream1"][2]["id"],
)
self.assertEqual(
altered_raw_data[2]["value"]["stream1"][0]["id"],
self.raw_data[2]["value"]["stream1"][0]["id"],
)
self.assertEqual(
altered_raw_data[1]["value"]["stream1"][0]["value"],
self.raw_data[1]["value"]["stream1"][0]["value"],
)
self.assertEqual(
altered_raw_data[1]["value"]["stream1"][2]["value"],
self.raw_data[1]["value"]["stream1"][2]["value"],
)
self.assertEqual(
altered_raw_data[2]["value"]["stream1"][0]["value"],
self.raw_data[2]["value"]["stream1"][0]["value"],
)
self.assertEqual(
altered_raw_data[1]["value"]["stream1"][1],
self.raw_data[1]["value"]["stream1"][1],
)
def test_remove(self):
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedstruct.stream1",
operation=RemoveStreamChildrenOperation(name="char1"),
streamfield=models.SampleModel.content,
)
self.assertEqual(len(altered_raw_data[1]["value"]["stream1"]), 1)
self.assertEqual(len(altered_raw_data[2]["value"]["stream1"]), 0)
self.assertEqual(
altered_raw_data[1]["value"]["stream1"][0],
self.raw_data[1]["value"]["stream1"][1],
)
class FieldStructStructChildBlockTest(TestCase):
"""Tests involving changes to a children of a StructBlock nested inside a StructBlock
We use `nestedstruct.simplestruct` blocks here
"""
def setUp(self):
raw_data = factories.SampleModelFactory(
content__0__char1__value="Char Block 1",
content__1="nestedstruct",
content__1__nestedstruct__list1__0__value="a",
content__2="nestedstruct",
content__2__nestedstruct__list1__0__value="a",
content__3="simplestruct",
).content.raw_data
self.raw_data = raw_data
def test_blocks_and_data_not_operated_on_intact(self):
"""Test whether other blocks and data not passed to an operation are intact.
We are checking whether the parts of the data which are not passed to an operation are
intact. Since the recursion process depends just on the block path and block structure,
this check is independent of the operation used. We will use a rename operation for now.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedstruct.struct1",
operation=RenameStructChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(altered_raw_data[0], self.raw_data[0])
self.assertEqual(altered_raw_data[3], self.raw_data[3])
self.assertEqual(altered_raw_data[1]["type"], self.raw_data[1]["type"])
self.assertEqual(altered_raw_data[2]["type"], self.raw_data[2]["type"])
self.assertEqual(altered_raw_data[1]["id"], self.raw_data[1]["id"])
self.assertEqual(altered_raw_data[2]["id"], self.raw_data[2]["id"])
for key in self.raw_data[1]["value"].keys():
self.assertIn(key, altered_raw_data[1]["value"])
for key in self.raw_data[1]["value"].keys():
self.assertIn(key, altered_raw_data[2]["value"])
self.assertEqual(
altered_raw_data[1]["value"]["char1"], self.raw_data[1]["value"]["char1"]
)
self.assertEqual(
altered_raw_data[2]["value"]["char1"], self.raw_data[2]["value"]["char1"]
)
self.assertEqual(
altered_raw_data[1]["value"]["stream1"],
self.raw_data[1]["value"]["stream1"],
)
self.assertEqual(
altered_raw_data[2]["value"]["stream1"],
self.raw_data[2]["value"]["stream1"],
)
self.assertEqual(
altered_raw_data[1]["value"]["list1"], self.raw_data[1]["value"]["list1"]
)
self.assertEqual(
altered_raw_data[2]["value"]["list1"], self.raw_data[2]["value"]["list1"]
)
def test_rename(self):
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedstruct.struct1",
operation=RenameStructChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertNotIn("char1", altered_raw_data[1]["value"]["struct1"])
self.assertNotIn("char1", altered_raw_data[2]["value"]["struct1"])
self.assertIn("renamed1", altered_raw_data[2]["value"]["struct1"])
self.assertIn("renamed1", altered_raw_data[2]["value"]["struct1"])
self.assertIn("char2", altered_raw_data[1]["value"]["struct1"])
self.assertIn("char2", altered_raw_data[2]["value"]["struct1"])
def test_remove(self):
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedstruct.struct1",
operation=RemoveStructChildrenOperation(name="char1"),
streamfield=models.SampleModel.content,
)
self.assertEqual(len(altered_raw_data[1]["value"]["struct1"]), 1)
self.assertEqual(len(altered_raw_data[2]["value"]["struct1"]), 1)
self.assertNotIn("char1", altered_raw_data[1]["value"]["struct1"])
self.assertNotIn("char1", altered_raw_data[2]["value"]["struct1"])
self.assertIn("char2", altered_raw_data[1]["value"]["struct1"])
self.assertIn("char2", altered_raw_data[2]["value"]["struct1"])
class FieldStreamStreamChildBlockTest(TestCase):
"""Tests involving changes to children of a StreamBlock nested inside a StreamBlock.
We use `nestedstream.stream1` blocks here.
"""
def setUp(self):
raw_data = factories.SampleModelFactory(
content__0__char1__value="Char Block 1",
content__1="nestedstream",
content__1__nestedstream__0__char1__value="Char Block 1",
content__1__nestedstream__1="stream1",
content__1__nestedstream__1__stream1__0__char1__value="Char Block 1",
content__1__nestedstream__1__stream1__1__char2__value="Char Block 2",
content__1__nestedstream__1__stream1__2__char1__value="Char Block 1",
content__1__nestedstream__2="stream1",
content__1__nestedstream__2__stream1__0__char1__value="Char Block 1",
content__2="nestedstream",
content__2__nestedstream__0="stream1",
content__2__nestedstream__0__stream1__0__char1__value="Char Block 1",
content__3="simplestream",
content__3__simplestream__0__char1__value="Char Block 1",
).content.raw_data
self.raw_data = raw_data
def test_blocks_and_data_not_operated_on_intact(self):
"""Test whether other blocks and data not passed to an operation are intact.
We are checking whether the parts of the data which are not passed to an operation are
intact. Since the recursion process depends just on the block path and block structure,
this check is independent of the operation used. We will use a rename operation for now.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedstream.stream1",
operation=RenameStreamChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(altered_raw_data[0], self.raw_data[0])
self.assertEqual(altered_raw_data[3], self.raw_data[3])
self.assertEqual(altered_raw_data[1]["type"], self.raw_data[1]["type"])
self.assertEqual(altered_raw_data[2]["type"], self.raw_data[2]["type"])
self.assertEqual(altered_raw_data[1]["id"], self.raw_data[1]["id"])
self.assertEqual(altered_raw_data[2]["id"], self.raw_data[2]["id"])
self.assertEqual(altered_raw_data[1]["value"][0], self.raw_data[1]["value"][0])
self.assertEqual(
altered_raw_data[1]["value"][1]["type"],
self.raw_data[1]["value"][1]["type"],
)
self.assertEqual(
altered_raw_data[1]["value"][2]["type"],
self.raw_data[1]["value"][2]["type"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["type"],
self.raw_data[2]["value"][0]["type"],
)
self.assertEqual(
altered_raw_data[1]["value"][1]["id"],
self.raw_data[1]["value"][1]["id"],
)
self.assertEqual(
altered_raw_data[1]["value"][2]["id"],
self.raw_data[1]["value"][2]["id"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["id"],
self.raw_data[2]["value"][0]["id"],
)
def test_rename(self):
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedstream.stream1",
operation=RenameStreamChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(
altered_raw_data[1]["value"][1]["value"][0]["type"], "renamed1"
)
self.assertEqual(
altered_raw_data[1]["value"][1]["value"][2]["type"], "renamed1"
)
self.assertEqual(
altered_raw_data[1]["value"][2]["value"][0]["type"], "renamed1"
)
self.assertEqual(
altered_raw_data[2]["value"][0]["value"][0]["type"], "renamed1"
)
self.assertEqual(
altered_raw_data[1]["value"][1]["value"][0]["id"],
self.raw_data[1]["value"][1]["value"][0]["id"],
)
self.assertEqual(
altered_raw_data[1]["value"][1]["value"][2]["id"],
self.raw_data[1]["value"][1]["value"][2]["id"],
)
self.assertEqual(
altered_raw_data[1]["value"][2]["value"][0]["id"],
self.raw_data[1]["value"][2]["value"][0]["id"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["value"][0]["id"],
self.raw_data[2]["value"][0]["value"][0]["id"],
)
self.assertEqual(
altered_raw_data[1]["value"][1]["value"][1],
self.raw_data[1]["value"][1]["value"][1],
)
def test_remove(self):
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedstream.stream1",
operation=RemoveStreamChildrenOperation(name="char1"),
streamfield=models.SampleModel.content,
)
self.assertEqual(len(altered_raw_data[1]["value"][1]["value"]), 1)
self.assertEqual(len(altered_raw_data[1]["value"][2]["value"]), 0)
self.assertEqual(len(altered_raw_data[2]["value"][0]["value"]), 0)
self.assertEqual(
altered_raw_data[1]["value"][1]["value"][0],
self.raw_data[1]["value"][1]["value"][1],
)
class FieldStreamStructChildBlockTest(TestCase):
"""Tests involving changes to children of a StructBlock nested inside a StreamBlock.
We use `nestedstream.simplestruct` blocks here.
"""
def setUp(self):
raw_data = factories.SampleModelFactory(
content__0__char1__value="Char Block 1",
content__1="nestedstream",
content__1__nestedstream__0__char1="Char Block 1",
content__1__nestedstream__1="struct1",
content__1__nestedstream__2="struct1",
content__2="nestedstream",
content__2__nestedstream__0="struct1",
content__3="simplestream",
content__3__simplestream__0__char1__value="Char Block 1",
).content.raw_data
self.raw_data = raw_data
def test_blocks_and_data_not_operated_on_intact(self):
"""Test whether other blocks and data not passed to an operation are intact.
We are checking whether the parts of the data which are not passed to an operation are
intact. Since the recursion process depends just on the block path and block structure,
this check is independent of the operation used. We will use a rename operation for now.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedstream.struct1",
operation=RenameStructChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(altered_raw_data[0], self.raw_data[0])
self.assertEqual(altered_raw_data[3], self.raw_data[3])
self.assertEqual(altered_raw_data[1]["type"], self.raw_data[1]["type"])
self.assertEqual(altered_raw_data[2]["type"], self.raw_data[2]["type"])
self.assertEqual(altered_raw_data[1]["id"], self.raw_data[1]["id"])
self.assertEqual(altered_raw_data[2]["id"], self.raw_data[2]["id"])
self.assertEqual(altered_raw_data[1]["value"][0], self.raw_data[1]["value"][0])
self.assertEqual(
altered_raw_data[1]["value"][1]["type"],
self.raw_data[1]["value"][1]["type"],
)
self.assertEqual(
altered_raw_data[1]["value"][2]["type"],
self.raw_data[1]["value"][2]["type"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["type"],
self.raw_data[2]["value"][0]["type"],
)
self.assertEqual(
altered_raw_data[1]["value"][1]["id"],
self.raw_data[1]["value"][1]["id"],
)
self.assertEqual(
altered_raw_data[1]["value"][2]["id"],
self.raw_data[1]["value"][2]["id"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["id"],
self.raw_data[2]["value"][0]["id"],
)
def test_rename(self):
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedstream.struct1",
operation=RenameStructChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertNotIn("char1", altered_raw_data[1]["value"][1]["value"])
self.assertNotIn("char1", altered_raw_data[1]["value"][2]["value"])
self.assertNotIn("char1", altered_raw_data[2]["value"][0]["value"])
self.assertIn("renamed1", altered_raw_data[1]["value"][1]["value"])
self.assertIn("renamed1", altered_raw_data[1]["value"][2]["value"])
self.assertIn("renamed1", altered_raw_data[2]["value"][0]["value"])
self.assertEqual(
altered_raw_data[1]["value"][1]["value"]["renamed1"],
self.raw_data[1]["value"][1]["value"]["char1"],
)
self.assertEqual(
altered_raw_data[1]["value"][2]["value"]["renamed1"],
self.raw_data[1]["value"][2]["value"]["char1"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["value"]["renamed1"],
self.raw_data[2]["value"][0]["value"]["char1"],
)
self.assertIn("char2", altered_raw_data[1]["value"][1]["value"])
self.assertIn("char2", altered_raw_data[1]["value"][2]["value"])
self.assertIn("char2", altered_raw_data[2]["value"][0]["value"])
self.assertEqual(
altered_raw_data[1]["value"][1]["value"]["char2"],
self.raw_data[1]["value"][1]["value"]["char2"],
)
self.assertEqual(
altered_raw_data[1]["value"][2]["value"]["char2"],
self.raw_data[1]["value"][2]["value"]["char2"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["value"]["char2"],
self.raw_data[2]["value"][0]["value"]["char2"],
)
def test_remove(self):
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedstream.struct1",
operation=RemoveStructChildrenOperation(name="char1"),
streamfield=models.SampleModel.content,
)
self.assertEqual(len(altered_raw_data[1]["value"][1]["value"]), 1)
self.assertEqual(len(altered_raw_data[1]["value"][2]["value"]), 1)
self.assertEqual(len(altered_raw_data[2]["value"][0]["value"]), 1)
self.assertIn("char2", altered_raw_data[1]["value"][1]["value"])
self.assertIn("char2", altered_raw_data[1]["value"][2]["value"])
self.assertIn("char2", altered_raw_data[2]["value"][0]["value"])
self.assertEqual(
altered_raw_data[1]["value"][1]["value"]["char2"],
self.raw_data[1]["value"][1]["value"]["char2"],
)
self.assertEqual(
altered_raw_data[1]["value"][2]["value"]["char2"],
self.raw_data[1]["value"][2]["value"]["char2"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["value"]["char2"],
self.raw_data[2]["value"][0]["value"]["char2"],
)
class FieldListStreamChildBlockTest(TestCase):
"""Tests involving changes to children of a StreamBlock nested inside a ListBlock.
We use `nestedlist_stream.item` blocks here.
"""
def setUp(self):
raw_data = factories.SampleModelFactory(
content__0__char1__value="Char Block 1",
content__1="nestedlist_stream",
content__1__nestedlist_stream__0__0__char1__value="Char Block 1",
content__1__nestedlist_stream__0__1__char2__value="Char Block 2",
content__1__nestedlist_stream__0__2__char1__value="Char Block 1",
content__1__nestedlist_stream__1__0__char1__value="Char Block 1",
content__2="nestedlist_stream",
content__2__nestedlist_stream__0__0__char1__value="Char Block 1",
content__3="simplestream",
content__3__simplestream__0__char1__value="Char Block 1",
content__3__simplestream__1__char2__value="Char Block 2",
).content.raw_data
self.raw_data = raw_data
def test_blocks_and_data_not_operated_on_intact(self):
"""Test whether other blocks and data not passed to an operation are intact.
We are checking whether the parts of the data which are not passed to an operation are
intact. Since the recursion process depends just on the block path and block structure,
this check is independent of the operation used. We will use a rename operation for now.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedlist_stream.item",
operation=RenameStreamChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(altered_raw_data[0], self.raw_data[0])
self.assertEqual(altered_raw_data[3], self.raw_data[3])
self.assertEqual(altered_raw_data[1]["type"], self.raw_data[1]["type"])
self.assertEqual(altered_raw_data[2]["type"], self.raw_data[2]["type"])
self.assertEqual(altered_raw_data[1]["id"], self.raw_data[1]["id"])
self.assertEqual(altered_raw_data[2]["id"], self.raw_data[2]["id"])
self.assertEqual(
altered_raw_data[1]["value"][0]["type"],
self.raw_data[1]["value"][0]["type"],
)
self.assertEqual(
altered_raw_data[1]["value"][1]["type"],
self.raw_data[1]["value"][1]["type"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["type"],
self.raw_data[2]["value"][0]["type"],
)
self.assertEqual(
altered_raw_data[1]["value"][0]["id"], self.raw_data[1]["value"][0]["id"]
)
self.assertEqual(
altered_raw_data[1]["value"][1]["id"], self.raw_data[1]["value"][1]["id"]
)
self.assertEqual(
altered_raw_data[2]["value"][0]["id"], self.raw_data[2]["value"][0]["id"]
)
def test_rename(self):
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedlist_stream.item",
operation=RenameStreamChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(
altered_raw_data[1]["value"][0]["value"][0]["type"], "renamed1"
)
self.assertEqual(
altered_raw_data[1]["value"][0]["value"][2]["type"], "renamed1"
)
self.assertEqual(
altered_raw_data[1]["value"][1]["value"][0]["type"], "renamed1"
)
self.assertEqual(
altered_raw_data[2]["value"][0]["value"][0]["type"], "renamed1"
)
self.assertEqual(
altered_raw_data[1]["value"][0]["value"][0]["id"],
self.raw_data[1]["value"][0]["value"][0]["id"],
)
self.assertEqual(
altered_raw_data[1]["value"][0]["value"][2]["id"],
self.raw_data[1]["value"][0]["value"][2]["id"],
)
self.assertEqual(
altered_raw_data[1]["value"][1]["value"][0]["id"],
self.raw_data[1]["value"][1]["value"][0]["id"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["value"][0]["id"],
self.raw_data[2]["value"][0]["value"][0]["id"],
)
self.assertEqual(
altered_raw_data[1]["value"][0]["value"][0]["value"],
self.raw_data[1]["value"][0]["value"][0]["value"],
)
self.assertEqual(
altered_raw_data[1]["value"][0]["value"][2]["value"],
self.raw_data[1]["value"][0]["value"][2]["value"],
)
self.assertEqual(
altered_raw_data[1]["value"][1]["value"][0]["value"],
self.raw_data[1]["value"][1]["value"][0]["value"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["value"][0]["value"],
self.raw_data[2]["value"][0]["value"][0]["value"],
)
self.assertEqual(
altered_raw_data[1]["value"][0]["value"][1],
self.raw_data[1]["value"][0]["value"][1],
)
def test_remove(self):
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedlist_stream.item",
operation=RemoveStreamChildrenOperation(name="char1"),
streamfield=models.SampleModel.content,
)
self.assertEqual(len(altered_raw_data[1]["value"][0]["value"]), 1)
self.assertEqual(len(altered_raw_data[1]["value"][1]["value"]), 0)
self.assertEqual(len(altered_raw_data[2]["value"][0]["value"]), 0)
self.assertEqual(
altered_raw_data[1]["value"][0]["value"][0],
self.raw_data[1]["value"][0]["value"][1],
)
class FieldListStructChildBlockTest(TestCase):
"""Tests involving changes to children of a StructBlock nested inside a ListBlock.
We use `nestedlist_struct.item` blocks here.
"""
def setUp(self):
raw_data = factories.SampleModelFactory(
content__0__char1__value="Char Block 1",
content__1__nestedlist_struct__0__char1="Nested List Struct 1",
content__1__nestedlist_struct__1__char1="Nested List Struct 2",
content__2__nestedlist_struct__0__char1="Nested List Struct 3",
content__3="simplestruct",
).content.raw_data
self.raw_data = raw_data
def test_blocks_and_data_not_operated_on_intact(self):
"""Test whether other blocks and data not passed to an operation are intact.
We are checking whether the parts of the data which are not passed to an operation are
intact. Since the recursion process depends just on the block path and block structure,
this check is independent of the operation used. We will use a rename operation for now.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedlist_struct.item",
operation=RenameStructChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(altered_raw_data[0], self.raw_data[0])
self.assertEqual(altered_raw_data[3], self.raw_data[3])
self.assertEqual(altered_raw_data[1]["type"], self.raw_data[1]["type"])
self.assertEqual(altered_raw_data[2]["type"], self.raw_data[2]["type"])
self.assertEqual(altered_raw_data[1]["id"], self.raw_data[1]["id"])
self.assertEqual(altered_raw_data[2]["id"], self.raw_data[2]["id"])
self.assertEqual(
altered_raw_data[1]["value"][0]["id"], self.raw_data[1]["value"][0]["id"]
)
self.assertEqual(
altered_raw_data[1]["value"][1]["id"], self.raw_data[1]["value"][1]["id"]
)
self.assertEqual(
altered_raw_data[2]["value"][0]["id"], self.raw_data[2]["value"][0]["id"]
)
def test_rename(self):
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="nestedlist_struct.item",
operation=RenameStructChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertNotIn("char1", altered_raw_data[1]["value"][0]["value"])
self.assertNotIn("char1", altered_raw_data[1]["value"][1]["value"])
self.assertNotIn("char1", altered_raw_data[2]["value"][0]["value"])
self.assertIn("renamed1", altered_raw_data[1]["value"][0]["value"])
self.assertIn("renamed1", altered_raw_data[1]["value"][1]["value"])
self.assertIn("renamed1", altered_raw_data[2]["value"][0]["value"])
self.assertEqual(
altered_raw_data[1]["value"][0]["value"]["renamed1"],
self.raw_data[1]["value"][0]["value"]["char1"],
)
self.assertEqual(
altered_raw_data[1]["value"][1]["value"]["renamed1"],
self.raw_data[1]["value"][1]["value"]["char1"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["value"]["renamed1"],
self.raw_data[2]["value"][0]["value"]["char1"],
)
self.assertIn("char2", altered_raw_data[1]["value"][0]["value"])
self.assertIn("char2", altered_raw_data[1]["value"][1]["value"])
self.assertIn("char2", altered_raw_data[2]["value"][0]["value"])
self.assertEqual(
altered_raw_data[1]["value"][0]["value"]["char2"],
self.raw_data[1]["value"][0]["value"]["char2"],
)
self.assertEqual(
altered_raw_data[1]["value"][1]["value"]["char2"],
self.raw_data[1]["value"][1]["value"]["char2"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["value"]["char2"],
self.raw_data[2]["value"][0]["value"]["char2"],
)

View File

@@ -0,0 +1,264 @@
from django.test import TestCase
from wagtail.blocks.migrations.operations import (
ListChildrenToStructBlockOperation,
RenameStreamChildrenOperation,
RenameStructChildrenOperation,
)
from wagtail.blocks.migrations.utils import apply_changes_to_raw_data
from wagtail.test.streamfield_migrations import models
class OldListFormatNestedStreamTestCase(TestCase):
"""Tests involving changes to ListBlocks in the old format with StreamBlock children"""
@classmethod
def setUpTestData(cls):
raw_data = [
{"type": "char1", "id": "0001", "value": "Char Block 1"},
{
"type": "nestedlist_stream",
"id": "0002",
"value": [
[
{"type": "char1", "id": "0003", "value": "Char Block 1"},
{"type": "char2", "id": "0004", "value": "Char Block 2"},
{"type": "char1", "id": "0005", "value": "Char Block 1"},
],
[
{"type": "char1", "id": "0006", "value": "Char Block 1"},
],
],
},
{
"type": "nestedlist_stream",
"id": "0007",
"value": [
[
{"type": "char1", "id": "0008", "value": "Char Block 1"},
]
],
},
]
cls.raw_data = raw_data
def test_list_converted_to_new_format_in_recursion(self):
"""Test whether all ListBlock children have converted formats during the recursion.
This tests the changes done in the recursion process only, so the operation used isn't
important. We will use a rename operation for now.
Check whether each ListBlock child has attributes id, value, type and type is item.
Check whether rename operation was done successfully.
"""
altered_raw_data = apply_changes_to_raw_data(
self.raw_data,
"nestedlist_stream.item",
RenameStreamChildrenOperation(old_name="char1", new_name="renamed1"),
streamfield=models.SampleModel.content,
)
for listitem in altered_raw_data[1]["value"]:
self.assertIsInstance(listitem, dict)
self.assertIn("type", listitem)
self.assertIn("value", listitem)
self.assertEqual(listitem["type"], "item")
for listitem in altered_raw_data[2]["value"]:
self.assertIsInstance(listitem, dict)
self.assertIn("type", listitem)
self.assertIn("value", listitem)
self.assertEqual(listitem["type"], "item")
# the nested blocks which were renamed
altered_block_path_indices = [
(1, 0, 0),
(1, 0, 2),
(1, 1, 0),
(2, 0, 0),
]
for ind0, ind1, ind2 in altered_block_path_indices:
self.assertEqual(
altered_raw_data[ind0]["value"][ind1]["value"][ind2]["type"], "renamed1"
)
self.assertEqual(
altered_raw_data[ind0]["value"][ind1]["value"][ind2]["id"],
self.raw_data[ind0]["value"][ind1][ind2]["id"],
)
self.assertEqual(
altered_raw_data[ind0]["value"][ind1]["value"][ind2]["value"],
self.raw_data[ind0]["value"][ind1][ind2]["value"],
)
self.assertEqual(
altered_raw_data[1]["value"][0]["value"][1],
self.raw_data[1]["value"][0][1],
)
def test_list_converted_to_new_format_in_operation(self):
"""Test whether all ListBlock children have converted formats in an operation using the generator
We will test this with the ListChildrenToStructBlockOperation.
Check whether each ListBlock child has attributes id, value, type and type is item.
Check whether the ListBlock child value is a struct with the previous block as value.
Check whether the previous values are intact.
"""
altered_raw_data = apply_changes_to_raw_data(
self.raw_data,
"nestedlist_stream",
ListChildrenToStructBlockOperation(block_name="stream1"),
streamfield=models.SampleModel.content,
)
for ind, listitem in enumerate(altered_raw_data[1]["value"]):
self.assertIsInstance(listitem, dict)
self.assertIn("type", listitem)
self.assertIn("value", listitem)
self.assertEqual(listitem["type"], "item")
self.assertIsInstance(listitem["value"], dict)
self.assertEqual(len(listitem["value"]), 1)
self.assertIn("stream1", listitem["value"])
self.assertEqual(
listitem["value"]["stream1"], self.raw_data[1]["value"][ind]
)
for ind, listitem in enumerate(altered_raw_data[2]["value"]):
self.assertIsInstance(listitem, dict)
self.assertIn("type", listitem)
self.assertIn("value", listitem)
self.assertEqual(listitem["type"], "item")
self.assertIsInstance(listitem["value"], dict)
self.assertEqual(len(listitem["value"]), 1)
self.assertIn("stream1", listitem["value"])
self.assertEqual(
listitem["value"]["stream1"], self.raw_data[2]["value"][ind]
)
class OldListFormatNestedStructTestCase(TestCase):
"""Tests involving changes to ListBlocks in the old format with StructBlock children"""
@classmethod
def setUpTestData(cls):
raw_data = [
{"type": "char1", "id": "0001", "value": "Char Block 1"},
{
"type": "nestedlist_struct",
"id": "0002",
"value": [
{"char1": "Char Block 1", "char2": "Char Block 2"},
{"char1": "Char Block 1", "char2": "Char Block 2"},
],
},
{
"type": "nestedlist_struct",
"id": "0007",
"value": [
{"char1": "Char Block 1", "char2": "Char Block 2"},
],
},
]
cls.raw_data = raw_data
def test_list_converted_to_new_format_in_recursion(self):
"""Test whether all ListBlock children have converted formats during the recursion.
This tests the changes done in the recursion process only, so the operation used isn't
important. We will use a rename operation for now.
Check whether each ListBlock child has attributes id, value, type and type is item.
Check whether rename operation was done successfully.
"""
altered_raw_data = apply_changes_to_raw_data(
self.raw_data,
"nestedlist_struct.item",
RenameStructChildrenOperation(old_name="char1", new_name="renamed1"),
streamfield=models.SampleModel.content,
)
for listitem in altered_raw_data[1]["value"]:
self.assertIsInstance(listitem, dict)
self.assertIn("type", listitem)
self.assertIn("value", listitem)
self.assertEqual(listitem["type"], "item")
for listitem in altered_raw_data[2]["value"]:
self.assertIsInstance(listitem, dict)
self.assertIn("type", listitem)
self.assertIn("value", listitem)
self.assertEqual(listitem["type"], "item")
# The nested blocks which were renamed
altered_block_indices = [(1, 0), (1, 1), (2, 0)]
for ind0, ind1 in altered_block_indices:
self.assertNotIn("char1", altered_raw_data[ind0]["value"][ind1]["value"])
self.assertIn("renamed1", altered_raw_data[ind0]["value"][ind1]["value"])
self.assertEqual(
altered_raw_data[ind0]["value"][ind1]["value"]["renamed1"],
self.raw_data[ind0]["value"][ind1]["char1"],
)
self.assertIn("char2", altered_raw_data[ind0]["value"][ind1]["value"])
self.assertEqual(
altered_raw_data[ind0]["value"][ind1]["value"]["char2"],
self.raw_data[ind0]["value"][ind1]["char2"],
)
def test_list_converted_to_new_format_in_operation(self):
"""Test whether all ListBlock children have converted formats in an operation using the generator
We will test this with the ListChildrenToStructBlockOperation.
Check whether each ListBlock child has attributes id, value, type and type is item.
Check whether the ListBlock child value is a struct with the previous block as value.
Check whether the previous values are intact.
"""
altered_raw_data = apply_changes_to_raw_data(
self.raw_data,
"nestedlist_struct",
ListChildrenToStructBlockOperation(block_name="struct1"),
streamfield=models.SampleModel.content,
)
for ind, listitem in enumerate(altered_raw_data[1]["value"]):
self.assertIsInstance(listitem, dict)
self.assertIn("type", listitem)
self.assertIn("value", listitem)
self.assertEqual(listitem["type"], "item")
self.assertIsInstance(listitem["value"], dict)
self.assertEqual(len(listitem["value"]), 1)
self.assertIn("struct1", listitem["value"])
self.assertEqual(
listitem["value"]["struct1"], self.raw_data[1]["value"][ind]
)
for ind, listitem in enumerate(altered_raw_data[2]["value"]):
self.assertIsInstance(listitem, dict)
self.assertIn("type", listitem)
self.assertIn("value", listitem)
self.assertEqual(listitem["type"], "item")
self.assertIsInstance(listitem["value"], dict)
self.assertEqual(len(listitem["value"]), 1)
self.assertIn("struct1", listitem["value"])
self.assertEqual(
listitem["value"]["struct1"], self.raw_data[2]["value"][ind]
)

View File

@@ -0,0 +1,535 @@
from django.test import TestCase
from wagtail.blocks.migrations.operations import (
AlterBlockValueOperation,
ListChildrenToStructBlockOperation,
RemoveStreamChildrenOperation,
RemoveStructChildrenOperation,
RenameStreamChildrenOperation,
RenameStructChildrenOperation,
StreamChildrenToListBlockOperation,
StreamChildrenToStreamBlockOperation,
StreamChildrenToStructBlockOperation,
)
from wagtail.blocks.migrations.utils import apply_changes_to_raw_data
from wagtail.test.streamfield_migrations import factories, models
class FieldChildBlockTest(TestCase):
"""Tests involving changes to top level blocks"""
def setUp(self):
raw_data = factories.SampleModelFactory(
content__0__char1__value="Char Block 1",
content__1__char2__value="Char Block 2",
content__2__char1__value="Char Block 1",
content__3__char2__value="Char Block 2",
).content.raw_data
self.raw_data = raw_data
def test_rename(self):
"""Rename `char1` blocks to `renamed1`
Check whether all `char1` blocks have been renamed correctly.
Check whether ids and values for renamed blocks are intact.
Check whether other block types are intact.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="",
operation=RenameStreamChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(altered_raw_data[0]["type"], "renamed1")
self.assertEqual(altered_raw_data[2]["type"], "renamed1")
self.assertEqual(altered_raw_data[0]["id"], self.raw_data[0]["id"])
self.assertEqual(altered_raw_data[2]["id"], self.raw_data[2]["id"])
self.assertEqual(altered_raw_data[0]["value"], self.raw_data[0]["value"])
self.assertEqual(altered_raw_data[2]["value"], self.raw_data[2]["value"])
self.assertEqual(altered_raw_data[1], self.raw_data[1])
self.assertEqual(altered_raw_data[3], self.raw_data[3])
def test_remove(self):
"""Remove all `char1` blocks
Check whether all `char1` blocks have been removed and whether other blocks are intact.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="",
operation=RemoveStreamChildrenOperation(name="char1"),
streamfield=models.SampleModel.content,
)
self.assertEqual(len(altered_raw_data), 2)
self.assertEqual(altered_raw_data[0], self.raw_data[1])
self.assertEqual(altered_raw_data[1], self.raw_data[3])
def test_combine_to_listblock(self):
"""Combine all `char1` blocks into a new ListBlock named `list1`
Check whether no `char1` blocks are present among the stream children and whether other
blocks are intact.
Check whether a new `list1` block has been added to the stream children and whether it has
child blocks corresponding to the previous `char1` blocks.
Check whether the ids and values from the `char1` blocks are intact in the list children.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="",
operation=StreamChildrenToListBlockOperation(
block_name="char1", list_block_name="list1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(len(altered_raw_data), 3)
self.assertEqual(altered_raw_data[0], self.raw_data[1])
self.assertEqual(altered_raw_data[1], self.raw_data[3])
self.assertEqual(altered_raw_data[2]["type"], "list1")
self.assertEqual(len(altered_raw_data[2]["value"]), 2)
self.assertEqual(altered_raw_data[2]["value"][0]["type"], "item")
self.assertEqual(altered_raw_data[2]["value"][1]["type"], "item")
self.assertEqual(altered_raw_data[2]["value"][0]["id"], self.raw_data[0]["id"])
self.assertEqual(altered_raw_data[2]["value"][1]["id"], self.raw_data[2]["id"])
self.assertEqual(
altered_raw_data[2]["value"][0]["value"], self.raw_data[0]["value"]
)
self.assertEqual(
altered_raw_data[2]["value"][1]["value"], self.raw_data[2]["value"]
)
def test_combine_to_listblock_no_existing_children(self):
"""Combine all `simplestruct` blocks into a new ListBlock named `list1`
We have no `simplestruct` blocks in our existing data, so there should be no list1 blocks
created and the data should be intact.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="",
operation=StreamChildrenToListBlockOperation(
block_name="simplestruct", list_block_name="list1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(len(altered_raw_data), 4)
self.assertEqual(altered_raw_data[0], self.raw_data[0])
self.assertEqual(altered_raw_data[1], self.raw_data[1])
self.assertEqual(altered_raw_data[2], self.raw_data[2])
self.assertEqual(altered_raw_data[3], self.raw_data[3])
def test_combine_single_type_to_streamblock(self):
"""Combine all `char1` blocks as children of a new StreamBlock named `stream1`
Check whether no `char1` blocks are present among the (top) stream children and whether
other blocks are intact.
Check whether a new `stream1` block has been added to the (top) stream children.
Check whether the new `stream1` block has the `char1` blocks as children and whether they
are intact.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="",
operation=StreamChildrenToStreamBlockOperation(
block_names=["char1"], stream_block_name="stream1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(len(altered_raw_data), 3)
self.assertEqual(altered_raw_data[0], self.raw_data[1])
self.assertEqual(altered_raw_data[1], self.raw_data[3])
self.assertEqual(altered_raw_data[2]["type"], "stream1")
self.assertEqual(len(altered_raw_data[2]["value"]), 2)
self.assertEqual(altered_raw_data[2]["value"][0], self.raw_data[0])
self.assertEqual(altered_raw_data[2]["value"][1], self.raw_data[2])
def test_combine_multiple_types_to_streamblock(self):
"""Combine all `char1` and `char2` blocks as children of a new StreamBlock named `stream1`
Check whether no `char1` or `char2` blocks are present among the (top) stream children.
Check whether a new `stream1` block has been added to the (top) stream children.
Check whether the new `stream1` block has the `char1` and `char2` blocks as children and
that they are intact.
Note:
We only have `char1` and `char2` blocks in our existing data.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="",
operation=StreamChildrenToStreamBlockOperation(
block_names=["char1", "char2"], stream_block_name="stream1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(len(altered_raw_data), 1)
self.assertEqual(altered_raw_data[0]["type"], "stream1")
self.assertEqual(len(altered_raw_data[0]["value"]), 4)
self.assertEqual(altered_raw_data[0]["value"][0], self.raw_data[0])
self.assertEqual(altered_raw_data[0]["value"][1], self.raw_data[1])
self.assertEqual(altered_raw_data[0]["value"][2], self.raw_data[2])
self.assertEqual(altered_raw_data[0]["value"][3], self.raw_data[3])
def test_combine_to_streamblock_no_existing_children(self):
"""Combine all `simplestruct` blocks as children of a new StreamBlock named `stream1`
We have no `simplestruct` blocks in our existing data, so there should be no stream1 blocks
created and the data should be intact.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="",
operation=StreamChildrenToStreamBlockOperation(
block_names=["simplestruct"], stream_block_name="stream1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(len(altered_raw_data), 4)
self.assertEqual(altered_raw_data[0], self.raw_data[0])
self.assertEqual(altered_raw_data[1], self.raw_data[1])
self.assertEqual(altered_raw_data[2], self.raw_data[2])
self.assertEqual(altered_raw_data[3], self.raw_data[3])
def test_to_structblock(self):
"""Move each `char1` block inside a new StructBlock named `struct1`
Check whether each `char1` block has been replaced with a `struct1` block in the stream
children.
Check whether other blocks are intact.
Check whether each `struct1` block has a `char1` child and whether it has the value of the
previous `char1` block.
Note:
Block ids are not preserved here.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="",
operation=StreamChildrenToStructBlockOperation("char1", "struct1"),
streamfield=models.SampleModel.content,
)
self.assertEqual(altered_raw_data[0]["type"], "struct1")
self.assertEqual(altered_raw_data[2]["type"], "struct1")
self.assertEqual(altered_raw_data[1], self.raw_data[1])
self.assertEqual(altered_raw_data[3], self.raw_data[3])
self.assertIn("char1", altered_raw_data[0]["value"])
self.assertIn("char1", altered_raw_data[2]["value"])
self.assertEqual(
altered_raw_data[0]["value"]["char1"], self.raw_data[0]["value"]
)
self.assertEqual(
altered_raw_data[2]["value"]["char1"], self.raw_data[2]["value"]
)
def test_alter_value(self):
"""Change the value of each `char1` block to `foo`
Check whether the value of each `char1` block has changed to `foo`.
Check whether the values of other blocks are intact.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="char1",
operation=AlterBlockValueOperation(new_value="foo"),
streamfield=models.SampleModel.content,
)
self.assertEqual(altered_raw_data[0]["value"], "foo")
self.assertEqual(altered_raw_data[1]["value"], self.raw_data[1]["value"])
self.assertEqual(altered_raw_data[2]["value"], "foo")
self.assertEqual(altered_raw_data[3]["value"], self.raw_data[3]["value"])
class FieldStructChildBlockTest(TestCase):
"""Tests involving changes to direct children of a StructBlock
We use `simplestruct` blocks as the StructBlocks here.
"""
def setUp(self):
raw_data = factories.SampleModelFactory(
content__0__char1__value="Char Block 1",
content__1="simplestruct",
content__2="simplestruct",
content__3__char2__value="Char Block 2",
).content.raw_data
self.raw_data = raw_data
def test_blocks_and_data_not_operated_on_intact(self):
"""Test whether other blocks and data not passed to an operation are intact.
We are checking whether the parts of the data which are not passed to an operation are
intact. Since the recursion process depends just on the block path and block structure,
this check is independent of the operation used. We will use a rename operation for now.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="simplestruct",
operation=RenameStructChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(altered_raw_data[0], self.raw_data[0])
self.assertEqual(altered_raw_data[3], self.raw_data[3])
self.assertEqual(altered_raw_data[1]["id"], self.raw_data[1]["id"])
self.assertEqual(altered_raw_data[2]["id"], self.raw_data[2]["id"])
self.assertEqual(altered_raw_data[1]["type"], self.raw_data[1]["type"])
self.assertEqual(altered_raw_data[2]["type"], self.raw_data[2]["type"])
def test_rename(self):
"""Rename `simplestruct.char1` blocks to `renamed1`
Check whether all `simplestruct.char1` blocks have been renamed correctly.
Check whether values for renamed blocks are intact.
Check whether other children of `simplestruct` are intact.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="simplestruct",
operation=RenameStructChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(len(altered_raw_data[1]["value"]), 2)
self.assertEqual(len(altered_raw_data[2]["value"]), 2)
self.assertNotIn("char1", altered_raw_data[1]["value"])
self.assertNotIn("char1", altered_raw_data[2]["value"])
self.assertIn("renamed1", altered_raw_data[1]["value"])
self.assertIn("renamed1", altered_raw_data[2]["value"])
self.assertEqual(
altered_raw_data[1]["value"]["renamed1"], self.raw_data[1]["value"]["char1"]
)
self.assertEqual(
altered_raw_data[2]["value"]["renamed1"], self.raw_data[2]["value"]["char1"]
)
self.assertIn("char2", altered_raw_data[1]["value"])
self.assertIn("char2", altered_raw_data[2]["value"])
self.assertEqual(
altered_raw_data[1]["value"]["char2"], self.raw_data[1]["value"]["char2"]
)
self.assertEqual(
altered_raw_data[2]["value"]["char2"], self.raw_data[2]["value"]["char2"]
)
def test_remove(self):
"""Remove `simplestruct.char1` blocks
Check whether all `simplestruct.char1` blocks have been removed.
Check whether other children of `simplestruct` are intact.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="simplestruct",
operation=RemoveStructChildrenOperation(name="char1"),
streamfield=models.SampleModel.content,
)
self.assertEqual(len(altered_raw_data[1]["value"]), 1)
self.assertEqual(len(altered_raw_data[2]["value"]), 1)
self.assertNotIn("char1", altered_raw_data[1]["value"])
self.assertNotIn("char1", altered_raw_data[2]["value"])
self.assertIn("char2", altered_raw_data[1]["value"])
self.assertIn("char2", altered_raw_data[2]["value"])
self.assertEqual(
altered_raw_data[1]["value"]["char2"], self.raw_data[1]["value"]["char2"]
)
self.assertEqual(
altered_raw_data[2]["value"]["char2"], self.raw_data[2]["value"]["char2"]
)
class FieldStreamChildBlockTest(TestCase):
"""Tests involving changes to direct children of a StreamBlock
We use `simplestream` blocks as the StreamBlocks here.
"""
def setUp(self):
raw_data = factories.SampleModelFactory(
content__0__char1__value="Char Block 1",
content__1="simplestream",
content__1__simplestream__0__char1__value="Char Block 1",
content__1__simplestream__1__char2__value="Char Block 2",
content__1__simplestream__2__char1__value="Char Block 1",
content__2="simplestream",
content__2__simplestream__0__char1__value="Char Block 1",
content__3__char2__value="Char Block 2",
).content.raw_data
self.raw_data = raw_data
def test_blocks_and_data_not_operated_on_intact(self):
"""Test whether other blocks and data not passed to an operation are intact.
We are checking whether the parts of the data which are not passed to an operation are
intact. Since the recursion process depends just on the block path and block structure,
this check is independent of the operation used. We will use a rename operation for now.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="simplestream",
operation=RenameStreamChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(altered_raw_data[0], self.raw_data[0])
self.assertEqual(altered_raw_data[3], self.raw_data[3])
self.assertEqual(altered_raw_data[1]["id"], self.raw_data[1]["id"])
self.assertEqual(altered_raw_data[2]["id"], self.raw_data[2]["id"])
self.assertEqual(altered_raw_data[1]["type"], self.raw_data[1]["type"])
self.assertEqual(altered_raw_data[2]["type"], self.raw_data[2]["type"])
def test_rename(self):
"""Rename `simplestream.char1` blocks to `renamed1`
Check whether all `simplestream.char1` blocks have been renamed correctly.
Check whether values and ids for renamed blocks are intact.
Check whether other children of `simplestream` are intact.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="simplestream",
operation=RenameStreamChildrenOperation(
old_name="char1", new_name="renamed1"
),
streamfield=models.SampleModel.content,
)
self.assertEqual(altered_raw_data[1]["value"][0]["type"], "renamed1")
self.assertEqual(altered_raw_data[1]["value"][2]["type"], "renamed1")
self.assertEqual(altered_raw_data[2]["value"][0]["type"], "renamed1")
self.assertEqual(
altered_raw_data[1]["value"][0]["id"], self.raw_data[1]["value"][0]["id"]
)
self.assertEqual(
altered_raw_data[1]["value"][2]["id"], self.raw_data[1]["value"][2]["id"]
)
self.assertEqual(
altered_raw_data[2]["value"][0]["id"], self.raw_data[2]["value"][0]["id"]
)
self.assertEqual(
altered_raw_data[1]["value"][0]["value"],
self.raw_data[1]["value"][0]["value"],
)
self.assertEqual(
altered_raw_data[1]["value"][2]["value"],
self.raw_data[1]["value"][2]["value"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["value"],
self.raw_data[2]["value"][0]["value"],
)
self.assertEqual(altered_raw_data[1]["value"][1], self.raw_data[1]["value"][1])
def test_remove(self):
"""Remove `simplestream.char1` blocks
Check whether all `simplestream.char1` blocks have been removed.
Check whether other children of `simplestream` are intact.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="simplestream",
operation=RemoveStreamChildrenOperation(name="char1"),
streamfield=models.SampleModel.content,
)
self.assertEqual(len(altered_raw_data[1]["value"]), 1)
self.assertEqual(len(altered_raw_data[2]["value"]), 0)
self.assertEqual(altered_raw_data[1]["value"][0], self.raw_data[1]["value"][1])
class FieldListChildBlockTest(TestCase):
"""Tests involving changes to direct children of a ListBlock
We use `simplelist` blocks as the ListBlocks here.
"""
def setUp(self):
raw_data = factories.SampleModelFactory(
content__0__char1__value="Char Block 1",
content__1__simplelist__0="Foo 1",
content__1__simplelist__1="Foo 2",
content__2__simplelist__0="Foo 3",
).content.raw_data
self.raw_data = raw_data
def test_to_structblock(self):
"""Turn each list child into a StructBlock and move value inside as a child named `text`
Check whether each list child has been converted to a StructBlock with a child named `text`
in it.
Check whether the previous value of each list child is now the value that `text` takes.
Note:
Block ids are not preserved here.
"""
altered_raw_data = apply_changes_to_raw_data(
raw_data=self.raw_data,
block_path_str="simplelist",
operation=ListChildrenToStructBlockOperation(block_name="text"),
streamfield=models.SampleModel.content,
)
self.assertEqual(type(altered_raw_data[1]["value"][0]["value"]), dict)
self.assertEqual(type(altered_raw_data[1]["value"][1]["value"]), dict)
self.assertEqual(type(altered_raw_data[2]["value"][0]["value"]), dict)
self.assertIn("text", altered_raw_data[1]["value"][0]["value"])
self.assertIn("text", altered_raw_data[1]["value"][1]["value"])
self.assertIn("text", altered_raw_data[2]["value"][0]["value"])
self.assertEqual(
altered_raw_data[1]["value"][0]["value"]["text"],
self.raw_data[1]["value"][0]["value"],
)
self.assertEqual(
altered_raw_data[1]["value"][1]["value"]["text"],
self.raw_data[1]["value"][1]["value"],
)
self.assertEqual(
altered_raw_data[2]["value"][0]["value"]["text"],
self.raw_data[2]["value"][0]["value"],
)