|
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 # noqa: TID253 |
12 | 12 | import pandas as pd # noqa: TID253 |
|
37 | 37 | from sqlmesh.core.console import Console, get_console |
38 | 38 | from sqlmesh.core.context import Context |
39 | 39 | from sqlmesh.core.config.categorizer import CategorizerConfig |
| 40 | +from sqlmesh.core.config.plan import PlanConfig |
40 | 41 | from sqlmesh.core.engine_adapter import EngineAdapter |
41 | 42 | from sqlmesh.core.environment import EnvironmentNamingInfo |
42 | 43 | from sqlmesh.core.macros import macro |
@@ -6252,3 +6253,103 @@ def test_render_path_instead_of_model(tmp_path: Path): |
6252 | 6253 |
|
6253 | 6254 | # Case 3: Render the model successfully |
6254 | 6255 | assert ctx.render("test_model").sql() == 'SELECT 1 AS "col"' |
| 6256 | + |
| 6257 | + |
| 6258 | +@use_terminal_console |
| 6259 | +def test_plan_always_recreate_environment(tmp_path: Path): |
| 6260 | + def plan_with_output(ctx: Context, environment: str): |
| 6261 | + with patch.object(logger, "info") as mock_logger: |
| 6262 | + with capture_output() as output: |
| 6263 | + ctx.load() |
| 6264 | + ctx.plan(environment, no_prompts=True, auto_apply=True) |
| 6265 | + |
| 6266 | + # Facade logs info "Promoting environment {environment}" |
| 6267 | + assert mock_logger.call_args[0][1] == environment |
| 6268 | + |
| 6269 | + return output |
| 6270 | + |
| 6271 | + models_dir = tmp_path / "models" |
| 6272 | + |
| 6273 | + logger = logging.getLogger("sqlmesh.core.state_sync.db.facade") |
| 6274 | + |
| 6275 | + create_temp_file( |
| 6276 | + tmp_path, models_dir / "a.sql", "MODEL (name test.a, kind FULL); SELECT 1 AS col" |
| 6277 | + ) |
| 6278 | + |
| 6279 | + config = Config(plan=PlanConfig(always_recreate_environment=True)) |
| 6280 | + ctx = Context(paths=[tmp_path], config=config) |
| 6281 | + |
| 6282 | + # Case 1: Neither prod nor dev exists, so dev is initialized |
| 6283 | + output = plan_with_output(ctx, "dev") |
| 6284 | + |
| 6285 | + assert """`dev` environment will be initialized""" in output.stdout |
| 6286 | + |
| 6287 | + # Case 2: Prod does not exist, so dev is updated |
| 6288 | + create_temp_file( |
| 6289 | + tmp_path, models_dir / "a.sql", "MODEL (name test.a, kind FULL); SELECT 5 AS col" |
| 6290 | + ) |
| 6291 | + |
| 6292 | + output = plan_with_output(ctx, "dev") |
| 6293 | + assert "`dev` environment will be initialized" in output.stdout |
| 6294 | + |
| 6295 | + # Case 3: Prod is initialized, so plan comparisons moving forward should be against prod |
| 6296 | + output = plan_with_output(ctx, "prod") |
| 6297 | + assert "`prod` environment will be initialized" in output.stdout |
| 6298 | + |
| 6299 | + # Case 4: Dev is updated with a breaking change. Prod exists now so plan comparisons moving forward should be against prod |
| 6300 | + create_temp_file( |
| 6301 | + tmp_path, models_dir / "a.sql", "MODEL (name test.a, kind FULL); SELECT 10 AS col" |
| 6302 | + ) |
| 6303 | + ctx.load() |
| 6304 | + |
| 6305 | + plan = ctx.plan_builder("dev").build() |
| 6306 | + |
| 6307 | + assert ( |
| 6308 | + next(iter(plan.context_diff.snapshots.values())).change_category |
| 6309 | + == SnapshotChangeCategory.BREAKING |
| 6310 | + ) |
| 6311 | + |
| 6312 | + output = plan_with_output(ctx, "dev") |
| 6313 | + assert "New environment `dev` will be created from `prod`" in output.stdout |
| 6314 | + assert "Differences from the `prod` environment" in output.stdout |
| 6315 | + |
| 6316 | + # Case 5: Dev is updated with a metadata change, but comparison against prod shows both the previous and the current changes |
| 6317 | + # so it's still classified as a breaking change |
| 6318 | + create_temp_file( |
| 6319 | + tmp_path, |
| 6320 | + models_dir / "a.sql", |
| 6321 | + "MODEL (name test.a, kind FULL, owner 'test'); SELECT 10 AS col", |
| 6322 | + ) |
| 6323 | + ctx.load() |
| 6324 | + |
| 6325 | + plan = ctx.plan_builder("dev").build() |
| 6326 | + |
| 6327 | + assert ( |
| 6328 | + next(iter(plan.context_diff.snapshots.values())).change_category |
| 6329 | + == SnapshotChangeCategory.BREAKING |
| 6330 | + ) |
| 6331 | + |
| 6332 | + output = plan_with_output(ctx, "dev") |
| 6333 | + assert "New environment `dev` will be created from `prod`" in output.stdout |
| 6334 | + assert "Differences from the `prod` environment" in output.stdout |
| 6335 | + |
| 6336 | + assert ( |
| 6337 | + """MODEL ( |
| 6338 | + name test.a, |
| 6339 | ++ owner test, |
| 6340 | + kind FULL |
| 6341 | + ) |
| 6342 | + SELECT |
| 6343 | +- 5 AS col |
| 6344 | ++ 10 AS col""" |
| 6345 | + in output.stdout |
| 6346 | + ) |
| 6347 | + |
| 6348 | + # Case 6: Ensure that target environment and create_from environment are not the same |
| 6349 | + output = plan_with_output(ctx, "prod") |
| 6350 | + assert not "New environment `prod` will be created from `prod`" in output.stdout |
| 6351 | + |
| 6352 | + # Case 7: Check that we can still run Context::diff() against any environment |
| 6353 | + for environment in ["dev", "prod"]: |
| 6354 | + context_diff = ctx._context_diff(environment) |
| 6355 | + assert context_diff.environment == environment |
0 commit comments