8585pytestmark = pytest .mark .slow
8686
8787
88+ def count_non_symbolic_snapshots (plan ):
89+ """Count only non-symbolic snapshots (excludes AUDIT_ONLY models)."""
90+ return len ([s for s in plan .new_snapshots if not (s .is_model and s .model .kind .is_symbolic )])
91+
92+
8893@pytest .fixture (autouse = True )
8994def mock_choices (mocker : MockerFixture ):
9095 mocker .patch ("sqlmesh.core.console.TerminalConsole._get_snapshot_change_category" )
@@ -238,7 +243,7 @@ def test_forward_only_model_regular_plan(init_and_plan_context: t.Callable):
238243 top_waiters_snapshot = context .get_snapshot ("sushi.top_waiters" , raise_if_missing = True )
239244
240245 plan = context .plan_builder ("dev" , skip_tests = True , enable_preview = False ).build ()
241- assert len (plan . new_snapshots ) == 2
246+ assert count_non_symbolic_snapshots (plan ) == 2
242247 assert (
243248 plan .context_diff .snapshots [snapshot .snapshot_id ].change_category
244249 == SnapshotChangeCategory .NON_BREAKING
@@ -347,7 +352,7 @@ def test_forward_only_model_regular_plan_preview_enabled(init_and_plan_context:
347352 top_waiters_snapshot = context .get_snapshot ("sushi.top_waiters" , raise_if_missing = True )
348353
349354 plan = context .plan_builder ("dev" , skip_tests = True , enable_preview = True ).build ()
350- assert len (plan . new_snapshots ) == 2
355+ assert count_non_symbolic_snapshots (plan ) == 2
351356 assert (
352357 plan .context_diff .snapshots [snapshot .snapshot_id ].change_category
353358 == SnapshotChangeCategory .NON_BREAKING
@@ -477,7 +482,7 @@ def test_full_history_restatement_model_regular_plan_preview_enabled(
477482
478483 plan = context .plan_builder ("dev" , skip_tests = True , enable_preview = True ).build ()
479484
480- assert len (plan . new_snapshots ) == 6
485+ assert count_non_symbolic_snapshots (plan ) == 6
481486 assert (
482487 plan .context_diff .snapshots [snapshot .snapshot_id ].change_category
483488 == SnapshotChangeCategory .NON_BREAKING
@@ -524,7 +529,7 @@ def test_metadata_changed_regular_plan_preview_enabled(init_and_plan_context: t.
524529 top_waiters_snapshot = context .get_snapshot ("sushi.top_waiters" , raise_if_missing = True )
525530
526531 plan = context .plan_builder ("dev" , skip_tests = True , enable_preview = True ).build ()
527- assert len (plan . new_snapshots ) == 2
532+ assert count_non_symbolic_snapshots (plan ) == 2
528533 assert (
529534 plan .context_diff .snapshots [snapshot .snapshot_id ].change_category
530535 == SnapshotChangeCategory .METADATA
@@ -887,7 +892,7 @@ def test_forward_only_parent_created_in_dev_child_created_in_prod(
887892 top_waiters_snapshot = context .get_snapshot ("sushi.top_waiters" , raise_if_missing = True )
888893
889894 plan = context .plan_builder ("dev" , skip_tests = True , enable_preview = False ).build ()
890- assert len (plan . new_snapshots ) == 2
895+ assert count_non_symbolic_snapshots (plan ) == 2
891896 assert (
892897 plan .context_diff .snapshots [waiter_revenue_by_day_snapshot .snapshot_id ].change_category
893898 == SnapshotChangeCategory .NON_BREAKING
@@ -910,7 +915,7 @@ def test_forward_only_parent_created_in_dev_child_created_in_prod(
910915 top_waiters_snapshot = context .get_snapshot ("sushi.top_waiters" , raise_if_missing = True )
911916
912917 plan = context .plan_builder ("prod" , skip_tests = True , enable_preview = False ).build ()
913- assert len (plan . new_snapshots ) == 1
918+ assert count_non_symbolic_snapshots (plan ) == 1
914919 assert (
915920 plan .context_diff .snapshots [top_waiters_snapshot .snapshot_id ].change_category
916921 == SnapshotChangeCategory .NON_BREAKING
@@ -952,7 +957,7 @@ def test_plan_set_choice_is_reflected_in_missing_intervals(init_and_plan_context
952957
953958 plan_builder = context .plan_builder ("dev" , skip_tests = True )
954959 plan = plan_builder .build ()
955- assert len (plan . new_snapshots ) == 2
960+ assert count_non_symbolic_snapshots (plan ) == 2
956961 assert (
957962 plan .context_diff .snapshots [snapshot .snapshot_id ].change_category
958963 == SnapshotChangeCategory .NON_BREAKING
@@ -1100,7 +1105,7 @@ def test_non_breaking_change_after_forward_only_in_dev(
11001105 top_waiters_snapshot = context .get_snapshot ("sushi.top_waiters" , raise_if_missing = True )
11011106
11021107 plan = context .plan_builder ("dev" , skip_tests = True , forward_only = True ).build ()
1103- assert len (plan . new_snapshots ) == 2
1108+ assert count_non_symbolic_snapshots (plan ) == 2
11041109 assert (
11051110 plan .context_diff .snapshots [waiter_revenue_by_day_snapshot .snapshot_id ].change_category
11061111 == SnapshotChangeCategory .NON_BREAKING
@@ -1133,7 +1138,7 @@ def test_non_breaking_change_after_forward_only_in_dev(
11331138 top_waiters_snapshot = context .get_snapshot ("sushi.top_waiters" , raise_if_missing = True )
11341139
11351140 plan = context .plan_builder ("dev" , skip_tests = True ).build ()
1136- assert len (plan . new_snapshots ) == 1
1141+ assert count_non_symbolic_snapshots (plan ) == 1
11371142 assert (
11381143 plan .context_diff .snapshots [top_waiters_snapshot .snapshot_id ].change_category
11391144 == SnapshotChangeCategory .NON_BREAKING
@@ -1232,7 +1237,7 @@ def test_indirect_non_breaking_change_after_forward_only_in_dev(init_and_plan_co
12321237 top_waiters_snapshot = context .get_snapshot ("sushi.top_waiters" , raise_if_missing = True )
12331238
12341239 plan = context .plan_builder ("dev" , skip_tests = True , enable_preview = False ).build ()
1235- assert len (plan . new_snapshots ) == 1
1240+ assert count_non_symbolic_snapshots (plan ) == 1
12361241 assert (
12371242 plan .context_diff .snapshots [top_waiters_snapshot .snapshot_id ].change_category
12381243 == SnapshotChangeCategory .NON_BREAKING
@@ -1265,7 +1270,7 @@ def test_indirect_non_breaking_change_after_forward_only_in_dev(init_and_plan_co
12651270 top_waiters_snapshot = context .get_snapshot ("sushi.top_waiters" , raise_if_missing = True )
12661271
12671272 plan = context .plan_builder ("dev" , skip_tests = True , enable_preview = False ).build ()
1268- assert len (plan . new_snapshots ) == 2
1273+ assert count_non_symbolic_snapshots (plan ) == 2
12691274 assert (
12701275 plan .context_diff .snapshots [waiter_revenue_by_day_snapshot .snapshot_id ].change_category
12711276 == SnapshotChangeCategory .NON_BREAKING
@@ -1383,14 +1388,14 @@ def test_metadata_change_after_forward_only_results_in_migration(init_and_plan_c
13831388 model = add_projection_to_model (t .cast (SqlModel , model ))
13841389 context .upsert_model (model )
13851390 plan = context .plan ("dev" , skip_tests = True , auto_apply = True , no_prompts = True )
1386- assert len (plan . new_snapshots ) == 2
1391+ assert count_non_symbolic_snapshots (plan ) == 2
13871392 assert all (s .is_forward_only for s in plan .new_snapshots )
13881393
13891394 # Follow-up with a metadata change in the same environment
13901395 model = model .copy (update = {"owner" : "new_owner" })
13911396 context .upsert_model (model )
13921397 plan = context .plan ("dev" , skip_tests = True , auto_apply = True , no_prompts = True )
1393- assert len (plan . new_snapshots ) == 2
1398+ assert count_non_symbolic_snapshots (plan ) == 2
13941399 assert all (s .change_category == SnapshotChangeCategory .METADATA for s in plan .new_snapshots )
13951400
13961401 # Deploy the latest change to prod
@@ -1581,7 +1586,10 @@ def test_run_with_select_models(
15811586
15821587 snapshots = context .state_sync .state_sync .get_snapshots (context .snapshots .values ())
15831588 # Only waiter_revenue_by_day and its parents should be backfilled up to 2023-01-09.
1584- assert {s .name : s .intervals [0 ][1 ] for s in snapshots .values () if s .intervals } == {
1589+ # Filter out AUDIT_ONLY models (but keep external models which also have is_symbolic=True)
1590+ non_audit_snapshots = {s .name : s .intervals [0 ][1 ] for s in snapshots .values ()
1591+ if s .intervals and not (s .is_model and s .model .kind .is_audit_only )}
1592+ assert non_audit_snapshots == {
15851593 '"memory"."sushi"."waiter_revenue_by_day"' : to_timestamp ("2023-01-09" ),
15861594 '"memory"."sushi"."order_items"' : to_timestamp ("2023-01-09" ),
15871595 '"memory"."sushi"."orders"' : to_timestamp ("2023-01-09" ),
@@ -1621,7 +1629,10 @@ def test_plan_with_run(
16211629 context .apply (plan )
16221630
16231631 snapshots = context .state_sync .state_sync .get_snapshots (context .snapshots .values ())
1624- assert {s .name : s .intervals [0 ][1 ] for s in snapshots .values () if s .intervals } == {
1632+ # Filter out AUDIT_ONLY models (but keep external models which also have is_symbolic=True)
1633+ non_audit_snapshots = {s .name : s .intervals [0 ][1 ] for s in snapshots .values ()
1634+ if s .intervals and not (s .is_model and s .model .kind .is_audit_only )}
1635+ assert non_audit_snapshots == {
16251636 '"memory"."sushi"."waiter_revenue_by_day"' : to_timestamp ("2023-01-09" ),
16261637 '"memory"."sushi"."order_items"' : to_timestamp ("2023-01-09" ),
16271638 '"memory"."sushi"."orders"' : to_timestamp ("2023-01-09" ),
@@ -1791,7 +1802,10 @@ def test_run_with_select_models_no_auto_upstream(
17911802
17921803 snapshots = context .state_sync .state_sync .get_snapshots (context .snapshots .values ())
17931804 # Only waiter_revenue_by_day should be backfilled up to 2023-01-09.
1794- assert {s .name : s .intervals [0 ][1 ] for s in snapshots .values () if s .intervals } == {
1805+ # Filter out AUDIT_ONLY models (but keep external models which also have is_symbolic=True)
1806+ non_audit_snapshots = {s .name : s .intervals [0 ][1 ] for s in snapshots .values ()
1807+ if s .intervals and not (s .is_model and s .model .kind .is_audit_only )}
1808+ assert non_audit_snapshots == {
17951809 '"memory"."sushi"."waiter_revenue_by_day"' : to_timestamp ("2023-01-09" ),
17961810 '"memory"."sushi"."order_items"' : to_timestamp ("2023-01-08" ),
17971811 '"memory"."sushi"."orders"' : to_timestamp ("2023-01-08" ),
@@ -5249,7 +5263,7 @@ def test_plan_explain(init_and_plan_context: t.Callable):
52495263 assert plan .has_changes
52505264 assert plan .missing_intervals
52515265 assert plan .directly_modified == {waiter_revenue_by_day_snapshot .snapshot_id }
5252- assert len (plan . new_snapshots ) == 2
5266+ assert count_non_symbolic_snapshots (plan ) == 2
52535267 assert {s .snapshot_id for s in plan .new_snapshots } == {
52545268 waiter_revenue_by_day_snapshot .snapshot_id ,
52555269 top_waiters_snapshot .snapshot_id ,
@@ -5787,7 +5801,7 @@ def test_multi(mocker):
57875801 )
57885802 context ._new_state_sync ().reset (default_catalog = context .default_catalog )
57895803 plan = context .plan_builder ().build ()
5790- assert len (plan . new_snapshots ) == 5
5804+ assert count_non_symbolic_snapshots (plan ) == 5
57915805 context .apply (plan )
57925806
57935807 # Ensure before_all, after_all statements for multiple repos have executed
@@ -5957,7 +5971,7 @@ def test_multi_virtual_layer(copy_to_temp_path):
59575971 )
59585972
59595973 plan = context .plan_builder ().build ()
5960- assert len (plan . new_snapshots ) == 4
5974+ assert count_non_symbolic_snapshots (plan ) == 4
59615975 context .apply (plan )
59625976
59635977 # Validate the tables that source from the first tables are correct as well with evaluate
@@ -6100,7 +6114,7 @@ def test_multi_dbt(mocker):
61006114 context = Context (paths = ["examples/multi_dbt/bronze" , "examples/multi_dbt/silver" ])
61016115 context ._new_state_sync ().reset (default_catalog = context .default_catalog )
61026116 plan = context .plan_builder ().build ()
6103- assert len (plan . new_snapshots ) == 4
6117+ assert count_non_symbolic_snapshots (plan ) == 4
61046118 context .apply (plan )
61056119 validate_apply_basics (context , c .PROD , plan .snapshots .values ())
61066120
@@ -6130,7 +6144,7 @@ def test_multi_hybrid(mocker):
61306144 context ._new_state_sync ().reset (default_catalog = context .default_catalog )
61316145 plan = context .plan_builder ().build ()
61326146
6133- assert len (plan . new_snapshots ) == 5
6147+ assert count_non_symbolic_snapshots (plan ) == 5
61346148 assert context .dag .roots == {'"memory"."dbt_repo"."e"' }
61356149 assert context .dag .graph ['"memory"."dbt_repo"."c"' ] == {'"memory"."sqlmesh_repo"."b"' }
61366150 assert context .dag .graph ['"memory"."sqlmesh_repo"."b"' ] == {'"memory"."sqlmesh_repo"."a"' }
@@ -7106,6 +7120,9 @@ def validate_tables(
71067120 is_deployable = deployability_index .is_representative (snapshot )
71077121 if not snapshot .is_model or snapshot .is_external :
71087122 continue
7123+ # AUDIT_ONLY models are symbolic and don't create tables
7124+ if snapshot .model .kind .is_symbolic :
7125+ continue
71097126 table_should_exist = not snapshot .is_embedded
71107127 assert adapter .table_exists (snapshot .table_name (is_deployable )) == table_should_exist
71117128 if table_should_exist :
@@ -7301,7 +7318,7 @@ def test_destroy(copy_to_temp_path):
73017318
73027319 context = Context (paths = paths , config = config )
73037320 plan = context .plan_builder ().build ()
7304- assert len (plan . new_snapshots ) == 4
7321+ assert count_non_symbolic_snapshots (plan ) == 4
73057322 context .apply (plan )
73067323
73077324 # Confirm cache exists
0 commit comments