Skip to content

Commit 2d36bee

Browse files
authored
test: stabilize TestChangingSchema* python-model tests under min-deps (dbt-core 1.11.2) (#1488)
## Problem The nightly **Integration Tests (Min-Deps)** workflow fails on the SQL-warehouse shard ([run 26536340109](https://github.com/databricks/dbt-databricks/actions/runs/26536340109)): ``` FAILED tests/functional/adapter/python_model/test_python_model.py::TestChangingSchemaV2::test_changing_unique_tmp_table_suffix FAILED tests/functional/adapter/python_model/test_python_model.py::TestChangingSchemaIncrementalV2::test_changing_unique_tmp_table_suffix dbt.exceptions.ParsingError: Failed to render models/schema.yml ... dbt.exceptions.EnvVarMissingError: Env var required but not provided: 'DBT_TEST_SCHEMA_NAME_VARIABLE' ``` The same `main` commit **passes** these tests on latest-deps (dbt-core 1.11.8) the same night — this is specific to **dbt-core 1.11.2**, newly exercised since min-deps became first-class CI (#1480, #1476). ## Root cause Under `--dist=loadfile`, every `test_python_model.py` class runs in one xdist worker. On dbt-core 1.11.2, a sibling `BasePythonModelTests` class's `test_source` schema.yml — whose `schema` renders `{{ var(env_var('DBT_TEST_SCHEMA_NAME_VARIABLE')) }}` — leaks into the parse of a *following* class. The `TestChangingSchema*` classes don't inherit `BasePythonModelTests`, so they supply neither thing that leaked source needs: - `env_var('DBT_TEST_SCHEMA_NAME_VARIABLE')` resolves from the per-class invocation-context snapshot → needs the env var set. - `var('test_run_schema')` is resolved by `ConfiguredVar` during schema rendering, which reads **only `config.cli_vars`** (`dbt/context/configured.py`) → needs `--vars`, not project-level `vars:`. It is position-dependent: `TestChangingSchema` (V1) passes only because its predecessor has no source; the V2 classes run right after `TestServerlessCluster*`. dbt-core 1.11.8 isolates parses correctly, so latest-deps is unaffected. ## Fix Test-only. A `SchemaNameVarMixin` that (a) sets `DBT_TEST_SCHEMA_NAME_VARIABLE` via an autouse fixture and (b) passes `test_run_schema` via `--vars` on `run_dbt` — exactly mirroring dbt-core's own `BasePythonModelTests`. Applied to all four `TestChangingSchema*` classes (the two failing V2 classes plus the two V1 classes, which pass only by ordering luck today). Inert on dbt-core 1.11.8. ## Verification Against the live SQL warehouse under min-deps (dbt-core 1.11.2), full file with `-n 2 --dist=loadfile --reruns 1` (faithful to CI): - Before: **2 failed** (`EnvVarMissingError`) — reproduces CI. - After: **11 passed, 9 skipped, 0 failed** (both V2 classes pass).
1 parent 437a7c3 commit 2d36bee

2 files changed

Lines changed: 44 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- Raise the `dbt-tests-adapter` test-dependency floor to `>=1.20.0` to pick up its `persist_docs` fixture typo fix (test-only, no runtime impact) ([#1490](https://github.com/databricks/dbt-databricks/pull/1490))
1717
- Defer SDK `Config` construction to connection-open time so offline paths (`dbt parse`/`list`/`compile`) don't trigger the host-metadata probe introduced in `databricks-sdk>=0.103`; as a side effect, auth errors now surface at first connection rather than during profile parsing. ([#1474](https://github.com/databricks/dbt-databricks/pull/1474))
1818
- Bump ceilings on `databricks-sdk` (now `<0.105.0`) and `databricks-sql-connector[pyarrow]` (now `<4.3.0`) to admit newer releases; floors unchanged. ([#1474](https://github.com/databricks/dbt-databricks/pull/1474))
19+
- Stabilize the `TestChangingSchema*` Python-model functional tests under min-deps (dbt-core 1.11.2), where a sibling class's source schema.yml could leak into their parse and fail with `EnvVarMissingError`. ([#1488](https://github.com/databricks/dbt-databricks/pull/1488))
1920

2021
## dbt-databricks 1.12.0 (May 18, 2026)
2122

tests/functional/adapter/python_model/test_python_model.py

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
import os
23

34
import pytest
@@ -29,6 +30,31 @@ def verify_temp_table_cleaned(project, suffix):
2930
assert len(tmp_tables) == 0
3031

3132

33+
class SchemaNameVarMixin:
34+
"""Make the schema-change tests resilient to dbt-core test-isolation leakage.
35+
36+
These classes don't inherit ``BasePythonModelTests`` and run plain ``dbt run``. On
37+
dbt-core 1.11.2 a sibling class's ``test_source`` schema.yml -- whose ``schema``
38+
renders ``var(env_var('DBT_TEST_SCHEMA_NAME_VARIABLE'))`` -- can bleed into these
39+
classes' parse when they run after one in the same xdist worker (``--dist=loadfile``),
40+
failing with ``EnvVarMissingError``. Rendering that source needs both the env var
41+
(read from the invocation context) and the ``test_run_schema`` var. Schema-yaml
42+
rendering resolves ``var()`` from CLI vars only, so the var must be passed via
43+
``--vars`` -- exactly as dbt-core's ``BasePythonModelTests`` does -- not via
44+
project-level ``vars``.
45+
"""
46+
47+
@pytest.fixture(scope="class", autouse=True)
48+
def schema_name_env_var(self):
49+
os.environ["DBT_TEST_SCHEMA_NAME_VARIABLE"] = "test_run_schema"
50+
yield
51+
os.environ.pop("DBT_TEST_SCHEMA_NAME_VARIABLE", None)
52+
53+
@staticmethod
54+
def schema_name_vars(project):
55+
return ["--vars", json.dumps({"test_run_schema": project.test_schema})]
56+
57+
3258
@pytest.mark.python
3359
@pytest.mark.skip_profile("databricks_cluster")
3460
class TestPythonModel(BasePythonModelTests):
@@ -84,7 +110,7 @@ def project_config_update(self):
84110

85111
@pytest.mark.python
86112
@pytest.mark.skip_profile("databricks_cluster")
87-
class TestChangingSchema:
113+
class TestChangingSchema(SchemaNameVarMixin):
88114
"""Test Python model schema changes using serverless compute."""
89115

90116
@pytest.fixture(scope="class")
@@ -96,13 +122,14 @@ def project_config_update(self):
96122
return {"models": {"+create_notebook": "true"}}
97123

98124
def test_changing_schema_with_log_validation(self, project, logs_dir):
99-
util.run_dbt(["run"])
125+
schema_vars = self.schema_name_vars(project)
126+
util.run_dbt(["run", *schema_vars])
100127
util.write_file(
101128
override_fixtures.simple_python_model_v2,
102129
project.project_root + "/models",
103130
"simple_python_model.py",
104131
)
105-
util.run_dbt(["run"])
132+
util.run_dbt(["run", *schema_vars])
106133
log_file = os.path.join(logs_dir, "dbt.log")
107134
with open(log_file) as f:
108135
log = f.read()
@@ -113,7 +140,7 @@ def test_changing_schema_with_log_validation(self, project, logs_dir):
113140

114141
@pytest.mark.python
115142
@pytest.mark.skip_profile("databricks_cluster")
116-
class TestChangingSchemaIncremental:
143+
class TestChangingSchemaIncremental(SchemaNameVarMixin):
117144
"""Test Python incremental schema changes using serverless compute."""
118145

119146
@pytest.fixture(scope="class")
@@ -129,9 +156,10 @@ def project_config_update(self):
129156
return {"models": {"+create_notebook": "true"}}
130157

131158
def test_changing_schema_via_incremental(self, project):
132-
util.run_dbt(["seed"])
133-
util.run_dbt(["run"])
134-
util.run_dbt(["run"])
159+
schema_vars = self.schema_name_vars(project)
160+
util.run_dbt(["seed", *schema_vars])
161+
util.run_dbt(["run", *schema_vars])
162+
util.run_dbt(["run", *schema_vars])
135163

136164
util.check_relations_equal(project.adapter, ["incremental_model", "expected_incremental"])
137165

@@ -412,7 +440,7 @@ def test_python_model_with_access_control_list(self, project):
412440

413441

414442
@pytest.mark.skip_profile("databricks_cluster")
415-
class TestChangingSchemaV2(MaterializationV2Mixin):
443+
class TestChangingSchemaV2(SchemaNameVarMixin, MaterializationV2Mixin):
416444
"""Test Python model schema changes with V2 materialization using serverless compute."""
417445

418446
@pytest.fixture(scope="class")
@@ -424,19 +452,20 @@ def project_config_update(self):
424452
return {"models": {"+create_notebook": "true"}}
425453

426454
def test_changing_unique_tmp_table_suffix(self, project):
427-
util.run_dbt(["run"])
455+
schema_vars = self.schema_name_vars(project)
456+
util.run_dbt(["run", *schema_vars])
428457
util.write_file(
429458
override_fixtures.simple_python_model_v2,
430459
project.project_root + "/models",
431460
"simple_python_model.py",
432461
)
433-
util.run_dbt(["run"])
462+
util.run_dbt(["run", *schema_vars])
434463
verify_temp_tables_cleaned(project)
435464

436465

437466
@pytest.mark.python
438467
@pytest.mark.skip_profile("databricks_cluster")
439-
class TestChangingSchemaIncrementalV2(MaterializationV2Mixin):
468+
class TestChangingSchemaIncrementalV2(SchemaNameVarMixin, MaterializationV2Mixin):
440469
"""Test Python incremental schema changes with V2 materialization using serverless compute."""
441470

442471
@pytest.fixture(scope="class")
@@ -448,13 +477,14 @@ def project_config_update(self):
448477
return {"models": {"+create_notebook": "true"}}
449478

450479
def test_changing_unique_tmp_table_suffix(self, project):
451-
util.run_dbt(["run"])
480+
schema_vars = self.schema_name_vars(project)
481+
util.run_dbt(["run", *schema_vars])
452482
util.write_file(
453483
override_fixtures.simple_incremental_python_model_v2,
454484
project.project_root + "/models",
455485
"incremental_model.py",
456486
)
457-
util.run_dbt(["run"])
487+
util.run_dbt(["run", *schema_vars])
458488
verify_temp_tables_cleaned(project)
459489

460490

0 commit comments

Comments
 (0)