@@ -399,28 +399,24 @@ def test_build_column_lineage_from_parser_resolves_alias(self):
399399 mock_from_entity .name .root = "relation_table"
400400 mock_data_model = MagicMock ()
401401
402- with patch (
403- "metadata.ingestion.source.dashboard.quicksight.metadata.get_column_fqn" ,
404- return_value = src_fqn ,
405- ) as mock_get_col_fqn :
406- with patch .object (
402+ with (
403+ patch (
404+ "metadata.ingestion.source.dashboard.quicksight.metadata.get_column_fqn" ,
405+ return_value = src_fqn ,
406+ ) as mock_get_col_fqn ,
407+ patch .object (
407408 self .quicksight ,
408409 "_get_data_model_column_fqn" ,
409410 return_value = alias_fqn ,
410- ) as mock_get_dm_col_fqn :
411- result = self .quicksight ._build_column_lineage_from_parser (
412- mock_parser , mock_from_entity , mock_data_model
413- )
411+ ) as mock_get_dm_col_fqn ,
412+ ):
413+ result = self .quicksight ._build_column_lineage_from_parser (mock_parser , mock_from_entity , mock_data_model )
414414
415- mock_get_col_fqn .assert_called_once_with (
416- table_entity = mock_from_entity , column = "id"
417- )
418- mock_get_dm_col_fqn .assert_called_once_with (
419- data_model_entity = mock_data_model , column = "relation_id"
420- )
415+ mock_get_col_fqn .assert_called_once_with (table_entity = mock_from_entity , column = "id" )
416+ mock_get_dm_col_fqn .assert_called_once_with (data_model_entity = mock_data_model , column = "relation_id" )
421417 assert len (result ) == 1
422- assert result [0 ].fromColumns == [ src_fqn ]
423- assert result [0 ].toColumn == alias_fqn
418+ assert result [0 ].fromColumns [ 0 ]. root == src_fqn
419+ assert result [0 ].toColumn . root == alias_fqn
424420
425421 @pytest .mark .order (11 )
426422 def test_build_column_lineage_from_parser_multi_table_filters_correctly (self ):
@@ -434,19 +430,15 @@ def test_build_column_lineage_from_parser_multi_table_filters_correctly(self):
434430 # Column from the correct upstream table
435431 src_col_correct = MagicMock ()
436432 src_col_correct .raw_name = "id"
437- src_col_correct ._parent = MagicMock ()
438- src_col_correct ._parent .__str__ = MagicMock (
439- return_value = "relation_table"
440- )
433+ src_col_correct ._parent = type ("_FakeTable" , (), {"__str__" : lambda self : "relation_table" })()
441434
442435 tgt_col_correct = MagicMock ()
443436 tgt_col_correct .raw_name = "relation_id"
444437
445438 # Column from a DIFFERENT table with same name 'id'
446439 src_col_wrong = MagicMock ()
447440 src_col_wrong .raw_name = "id"
448- src_col_wrong ._parent = MagicMock ()
449- src_col_wrong ._parent .__str__ = MagicMock (return_value = "other_table" )
441+ src_col_wrong ._parent = type ("_FakeTable" , (), {"__str__" : lambda self : "other_table" })()
450442
451443 tgt_col_wrong = MagicMock ()
452444 tgt_col_wrong .raw_name = "other_relation_id"
@@ -464,66 +456,23 @@ def test_build_column_lineage_from_parser_multi_table_filters_correctly(self):
464456 mock_from_entity .name .root = "relation_table"
465457 mock_data_model = MagicMock ()
466458
467- with patch (
468- "metadata.ingestion.source.dashboard.quicksight.metadata.get_column_fqn" ,
469- return_value = src_fqn ,
470- ):
471- with patch .object (
459+ with (
460+ patch (
461+ "metadata.ingestion.source.dashboard.quicksight.metadata.get_column_fqn" ,
462+ return_value = src_fqn ,
463+ ),
464+ patch .object (
472465 self .quicksight ,
473466 "_get_data_model_column_fqn" ,
474467 return_value = alias_fqn ,
475- ):
476- result = self .quicksight ._build_column_lineage_from_parser (
477- mock_parser , mock_from_entity , mock_data_model
478- )
468+ ),
469+ ):
470+ result = self .quicksight ._build_column_lineage_from_parser (mock_parser , mock_from_entity , mock_data_model )
479471
480472 # Only 1 result — the wrong table's column must be filtered out
481473 assert len (result ) == 1
482- assert result [0 ].fromColumns == [src_fqn ]
483- assert result [0 ].toColumn == alias_fqn
484-
485- @pytest .mark .order (11 )
486- def test_build_column_lineage_no_fallback_when_parser_has_global_lineage (self ):
487- """
488- Regression test for the multi-table fallback bug (Issue #26670).
489-
490- When lineage_parser.column_lineage is non-empty (parser succeeded)
491- but none of the pairs match from_entity (because they belong to a
492- different upstream table in a multi-table JOIN), the method must
493- return an empty list and must NOT call _get_column_lineage (the
494- name-based fallback). Calling the fallback here would manufacture
495- incorrect cross-table column lineage.
496- """
497- # Parser found lineage for a DIFFERENT table, not our from_entity
498- other_src_col = MagicMock ()
499- other_src_col .raw_name = "user_id"
500- other_src_col ._parent = MagicMock ()
501- other_src_col ._parent .__str__ = MagicMock (return_value = "users_table" )
502-
503- other_tgt_col = MagicMock ()
504- other_tgt_col .raw_name = "uid"
505-
506- mock_parser = MagicMock ()
507- # Parser globally found lineage — but only for 'users_table'
508- mock_parser .column_lineage = [(other_src_col , other_tgt_col )]
509-
510- mock_from_entity = MagicMock ()
511- # Our from_entity is 'orders_table' — no parser pairs match it
512- mock_from_entity .name .root = "orders_table"
513- mock_data_model = MagicMock ()
514-
515- with patch .object (
516- self .quicksight ,
517- "_get_column_lineage" ,
518- ) as mock_fallback :
519- result = self .quicksight ._build_column_lineage_from_parser (
520- mock_parser , mock_from_entity , mock_data_model
521- )
522-
523- # Must NOT have called the name-based fallback
524- mock_fallback .assert_not_called ()
525- # Must return an empty list — no manufactured lineage
526- assert result == []
474+ assert result [0 ].fromColumns [0 ].root == src_fqn
475+ assert result [0 ].toColumn .root == alias_fqn
527476
528477 @pytest .mark .order (12 )
529478 def test_build_column_lineage_no_fallback_when_parser_has_global_lineage (self ):
@@ -559,9 +508,7 @@ def test_build_column_lineage_no_fallback_when_parser_has_global_lineage(self):
559508 self .quicksight ,
560509 "_get_column_lineage" ,
561510 ) as mock_fallback :
562- result = self .quicksight ._build_column_lineage_from_parser (
563- mock_parser , mock_from_entity , mock_data_model
564- )
511+ result = self .quicksight ._build_column_lineage_from_parser (mock_parser , mock_from_entity , mock_data_model )
565512
566513 # Must NOT have called the name-based fallback
567514 mock_fallback .assert_not_called ()
@@ -578,9 +525,7 @@ def test_build_column_lineage_from_parser_iterable_parent(self):
578525 """
579526 # Parent is an iterable (list) of Table objects
580527 parent_table_mock = MagicMock ()
581- parent_table_mock .__str__ = MagicMock (
582- return_value = "relation_table"
583- )
528+ parent_table_mock .__str__ = MagicMock (return_value = "relation_table" )
584529
585530 src_col = MagicMock ()
586531 src_col .raw_name = "id"
@@ -600,26 +545,26 @@ def test_build_column_lineage_from_parser_iterable_parent(self):
600545 mock_from_entity .name .root = "relation_table"
601546 mock_data_model = MagicMock ()
602547
603- with patch (
604- "metadata.ingestion.source.dashboard.quicksight.metadata.get_column_fqn" ,
605- return_value = src_fqn ,
606- ):
607- with patch .object (
548+ with (
549+ patch (
550+ "metadata.ingestion.source.dashboard.quicksight.metadata.get_column_fqn" ,
551+ return_value = src_fqn ,
552+ ),
553+ patch .object (
608554 self .quicksight ,
609555 "_get_data_model_column_fqn" ,
610556 return_value = alias_fqn ,
611- ):
612- result = (
613- self .quicksight ._build_column_lineage_from_parser (
614- mock_parser ,
615- mock_from_entity ,
616- mock_data_model ,
617- )
618- )
557+ ),
558+ ):
559+ result = self .quicksight ._build_column_lineage_from_parser (
560+ mock_parser ,
561+ mock_from_entity ,
562+ mock_data_model ,
563+ )
619564
620565 assert len (result ) == 1
621- assert result [0 ].fromColumns == [ src_fqn ]
622- assert result [0 ].toColumn == alias_fqn
566+ assert result [0 ].fromColumns [ 0 ]. root == src_fqn
567+ assert result [0 ].toColumn . root == alias_fqn
623568
624569 @pytest .mark .order (14 )
625570 def test_build_column_lineage_from_parser_falls_back_when_empty (self ):
@@ -649,12 +594,8 @@ def test_build_column_lineage_from_parser_falls_back_when_empty(self):
649594 "_get_column_lineage" ,
650595 return_value = fallback_lineage ,
651596 ) as mock_get_col_lineage :
652- result = self .quicksight ._build_column_lineage_from_parser (
653- mock_parser , mock_from_entity , mock_data_model
654- )
597+ result = self .quicksight ._build_column_lineage_from_parser (mock_parser , mock_from_entity , mock_data_model )
655598
656599 # Verify fallback was called with correct column names
657- mock_get_col_lineage .assert_called_once_with (
658- mock_from_entity , mock_data_model , ["col_a" ]
659- )
600+ mock_get_col_lineage .assert_called_once_with (mock_from_entity , mock_data_model , ["col_a" ])
660601 assert result is fallback_lineage
0 commit comments