|
19 | 19 | from sqlmesh.core.config.connection import DuckDBConnectionConfig |
20 | 20 | from sqlmesh.core.engine_adapter import DuckDBEngineAdapter |
21 | 21 | from sqlmesh.utils.pandas import columns_to_types_from_df |
22 | | -from sqlmesh.utils.yaml import YAML |
| 22 | +from sqlmesh.utils.yaml import YAML, load as yaml_load, dump as yaml_dump |
| 23 | +from sqlmesh_dbt.operations import init_project_if_required |
23 | 24 | from tests.utils.pandas import compare_dataframes, create_df |
24 | 25 |
|
25 | 26 | # Some developers had issues with this test freezing locally so we mark it as cicdonly |
@@ -604,3 +605,50 @@ def test_dbt_node_info(jaffle_shop_duckdb_context: Context): |
604 | 605 | relationship_audit.node.dbt_node_info.name |
605 | 606 | == "relationships_orders_customer_id__customer_id__ref_customers_" |
606 | 607 | ) |
| 608 | + |
| 609 | + |
| 610 | +def test_state_schema_isolation_per_target(jaffle_shop_duckdb: Path): |
| 611 | + profiles_file = jaffle_shop_duckdb / "profiles.yml" |
| 612 | + |
| 613 | + profiles_yml = yaml_load(profiles_file) |
| 614 | + |
| 615 | + # make prod / dev config identical with the exception of a different default schema to simulate using the same warehouse |
| 616 | + profiles_yml["jaffle_shop"]["outputs"]["prod"] = { |
| 617 | + **profiles_yml["jaffle_shop"]["outputs"]["dev"] |
| 618 | + } |
| 619 | + profiles_yml["jaffle_shop"]["outputs"]["prod"]["schema"] = "prod_schema" |
| 620 | + profiles_yml["jaffle_shop"]["outputs"]["dev"]["schema"] = "dev_schema" |
| 621 | + |
| 622 | + profiles_file.write_text(yaml_dump(profiles_yml)) |
| 623 | + |
| 624 | + init_project_if_required(jaffle_shop_duckdb) |
| 625 | + |
| 626 | + # start off with the prod target |
| 627 | + prod_ctx = Context(paths=[jaffle_shop_duckdb], config_loader_kwargs={"target": "prod"}) |
| 628 | + assert prod_ctx.config.get_state_schema() == "sqlmesh_state_jaffle_shop_prod_schema" |
| 629 | + assert all("prod_schema" in fqn for fqn in prod_ctx.models) |
| 630 | + assert prod_ctx.plan(auto_apply=True).has_changes |
| 631 | + assert not prod_ctx.plan(auto_apply=True).has_changes |
| 632 | + |
| 633 | + # dev target should have changes - new state separate from prod |
| 634 | + dev_ctx = Context(paths=[jaffle_shop_duckdb], config_loader_kwargs={"target": "dev"}) |
| 635 | + assert dev_ctx.config.get_state_schema() == "sqlmesh_state_jaffle_shop_dev_schema" |
| 636 | + assert all("dev_schema" in fqn for fqn in dev_ctx.models) |
| 637 | + assert dev_ctx.plan(auto_apply=True).has_changes |
| 638 | + assert not dev_ctx.plan(auto_apply=True).has_changes |
| 639 | + |
| 640 | + # no explicitly specified target should use dev because that's what's set for the default in the profiles.yml |
| 641 | + assert profiles_yml["jaffle_shop"]["target"] == "dev" |
| 642 | + default_ctx = Context(paths=[jaffle_shop_duckdb]) |
| 643 | + assert default_ctx.config.get_state_schema() == "sqlmesh_state_jaffle_shop_dev_schema" |
| 644 | + assert all("dev_schema" in fqn for fqn in default_ctx.models) |
| 645 | + assert not default_ctx.plan(auto_apply=True).has_changes |
| 646 | + |
| 647 | + # an explicit state schema override set in `sqlmesh.yaml` should use that |
| 648 | + sqlmesh_yaml_file = jaffle_shop_duckdb / "sqlmesh.yaml" |
| 649 | + sqlmesh_yaml = yaml_load(sqlmesh_yaml_file) |
| 650 | + sqlmesh_yaml["gateways"] = {"dev": {"state_schema": "sqlmesh_dev_state_override"}} |
| 651 | + sqlmesh_yaml_file.write_text(yaml_dump(sqlmesh_yaml)) |
| 652 | + default_ctx = Context(paths=[jaffle_shop_duckdb]) |
| 653 | + assert default_ctx.config.get_state_schema() == "sqlmesh_dev_state_override" |
| 654 | + assert all("dev_schema" in fqn for fqn in default_ctx.models) |
0 commit comments