|
6 | 6 | from datetime import timedelta |
7 | 7 | from unittest import mock |
8 | 8 | from unittest.mock import patch |
9 | | - |
| 9 | +import logging |
10 | 10 | import os |
11 | 11 | import numpy as np |
12 | 12 | import pandas as pd |
|
36 | 36 | from sqlmesh.core.console import Console, get_console |
37 | 37 | from sqlmesh.core.context import Context |
38 | 38 | from sqlmesh.core.config.categorizer import CategorizerConfig |
| 39 | +from sqlmesh.core.config.plan import PlanConfig |
39 | 40 | from sqlmesh.core.engine_adapter import EngineAdapter |
40 | 41 | from sqlmesh.core.environment import EnvironmentNamingInfo |
41 | 42 | from sqlmesh.core.macros import macro |
@@ -6129,3 +6130,107 @@ def setup_senario(model_before: str, model_after: str): |
6129 | 6130 | 'Binder Error: Referenced column "this_col_does_not_exist" not found in \nFROM clause!' |
6130 | 6131 | in output.stdout |
6131 | 6132 | ) |
| 6133 | + |
| 6134 | + |
| 6135 | +@use_terminal_console |
| 6136 | +def test_plan_always_compare_against_prod(mocker: MockerFixture, tmp_path: Path): |
| 6137 | + def plan_with_output(ctx: Context, environment: str): |
| 6138 | + with patch.object(logger, "info") as mock_logger: |
| 6139 | + with capture_output() as output: |
| 6140 | + ctx.load() |
| 6141 | + ctx.plan(environment, no_prompts=True, auto_apply=True) |
| 6142 | + |
| 6143 | + # Facade logs info "Promoting environment {environment}" |
| 6144 | + assert mock_logger.call_args[0][1] == environment |
| 6145 | + |
| 6146 | + return output |
| 6147 | + |
| 6148 | + models_dir = tmp_path / "models" |
| 6149 | + |
| 6150 | + logger = logging.getLogger("sqlmesh.core.state_sync.db.facade") |
| 6151 | + |
| 6152 | + create_temp_file( |
| 6153 | + tmp_path, models_dir / "a.sql", "MODEL (name test.a, kind FULL); SELECT 1 AS col" |
| 6154 | + ) |
| 6155 | + |
| 6156 | + config = Config(plan=PlanConfig(always_compare_against_prod=True)) |
| 6157 | + ctx = Context(paths=[tmp_path], config=config) |
| 6158 | + |
| 6159 | + # Case 1: Neither prod nor dev exists, so dev is initialized |
| 6160 | + output = plan_with_output(ctx, "dev") |
| 6161 | + |
| 6162 | + assert """`dev` environment will be initialized""" in output.stdout |
| 6163 | + |
| 6164 | + # Case 2: Prod does not exist, so dev is updated |
| 6165 | + create_temp_file( |
| 6166 | + tmp_path, models_dir / "a.sql", "MODEL (name test.a, kind FULL); SELECT 5 AS col" |
| 6167 | + ) |
| 6168 | + |
| 6169 | + plan = ctx.plan_builder("dev").build() |
| 6170 | + |
| 6171 | + assert plan.context_diff.initial_environment == "dev" |
| 6172 | + assert plan.context_diff.environment == "dev" |
| 6173 | + |
| 6174 | + output = plan_with_output(ctx, "dev") |
| 6175 | + |
| 6176 | + assert "Differences from the `dev` environment" in output.stdout |
| 6177 | + |
| 6178 | + # Case 3: Prod is initialized, so plan comparisons moving forward should be against prod |
| 6179 | + output = plan_with_output(ctx, "prod") |
| 6180 | + |
| 6181 | + assert "`prod` environment will be initialized" in output.stdout |
| 6182 | + |
| 6183 | + # Case 4: Dev is updated with a breaking change, so plan comparisons moving forward should be against prod |
| 6184 | + create_temp_file( |
| 6185 | + tmp_path, models_dir / "a.sql", "MODEL (name test.a, kind FULL); SELECT 10 AS col" |
| 6186 | + ) |
| 6187 | + ctx.load() |
| 6188 | + |
| 6189 | + plan = ctx.plan_builder("dev").build() |
| 6190 | + |
| 6191 | + assert plan.context_diff.initial_environment == "dev" |
| 6192 | + assert plan.context_diff.environment == "prod" |
| 6193 | + |
| 6194 | + assert ( |
| 6195 | + next(iter(plan.context_diff.snapshots.values())).change_category |
| 6196 | + == SnapshotChangeCategory.BREAKING |
| 6197 | + ) |
| 6198 | + |
| 6199 | + output = plan_with_output(ctx, "dev") |
| 6200 | + |
| 6201 | + assert "Differences from the `prod` environment" in output.stdout |
| 6202 | + |
| 6203 | + # Case 4: Dev is updated with a metadata change, but comparison against prod shows both the previous and the current changes |
| 6204 | + # so it's still classified as a breaking change |
| 6205 | + create_temp_file( |
| 6206 | + tmp_path, |
| 6207 | + models_dir / "a.sql", |
| 6208 | + "MODEL (name test.a, kind FULL, owner 'test'); SELECT 10 AS col", |
| 6209 | + ) |
| 6210 | + ctx.load() |
| 6211 | + |
| 6212 | + plan = ctx.plan_builder("dev").build() |
| 6213 | + |
| 6214 | + assert plan.context_diff.initial_environment == "dev" |
| 6215 | + assert plan.context_diff.environment == "prod" |
| 6216 | + |
| 6217 | + assert ( |
| 6218 | + next(iter(plan.context_diff.snapshots.values())).change_category |
| 6219 | + == SnapshotChangeCategory.BREAKING |
| 6220 | + ) |
| 6221 | + |
| 6222 | + output = plan_with_output(ctx, "dev") |
| 6223 | + |
| 6224 | + assert "Differences from the `prod` environment" in output.stdout |
| 6225 | + |
| 6226 | + assert ( |
| 6227 | + """MODEL ( |
| 6228 | + name test.a, |
| 6229 | ++ owner test, |
| 6230 | + kind FULL |
| 6231 | + ) |
| 6232 | + SELECT |
| 6233 | +- 5 AS col |
| 6234 | ++ 10 AS col""" |
| 6235 | + in output.stdout |
| 6236 | + ) |
0 commit comments