From 1baff6ad901a724d21f917da4b91848da4c91b16 Mon Sep 17 00:00:00 2001 From: Blaine Jester Date: Mon, 16 Mar 2026 15:52:01 -0700 Subject: [PATCH] Prevent syncing from incomplete source node --- .../tests/test_contentnodes.py | 28 +++++++++++++++++++ contentcuration/contentcuration/utils/sync.py | 9 ++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/contentcuration/contentcuration/tests/test_contentnodes.py b/contentcuration/contentcuration/tests/test_contentnodes.py index 0de23ab008..13b62fff17 100644 --- a/contentcuration/contentcuration/tests/test_contentnodes.py +++ b/contentcuration/contentcuration/tests/test_contentnodes.py @@ -824,6 +824,27 @@ def test_sync_after_no_changes(self): ) self._assert_same_files(orig_video, cloned_video) + def test_sync_but_incomplete(self): + orig_video, cloned_video = self._setup_original_and_deriative_nodes() + orig_video.license_id = None + orig_video.mark_complete() + self.assertFalse(orig_video.complete) + orig_video.save() + + self.assertTrue(cloned_video.complete) + + sync_node( + cloned_video, + sync_titles_and_descriptions=True, + sync_resource_details=True, + sync_files=True, + sync_assessment_items=True, + ) + + self.assertIsNotNone(cloned_video.license_id) + cloned_video.mark_complete() + self.assertTrue(cloned_video.complete) + def test_sync_with_subs(self): orig_video, cloned_video = self._setup_original_and_deriative_nodes() self._add_subs_to_video_node(orig_video, "fr") @@ -868,6 +889,13 @@ def _create_video_node(self, title, parent, withsubs=False): node_id="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ) video_node = testdata.node(data, parent=parent) + video_node.license_id = 9 # Special Permissions + video_node.license_description = "Special permissions for testing" + video_node.copyright_holder = "LE" + # ensure the node is complete according to our logic + video_node.mark_complete() + self.assertTrue(video_node.complete) + video_node.save() if withsubs: self._add_subs_to_video_node(video_node, "fr") diff --git a/contentcuration/contentcuration/utils/sync.py b/contentcuration/contentcuration/utils/sync.py index 2987d1c75b..6f175d9a4f 100644 --- a/contentcuration/contentcuration/utils/sync.py +++ b/contentcuration/contentcuration/utils/sync.py @@ -53,11 +53,14 @@ def sync_node( sync_assessment_items=False, ): original_node = node.get_original_node() + if not original_node.complete: + logging.warning( + f"Refusing to sync node {node.pk} from incomplete source node: {original_node.pk}" + ) + return node if original_node.node_id != node.node_id: # Only update if node is not original logging.info( - "----- Syncing: {} from {}".format( - node.title, original_node.get_channel().name - ) + f"----- Syncing: {node.title} from {original_node.get_channel().name}" ) if sync_titles_and_descriptions: fields = [