def test_update(self):
        source = TranslationSource.objects.create(
            object_id=self.snippet.translation_key,
            specific_content_type=ContentType.objects.get_for_model(
                TestSnippet),
            locale=self.snippet.locale,
            content_json=json.dumps({
                "pk":
                self.snippet.pk,
                "field":
                "Some different content",  # Changed
                "translation_key":
                str(self.snippet.translation_key),
                "locale":
                self.snippet.locale_id,
            }),
            last_updated_at=timezone.now(),
        )

        new_source, created = TranslationSource.update_or_create_from_instance(
            self.snippet)

        self.assertFalse(created)

        self.assertEqual(source, new_source)
        self.assertEqual(
            json.loads(source.content_json)["field"], "Some different content")
        self.assertEqual(
            json.loads(new_source.content_json)["field"],
            "This is some test content")
    def test_update_streamfields(self):
        # Streamfields are special in that they contain content that needs to be synchronised as well as
        # translatable content.

        # Copy page for translation, this will have a blank streamfield
        translated = self.page.copy_for_translation(self.dest_locale)

        # Set streamfield value on original
        self.page.test_streamfield = StreamValue(
            TestPage.test_streamfield.field.stream_block,
            [
                {
                    "id": "id",
                    "type": "test_charblock",
                    "value": "This is some test content",
                }
            ],
            is_lazy=True,
        )

        # Save the page
        revision = self.page.save_revision()
        revision.publish()
        self.page.refresh_from_db()
        (
            source_with_streamfield,
            created,
        ) = TranslationSource.update_or_create_from_instance(self.page)

        # Create a translation for the new context
        StringTranslation.objects.create(
            translation_of=self.string,
            locale=self.dest_locale,
            context=TranslationContext.objects.get(
                object_id=self.page.translation_key, path="test_streamfield.id"
            ),
            data="Ceci est du contenu de test",
        )

        new_page, created = source_with_streamfield.create_or_update_translation(
            self.dest_locale
        )

        self.assertFalse(created)
        self.assertEqual(new_page, translated)

        # Check the block was copied into translation
        self.assertEqual(new_page.test_streamfield[0].id, "id")
        self.assertEqual(
            new_page.test_streamfield[0].value, "Ceci est du contenu de test"
        )
    def test_create(self):
        source, created = TranslationSource.update_or_create_from_instance(
            self.snippet)

        self.assertTrue(created)
        self.assertEqual(source.object_id, self.snippet.translation_key)
        self.assertEqual(source.locale, self.snippet.locale)
        self.assertEqual(
            json.loads(source.content_json),
            {
                "pk": self.snippet.pk,
                "field": "This is some test content",
                "translation_key": str(self.snippet.translation_key),
                "locale": self.snippet.locale_id,
            },
        )
        self.assertTrue(source.created_at)
    def test_update_synchronised_fields(self):
        # Add a couple of initial child objects.
        # The first one will be deleted in the source page after initial translation. The sync should carry this deletion across.
        # The second won't be deleted so should remain in the translation
        self.page.test_synchronized_childobjects.add(
            TestSynchronizedChildObject(
                field=
                "Test child object that existed before initial translation and was deleted"
            ))

        self.page.test_synchronized_childobjects.add(
            TestSynchronizedChildObject(
                field=
                "Test child object that existed before initial translation and was not deleted"
            ))

        translated = self.page.copy_for_translation(self.dest_locale)

        self.page.test_synchronized_charfield = "Test synchronised content"
        self.page.test_synchronized_textfield = "Test synchronised content"
        self.page.test_synchronized_emailfield = "*****@*****.**"
        self.page.test_synchronized_slugfield = "test-synchronised-content"
        self.page.test_synchronized_urlfield = "https://test.synchronised/content"
        self.page.test_synchronized_richtextfield = "<p>Test synchronised content</p>"
        # self.page.test_synchronized_streamfield = ""
        synchronized_snippet = TestSnippet.objects.create(
            field="Synchronised snippet")
        self.page.test_synchronized_snippet = synchronized_snippet
        self.page.test_synchronized_customfield = "Test synchronised content"

        # Add some child objects
        self.page.test_childobjects.add(
            TestChildObject(field="A test child object"))

        # Remove one of the child objects to test that deletions are syncrhonised
        self.page.test_synchronized_childobjects.remove(
            self.page.test_synchronized_childobjects.get(
                field=
                "Test child object that existed before initial translation and was deleted"
            ))
        self.page.test_synchronized_childobjects.add(
            TestSynchronizedChildObject(field="A test synchronized object"))

        # Non-parental child objects are created differently because modelcluster doesn't help us
        TestNonParentalChildObject.objects.create(
            page=self.page, field="A non-parental child object")

        # Save the page
        revision = self.page.save_revision()
        revision.publish()
        self.page.refresh_from_db()
        source_with_changed_content, created = TranslationSource.update_or_create_from_instance(
            self.page)

        # Check translation hasn't been updated yet
        translated.refresh_from_db()
        self.assertEqual(translated.test_synchronized_charfield, "")

        # Update the original page again. This will make sure it's taking the content from the translation source and not the live version
        self.page.test_synchronized_charfield = (
            "Test synchronised content updated again")
        self.page.save_revision().publish()

        (
            new_page,
            created,
        ) = source_with_changed_content.create_or_update_translation(
            self.dest_locale,
            fallback=True,
        )

        self.assertFalse(created)
        self.assertEqual(new_page, translated)
        self.assertEqual(new_page.test_synchronized_charfield,
                         "Test synchronised content")
        self.assertEqual(new_page.test_synchronized_charfield,
                         "Test synchronised content")
        self.assertEqual(new_page.test_synchronized_textfield,
                         "Test synchronised content")
        self.assertEqual(new_page.test_synchronized_emailfield,
                         "*****@*****.**")
        self.assertEqual(new_page.test_synchronized_slugfield,
                         "test-synchronised-content")
        self.assertEqual(new_page.test_synchronized_urlfield,
                         "https://test.synchronised/content")
        self.assertEqual(new_page.test_synchronized_richtextfield,
                         "<p>Test synchronised content</p>")
        self.assertEqual(new_page.test_synchronized_snippet,
                         synchronized_snippet)
        self.assertEqual(new_page.test_synchronized_customfield,
                         "Test synchronised content")

        # Translatable child objects should always be synchronised
        # Locale should be updated but translation key should be the same
        translatable_child_object = new_page.test_childobjects.get()
        self.assertEqual(translatable_child_object.field,
                         "A test child object")
        self.assertEqual(translatable_child_object.locale, self.dest_locale)
        self.assertTrue(
            translatable_child_object.has_translation(self.source_locale))

        # Test both the non deleted and new synchronised child objects remain
        self.assertEqual(
            set(
                new_page.test_synchronized_childobjects.values_list(
                    'field', flat=True)),
            {
                "A test synchronized object",
                "Test child object that existed before initial translation and was not deleted"
            })

        # Non parental child objects should be ignored
        self.assertFalse(new_page.test_nonparentalchildobjects.exists())