@@ -1332,15 +1332,16 @@ def test_collect_concept_hierarchy_map_ignores_non_concepts(self):
13321332
13331333 # ── run() hierarchy reconciliation ───────────────────────────────────────
13341334
1335+ @patch ('core.importers.models.post_import_update_resource_counts.apply_async' , Mock ())
1336+ @patch ('core.importers.models.BulkImportParallelRunner.wait_till_tasks_alive' , Mock ())
1337+ @patch ('core.importers.models.BulkImportParallelRunner.queue_tasks' , Mock ())
13351338 @patch ('core.importers.models.make_hierarchy' )
1336- @patch ('core.importers.models.BulkImportParallelRunner.wait_till_tasks_alive' )
1337- @patch ('core.importers.models.BulkImportParallelRunner.queue_tasks' )
1338- def test_run_calls_make_hierarchy_with_inverted_map (self , queue_tasks_mock , wait_mock , make_hierarchy_mock ):
1339+ def test_run_calls_make_hierarchy_with_inverted_map (self , make_hierarchy_mock ):
13391340 """After all chunks complete, make_hierarchy receives the inverted {parent_uri: [child_uris]} map."""
1340- org = OrganizationFactory (mnemonic = 'TestOrg' )
1341+ org = OrganizationFactory (mnemonic = 'TestOrg' )
13411342 source = OrganizationSourceFactory (organization = org , mnemonic = 'TestSource' , version = 'HEAD' )
13421343 parent = ConceptFactory (parent = source , mnemonic = 'ParentConcept' )
1343- child = ConceptFactory (parent = source , mnemonic = 'ChildConcept' )
1344+ child = ConceptFactory (parent = source , mnemonic = 'ChildConcept' )
13441345
13451346 # File order: child before parent (the exact scenario the fix targets)
13461347 content = '\n ' .join ([
@@ -1363,10 +1364,11 @@ def test_run_calls_make_hierarchy_with_inverted_map(self, queue_tasks_mock, wait
13631364 self .assertIn (parent .uri , inverted )
13641365 self .assertIn (child .uri , inverted [parent .uri ])
13651366
1367+ @patch ('core.importers.models.post_import_update_resource_counts.apply_async' , Mock ())
1368+ @patch ('core.importers.models.BulkImportParallelRunner.wait_till_tasks_alive' , Mock ())
1369+ @patch ('core.importers.models.BulkImportParallelRunner.queue_tasks' , Mock ())
13661370 @patch ('core.importers.models.make_hierarchy' )
1367- @patch ('core.importers.models.BulkImportParallelRunner.wait_till_tasks_alive' )
1368- @patch ('core.importers.models.BulkImportParallelRunner.queue_tasks' )
1369- def test_run_skips_make_hierarchy_when_no_hierarchy (self , queue_tasks_mock , wait_mock , make_hierarchy_mock ):
1371+ def test_run_skips_make_hierarchy_when_no_hierarchy (self , make_hierarchy_mock ):
13701372 """make_hierarchy is not called when no concept in the import has parent_concept_urls."""
13711373 content = '\n ' .join ([
13721374 json .dumps ({
@@ -1379,9 +1381,10 @@ def test_run_skips_make_hierarchy_when_no_hierarchy(self, queue_tasks_mock, wait
13791381
13801382 make_hierarchy_mock .assert_not_called ()
13811383
1382- @patch ('core.importers.models.BulkImportParallelRunner.wait_till_tasks_alive' )
1383- @patch ('core.importers.models.BulkImportParallelRunner.queue_tasks' )
1384- def test_run_hierarchy_child_before_parent (self , queue_tasks_mock , wait_mock ):
1384+ @patch ('core.importers.models.post_import_update_resource_counts.apply_async' , Mock ())
1385+ @patch ('core.importers.models.BulkImportParallelRunner.wait_till_tasks_alive' , Mock ())
1386+ @patch ('core.importers.models.BulkImportParallelRunner.queue_tasks' , Mock ())
1387+ def test_run_hierarchy_child_before_parent (self ):
13851388 """
13861389 End-to-end: when child appears before parent in the import file, the reconciliation
13871390 step must correctly establish the parent_concepts M2M link in the database.
@@ -1411,17 +1414,18 @@ def test_run_hierarchy_child_before_parent(self, queue_tasks_mock, wait_mock):
14111414 child .refresh_from_db ()
14121415 self .assertIn (parent .get_latest_version (), child .parent_concepts .all ())
14131416
1414- @patch ('core.importers.models.BulkImportParallelRunner.wait_till_tasks_alive' )
1415- @patch ('core.importers.models.BulkImportParallelRunner.queue_tasks' )
1416- def test_run_hierarchy_parent_before_child (self , queue_tasks_mock , wait_mock ):
1417+ @patch ('core.importers.models.post_import_update_resource_counts.apply_async' , Mock ())
1418+ @patch ('core.importers.models.BulkImportParallelRunner.wait_till_tasks_alive' , Mock ())
1419+ @patch ('core.importers.models.BulkImportParallelRunner.queue_tasks' , Mock ())
1420+ def test_run_hierarchy_parent_before_child (self ):
14171421 """
14181422 End-to-end: when parent appears before child (natural order), the reconciliation
14191423 must also establish the link correctly — confirming the fix is order-agnostic.
14201424 """
1421- org = OrganizationFactory (mnemonic = 'TestOrg' )
1425+ org = OrganizationFactory (mnemonic = 'TestOrg' )
14221426 source = OrganizationSourceFactory (organization = org , mnemonic = 'TestSource' , version = 'HEAD' )
14231427 parent = ConceptFactory (parent = source , mnemonic = 'ParentConcept' )
1424- child = ConceptFactory (parent = source , mnemonic = 'ChildConcept' )
1428+ child = ConceptFactory (parent = source , mnemonic = 'ChildConcept' )
14251429
14261430 # parent listed before child — normal/expected order
14271431 content = '\n ' .join ([
@@ -1442,26 +1446,28 @@ def test_run_hierarchy_parent_before_child(self, queue_tasks_mock, wait_mock):
14421446 child .refresh_from_db ()
14431447 self .assertIn (parent .get_latest_version (), child .parent_concepts .all ())
14441448
1449+ @patch ('core.importers.models.post_import_update_resource_counts.apply_async' , Mock ())
1450+ @patch ('core.importers.models.BulkImportParallelRunner.wait_till_tasks_alive' , Mock ())
1451+ @patch ('core.importers.models.BulkImportParallelRunner.queue_tasks' , Mock ())
14451452 @patch ('core.importers.models.make_hierarchy' )
1446- @patch ('core.importers.models.BulkImportParallelRunner.wait_till_tasks_alive' )
1447- @patch ('core.importers.models.BulkImportParallelRunner.queue_tasks' )
1448- def test_run_excludes_inaccessible_concepts (self , queue_tasks_mock , wait_mock , make_hierarchy_mock ):
1453+ def test_run_excludes_inaccessible_concepts (self , make_hierarchy_mock ):
14491454 """
14501455 Concepts from sources the importing user cannot edit must be excluded from make_hierarchy,
14511456 even when they exist in the database. This prevents hierarchy changes on foreign sources
14521457 that were denied during import (P1 security fix).
14531458 """
14541459 # OrgA — importing user is a member → has edit access
1455- org_a = OrganizationFactory (mnemonic = 'OrgA' )
1460+ org_a = OrganizationFactory (mnemonic = 'OrgA' )
14561461 source_a = OrganizationSourceFactory (organization = org_a , mnemonic = 'SourceA' , version = 'HEAD' )
14571462 parent_a = ConceptFactory (parent = source_a , mnemonic = 'ParentA' )
1458- child_a = ConceptFactory (parent = source_a , mnemonic = 'ChildA' )
1463+ child_a = ConceptFactory (parent = source_a , mnemonic = 'ChildA' )
14591464
14601465 # OrgB — importing user is NOT a member and source is not publicly editable → no edit access
1461- org_b = OrganizationFactory (mnemonic = 'OrgB' )
1462- source_b = OrganizationSourceFactory (organization = org_b , mnemonic = 'SourceB' , version = 'HEAD' , public_access = 'None' )
1466+ org_b = OrganizationFactory (mnemonic = 'OrgB' )
1467+ source_b = OrganizationSourceFactory (
1468+ organization = org_b , mnemonic = 'SourceB' , version = 'HEAD' , public_access = 'None' )
14631469 parent_b = ConceptFactory (parent = source_b , mnemonic = 'ParentB' )
1464- child_b = ConceptFactory (parent = source_b , mnemonic = 'ChildB' )
1470+ child_b = ConceptFactory (parent = source_b , mnemonic = 'ChildB' )
14651471
14661472 importing_user = UserProfileFactory (username = 'importer-user' )
14671473 org_a .members .add (importing_user )
0 commit comments