@@ -562,3 +562,136 @@ def test_data_diff_nullable_booleans():
562562"""
563563
564564 assert strip_ansi_codes (output ) == expected_output .strip ()
565+
566+
567+ @pytest .mark .slow
568+ def test_data_diff_multiple_models (sushi_context_fixed_date , capsys , caplog ):
569+ # Create first analytics model
570+ expressions = d .parse (
571+ """
572+ MODEL (name memory.sushi.analytics_1, kind full, grain(key), tags (finance),);
573+ SELECT
574+ key,
575+ value,
576+ FROM
577+ (VALUES
578+ (1, 3),
579+ (2, 4),
580+ ) AS t (key, value)
581+ """
582+ )
583+ model_s = load_sql_based_model (expressions , dialect = "snowflake" )
584+ sushi_context_fixed_date .upsert_model (model_s )
585+
586+ # Create second analytics model from analytics_1
587+ expressions_2 = d .parse (
588+ """
589+ MODEL (name memory.sushi.analytics_2, kind full, grain(key), tags (finance),);
590+ SELECT
591+ key,
592+ value as amount,
593+ FROM
594+ memory.sushi.analytics_1
595+ """
596+ )
597+ model_s2 = load_sql_based_model (expressions_2 , dialect = "snowflake" )
598+ sushi_context_fixed_date .upsert_model (model_s2 )
599+
600+ sushi_context_fixed_date .plan (
601+ "source_dev" ,
602+ no_prompts = True ,
603+ auto_apply = True ,
604+ skip_tests = True ,
605+ start = "2023-01-31" ,
606+ end = "2023-01-31" ,
607+ )
608+
609+ # Modify first model
610+ model = sushi_context_fixed_date .models ['"MEMORY"."SUSHI"."ANALYTICS_1"' ]
611+ modified_model = model .dict ()
612+ modified_model ["query" ] = (
613+ exp .select ("*" )
614+ .from_ (model .query .subquery ())
615+ .union ("SELECT key, value FROM (VALUES (1, 6),(2,3),) AS t (key, value)" )
616+ )
617+ modified_sqlmodel = SqlModel (** modified_model )
618+ sushi_context_fixed_date .upsert_model (modified_sqlmodel )
619+
620+ # Modify second model
621+ model2 = sushi_context_fixed_date .models ['"MEMORY"."SUSHI"."ANALYTICS_2"' ]
622+ modified_model2 = model2 .dict ()
623+ modified_model2 ["query" ] = (
624+ exp .select ("*" )
625+ .from_ (model2 .query .subquery ())
626+ .union ("SELECT key, amount FROM (VALUES (5, 150.2),(6,250.2),) AS t (key, amount)" )
627+ )
628+ modified_sqlmodel2 = SqlModel (** modified_model2 )
629+ sushi_context_fixed_date .upsert_model (modified_sqlmodel2 )
630+
631+ sushi_context_fixed_date .auto_categorize_changes = CategorizerConfig (
632+ sql = AutoCategorizationMode .FULL
633+ )
634+ sushi_context_fixed_date .plan (
635+ "target_dev" ,
636+ create_from = "source_dev" ,
637+ no_prompts = True ,
638+ auto_apply = True ,
639+ skip_tests = True ,
640+ start = "2023-01-31" ,
641+ end = "2023-01-31" ,
642+ )
643+
644+ # Get diffs for both models
645+ selector = {"tag:finance & memory.sushi.analytics*" }
646+ diffs = sushi_context_fixed_date .table_diff (
647+ source = "source_dev" ,
648+ target = "target_dev" ,
649+ on = ["key" ],
650+ select_models = selector ,
651+ skip_grain_check = False ,
652+ )
653+
654+ assert len (diffs ) == 2
655+
656+ # Check analytics_1 diff
657+ diff1 = next (d for d in diffs if "ANALYTICS_1" in d .source )
658+ row_diff1 = diff1 .row_diff ()
659+ assert row_diff1 .full_match_count == 2
660+ assert row_diff1 .full_match_pct == 50.0
661+ assert row_diff1 .s_only_count == 0
662+ assert row_diff1 .t_only_count == 0
663+ assert row_diff1 .stats ["join_count" ] == 4
664+ assert row_diff1 .stats ["null_grain_count" ] == 0
665+ assert row_diff1 .stats ["s_count" ] == 4
666+ assert row_diff1 .stats ["distinct_count_s" ] == 2
667+ assert row_diff1 .stats ["t_count" ] == 4
668+ assert row_diff1 .stats ["distinct_count_t" ] == 2
669+ assert row_diff1 .s_sample .shape == (0 , 2 )
670+ assert row_diff1 .t_sample .shape == (0 , 2 )
671+
672+ # Check analytics_2 diff
673+ diff2 = next (d for d in diffs if "ANALYTICS_2" in d .source )
674+ row_diff2 = diff2 .row_diff ()
675+ assert row_diff2 .full_match_count == 2
676+ assert row_diff2 .full_match_pct == 40.0
677+ assert row_diff2 .s_only_count == 0
678+ assert row_diff2 .t_only_count == 2
679+ assert row_diff2 .stats ["join_count" ] == 4
680+ assert row_diff2 .stats ["null_grain_count" ] == 0
681+ assert row_diff2 .stats ["s_count" ] == 4
682+ assert row_diff2 .stats ["distinct_count_s" ] == 2
683+ assert row_diff2 .stats ["t_count" ] == 6
684+ assert row_diff2 .stats ["distinct_count_t" ] == 4
685+ assert row_diff2 .s_sample .shape == (0 , 2 )
686+ assert row_diff2 .t_sample .shape == (2 , 2 )
687+
688+ # This selector shouldn't return any diffs since both models have this tag
689+ selector = {"^tag:finance" }
690+ diffs = sushi_context_fixed_date .table_diff (
691+ source = "source_dev" ,
692+ target = "target_dev" ,
693+ on = ["key" ],
694+ select_models = selector ,
695+ skip_grain_check = False ,
696+ )
697+ assert len (diffs ) == 0
0 commit comments