|
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 |
@@ -6208,3 +6209,107 @@ def test_render_path_instead_of_model(tmp_path: Path): |
6208 | 6209 |
|
6209 | 6210 | # Case 3: Render the model successfully |
6210 | 6211 | assert ctx.render("test_model").sql() == 'SELECT 1 AS "col"' |
| 6212 | + |
| 6213 | + |
| 6214 | +@use_terminal_console |
| 6215 | +def test_plan_always_compare_against_prod(mocker: MockerFixture, tmp_path: Path): |
| 6216 | + def plan_with_output(ctx: Context, environment: str): |
| 6217 | + with patch.object(logger, "info") as mock_logger: |
| 6218 | + with capture_output() as output: |
| 6219 | + ctx.load() |
| 6220 | + ctx.plan(environment, no_prompts=True, auto_apply=True) |
| 6221 | + |
| 6222 | + # Facade logs info "Promoting environment {environment}" |
| 6223 | + assert mock_logger.call_args[0][1] == environment |
| 6224 | + |
| 6225 | + return output |
| 6226 | + |
| 6227 | + models_dir = tmp_path / "models" |
| 6228 | + |
| 6229 | + logger = logging.getLogger("sqlmesh.core.state_sync.db.facade") |
| 6230 | + |
| 6231 | + create_temp_file( |
| 6232 | + tmp_path, models_dir / "a.sql", "MODEL (name test.a, kind FULL); SELECT 1 AS col" |
| 6233 | + ) |
| 6234 | + |
| 6235 | + config = Config(plan=PlanConfig(always_compare_against_prod=True)) |
| 6236 | + ctx = Context(paths=[tmp_path], config=config) |
| 6237 | + |
| 6238 | + # Case 1: Neither prod nor dev exists, so dev is initialized |
| 6239 | + output = plan_with_output(ctx, "dev") |
| 6240 | + |
| 6241 | + assert """`dev` environment will be initialized""" in output.stdout |
| 6242 | + |
| 6243 | + # Case 2: Prod does not exist, so dev is updated |
| 6244 | + create_temp_file( |
| 6245 | + tmp_path, models_dir / "a.sql", "MODEL (name test.a, kind FULL); SELECT 5 AS col" |
| 6246 | + ) |
| 6247 | + |
| 6248 | + plan = ctx.plan_builder("dev").build() |
| 6249 | + |
| 6250 | + assert plan.context_diff.initial_environment == "dev" |
| 6251 | + assert plan.context_diff.environment == "dev" |
| 6252 | + |
| 6253 | + output = plan_with_output(ctx, "dev") |
| 6254 | + |
| 6255 | + assert "Differences from the `dev` environment" in output.stdout |
| 6256 | + |
| 6257 | + # Case 3: Prod is initialized, so plan comparisons moving forward should be against prod |
| 6258 | + output = plan_with_output(ctx, "prod") |
| 6259 | + |
| 6260 | + assert "`prod` environment will be initialized" in output.stdout |
| 6261 | + |
| 6262 | + # Case 4: Dev is updated with a breaking change, so plan comparisons moving forward should be against prod |
| 6263 | + create_temp_file( |
| 6264 | + tmp_path, models_dir / "a.sql", "MODEL (name test.a, kind FULL); SELECT 10 AS col" |
| 6265 | + ) |
| 6266 | + ctx.load() |
| 6267 | + |
| 6268 | + plan = ctx.plan_builder("dev").build() |
| 6269 | + |
| 6270 | + assert plan.context_diff.initial_environment == "dev" |
| 6271 | + assert plan.context_diff.environment == "prod" |
| 6272 | + |
| 6273 | + assert ( |
| 6274 | + next(iter(plan.context_diff.snapshots.values())).change_category |
| 6275 | + == SnapshotChangeCategory.BREAKING |
| 6276 | + ) |
| 6277 | + |
| 6278 | + output = plan_with_output(ctx, "dev") |
| 6279 | + |
| 6280 | + assert "Differences from the `prod` environment" in output.stdout |
| 6281 | + |
| 6282 | + # Case 4: Dev is updated with a metadata change, but comparison against prod shows both the previous and the current changes |
| 6283 | + # so it's still classified as a breaking change |
| 6284 | + create_temp_file( |
| 6285 | + tmp_path, |
| 6286 | + models_dir / "a.sql", |
| 6287 | + "MODEL (name test.a, kind FULL, owner 'test'); SELECT 10 AS col", |
| 6288 | + ) |
| 6289 | + ctx.load() |
| 6290 | + |
| 6291 | + plan = ctx.plan_builder("dev").build() |
| 6292 | + |
| 6293 | + assert plan.context_diff.initial_environment == "dev" |
| 6294 | + assert plan.context_diff.environment == "prod" |
| 6295 | + |
| 6296 | + assert ( |
| 6297 | + next(iter(plan.context_diff.snapshots.values())).change_category |
| 6298 | + == SnapshotChangeCategory.BREAKING |
| 6299 | + ) |
| 6300 | + |
| 6301 | + output = plan_with_output(ctx, "dev") |
| 6302 | + |
| 6303 | + assert "Differences from the `prod` environment" in output.stdout |
| 6304 | + |
| 6305 | + assert ( |
| 6306 | + """MODEL ( |
| 6307 | + name test.a, |
| 6308 | ++ owner test, |
| 6309 | + kind FULL |
| 6310 | + ) |
| 6311 | + SELECT |
| 6312 | +- 5 AS col |
| 6313 | ++ 10 AS col""" |
| 6314 | + in output.stdout |
| 6315 | + ) |
0 commit comments