dbt-labs / dbt-labs/dbt

[Bug] Unit tests render the tested model with config.meta stripped to {}

Open
#12,938 2 comments 0 reactions 0 assignees View on GitHub
engine:v1 status:triage
Dominant language
Rust
Stars
13.8k
Forks
2.6k
Avg merge
21h 31m
Merged PRs (30d)
56

Description

### Is this a new bug in dbt-core?

- [x] I believe this is a new bug in dbt-core
- [x] I have searched the existing issues, and I could not find an existing issue for this bug

### Current Behavior

When dbt renders a tested model **inside a unit test**, the model's own `config.meta` is empty — regardless of whether it was set via `{{ config(meta={...}) }}` in the `.sql` file or via `config.meta:` in the model's `.yml`. The same Jinja that reads `config.meta` returns the configured dict during `dbt compile` / `dbt run`, but returns `{}` during `dbt test`.

The same behavior is observed on dbt Core 1.11.9 and on dbt Fusion 2.0.0-preview.175 — this isn't a Fusion regression, it's a Core-side gap that Fusion inherits.

Minimal public reproducer (dbt-duckdb, no creds required): https://github.com/ms-86/dbt-unit-test-meta-stripped-repro

| Source of `meta` | `dbt compile` (model) | `dbt test` (unit test) |
|------------------|----------------------|------------------------|
| `{{ config(meta={...}) }}` in `.sql` | visible | stripped to `{}` |
| `config.meta:` in `.yml` | visible | stripped to `{}` |

Concrete output from the repro:

```sql
-- target/compiled/unit_test_meta_repro/models/meta_probe.sql (normal render)
SELECT
CAST('True' AS VARCHAR) AS sql_block_flag,
CAST('set-from-sql' AS VARCHAR) AS sql_block_label,
CAST('set-from-yml' AS VARCHAR) AS yml_block_flag,
CAST('{...}' AS VARCHAR) AS meta_dict_repr

-- target/compiled/unit_test_meta_repro/models/meta_probe.yml/models/meta_probe_sees_meta.sql
SELECT
CAST('NOT_SET' AS VARCHAR) AS sql_block_flag,
CAST('NOT_SET' AS VARCHAR) AS sql_block_label,
CAST('NOT_SET' AS VARCHAR) AS yml_block_flag,
CAST('{}' AS VARCHAR) AS meta_dict_repr
```

### Expected Behavior

When dbt renders a tested model inside a unit test, `config.meta` should return the same dictionary that `dbt compile` / `dbt run` would see — the union of `meta` set via `{{ config(meta=...) }}` in `.sql` and `config.meta:` in `.yml`. Anything less makes meta-driven SQL switches untestable.

### Steps To Reproduce

```bash
git clone https://github.com/ms-86/dbt-unit-test-meta-stripped-repro
cd dbt-unit-test-meta-stripped-repro
uv sync

# Normal compile — config.meta IS visible
uv run dbt compile --profiles-dir . --select meta_probe
cat target/compiled/unit_test_meta_repro/models/meta_probe.sql
# → CAST('True' AS VARCHAR), CAST('set-from-sql' AS VARCHAR),
# CAST('set-from-yml' AS VARCHAR),
# CAST('{'sql_flag': True, ...}' AS VARCHAR)

# Unit test render — config.meta is empty
uv run dbt test --profiles-dir . --select meta_probe
cat target/compiled/unit_test_meta_repro/models/meta_probe.yml/models/meta_probe_sees_meta.sql
# → CAST('NOT_SET' AS VARCHAR), CAST('NOT_SET' AS VARCHAR),
# CAST('NOT_SET' AS VARCHAR), CAST('{}' AS VARCHAR)
```

### Relevant log output

```shell
Failure in unit_test meta_probe_sees_meta (models/meta_probe.yml)

actual differs from expected:

,sql_block_flag,sql_block_label,yml_block_flag,meta_dict_repr
+++,NOT_SET ,NOT_SET ,NOT_SET ,{}
---,True ,set-from-sql ,set-from-yml ,"{'sql_flag': True, 'sql_label': 'set-from-sql', 'yml_flag': 'set-from-yml'}"
```

### Environment

```markdown
- OS: macOS 15.7.3 (Darwin 24.6.0, arm64)
- Python: 3.12.12 (uv-managed venv in the repro project)
- dbt-core: 1.11.9 (latest patch on the 1.11 line)
- dbt-duckdb: 1.10.0
- Cross-checked on dbt-fusion 2.0.0-preview.175 — same behavior
```

### Which database adapter are you using with dbt?

other — duckdb. The bug is not adapter-specific; it lives in core's unit-test parser before adapter dispatch.

### Additional Context

**Root cause.** `core/dbt/parser/unit_tests.py` constructs the `UnitTestNode` with a fresh `UnitTestNodeConfig` carrying only `materialized`, `expected_rows`, and `expected_sql` — none of the tested model's config (including `meta`) is propagated:

```python
# core/dbt/parser/unit_tests.py (v1.11.9)
unit_test_node = UnitTestNode(
...,
config=UnitTestNodeConfig(
materialized="unit",
expected_rows=expected_rows,
expected_sql=expected_sql,
),
raw_code=tested_node.raw_code, # tested model's Jinja
...,
)

ctx = generate_parser_unit_test_context(unit_test_node, ...)
get_rendered(unit_test_node.raw_code, ctx, unit_test_node, capture_macros=True)
```

`UnitTestNodeConfig` is defined at `core/dbt/contracts/graph/model_config.py:39-41` and inherits from `NodeConfig`. `get_rendered` then renders the tested model's `raw_code` against this empty `UnitTestNodeConfig`, so `config.get('meta')` returns the default `{}`.

A minimal fix would copy `meta` (and possibly other relevant fields) from `tested_node.config` onto the `UnitTestNodeConfig` at construction time. The harder question is whether **all** of `tested_node.config` should be propagated, or just the fields a model body might reasonably introspect (`meta`, `tags`, model-level user-set keys). Either way, the current behavior — silently dropping the entire config — is surprising and breaks meta-driven SQL switches.

**Impact.** Several dbt packages switch SQL on `config.meta` toggles. The most prominent example is [**automate_dv**](https://github.com/Datavault-UK/automate-dv): `automate_dv.databricks__eff_sat` reads `is_auto_end_dating` via `automate_dv.config_meta_get(...)` ([`macros/tables/databricks/eff_sat.sql:11`](https://github.com/Datavault-UK/automate-dv/blob/master/macros/tables/databricks/eff_sat.sql#L11)).

`config_meta_get` ([`macros/supporting/fusion_compat.sql`](https://github.com/Datavault-UK/automate-dv/blob/master/macros/supporting/fusion_compat.sql)) probes **both** `config.get('meta').get(key)` and `config.get(key)`. The file is named `fusion_compat.sql` because it works around dbt Fusion's strict validation of custom top-level config keys ([dbt-fusion#511](https://github.com/dbt-labs/dbt-core/issues/13546)) — the helper itself is plain Jinja that runs on both engines.

The unit-test bug reported here defeats both lookup paths on both engines, because `UnitTestNodeConfig` is a blank slate. The auto-end-dating branch of `automate_dv.eff_sat` is therefore unreachable in any unit test. The same is true of any other macro that switches SQL on `config.meta`.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.